Click here to Skip to main content
15,881,381 members
Articles / All Topics

Getting Started with the PowerBasic Compiler – Part 1

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
23 Mar 2016CPOL7 min read 17.5K   1   8
This series of articles is about getting started in using the PowerBasic Windows native code compiler.

This series of articles is about getting started in using the PowerBasic Windows native code compiler. It is a tutorial about what the compiler is for and how it is used to write desktop applications, as well as standard Windows DLLs usable by other languages like C.

A Little History

For programmers who have been around for a while, they may be familiar with the name Turbo Basic. Back in the days of DOS, Borland International was a big player in the programming language market. Eventually, they decided to sell off their programming tools line, most of it going to Embarcadero (see: Embarcadero web site). That is where tools like Turbo C and Turbo Pascal ended up. Turbo Basic though had a different path. Borland did not originally create Turbo Basic (which competed directly with Microsoft's famous Quick Basic and PDS 7.1 languages). Instead, they purchased an existing product called Basic/Z created by Bob Zale and renamed it TurboBasic. When Borland sold off their programming tools, they sold back the rights to TurboBasic to its original developer, Bob Zale and he renamed it PowerBasic.

What is Native Code Compiling?

Most programmers today are familiar with either .NET programming languages or scripting languages (i.e., HTML5, JavaScript, etc.). In the Windows world, .NET currently “rules the roost” for many while old fashioned WIN32 programming has gone by the wayside. Native coding for Windows programmers means coding directly to the WIN32 API. Why would anyone want to do that, one may ask? One word “Performance”! To better appreciate how native coding affects performance, watch Herb Sutter’s talk “Why C++?” on Microsoft's Channel 9. No matter what programming language you currently use, his talk about C++ is a must watch video. Simply put, native coding directly to the operating system produces the best performance possible.

.NET is a framework between the operating system and one's application, a middleman one could say. So would not talking directly to the operating system provide better performance? Actually it does. But who cares about performance anymore, since computers are so much faster today? Today's small Windows tablets for one require high performance applications since they usually have minimal hardware. Developers of performance critical apps, like graphic editors, look for every bit of performance they can get. Embedded programmers also may want high performance apps so they can use minimal hardware.

OK, so maybe you are interested in a native code compiler. So what are the key things one should know about the PowerBasic compiler before using it?

How Does the Compiler Work Under the Hood?

The first thing to understand about PowerBasic is that the core compiler is written in assembler. There are two reasons for this:

  1. Fast compile speeds
  2. Fast (and small) executable code

Even hundreds of thousands of lines of code can be compiled in seconds rather than minutes. Executable sizes rival anything that can be done using raw C (not C++, but pure C), producing executable files in kilobytes, rather than megabytes. Executables have no runtimes required, but only the core Windows operating system DLLs (i.e., user32.dll, kernal32.dll, etc.). No .NET runtimes. No C runtimes.

The compiler is capable of producing both standard Windows EXEs and DLLs. This means you can also use it to write DLLs for any language which allows access to standard Windows DLLs. PowerBasic EXEs themselves can access standard Windows DLLs too, so you can use DLLs written in C or other languages in your apps.

PowerBasic is also a language which lacks a GUI framework, but instead relies directly on the WIN32 API for user interfaces, but handles this in two ways. First, one can write directly using the WIN32 API calls (headers are provided in PowerBasic code format). Second, one can use PowerBasic’s shortcut method via its Dynamic Dialog Tools (Called DDT). This is not a GUI framework, but instead is a GUI command set which simplifies many user interface WIN32 API tasks via a lean set of command wrappers using the Windows Dialog engine as the basis. For example, the WIN32 CreateWindowEx function is replaced with a DDT counterpart called CONTROL ADD. It works similar to CreateWindowEx, but is easier to implement.

PowerBasic supports the most commonly used system controls in its DDT command set, while of course everything is available if you want to use the WIN32 API directly. DDT even provides a unique Canvas control (which is not available in system controls), by using a simple trick. The DDT Canvas control is actually a WIN32 STATIC class control which uses OwnerDraw. The DDT command set simply provides a memory buffer to draw into and the compilers embedded runtime code handles the drawing of the memory buffer to the STATIC control during the WM_DRAWITEM message processing. This is all done under the hood, so it appears like you have a real Canvas control.

Most BASIC programmers are used to working with the variable length string data type and PowerBasic not only supports this, but it does so in a unique way. Rather than use Global memory and zero terminated strings, PowerBasic uses the WIN32 OLE string APIs internally. OLE strings do not require they be zero (null) terminated, so even 100% binary data can be stored in a string variable (i.e., CHR$(0)). The latest version of PowerBasic also supports Unicode strings as well. One nice thing about the OLE string variables is that PowerBasic always appends them with a NULL character just in case you need to pass it to a C DLL. You can just pass the string by a pointer and it already has a NULL termination in it (not needed for PowerBasic itself, but is there for multi-language support). The PowerBasic string command set is extremely rich and is one benefit for programmers who write a lot of text string parsers.

Lastly, PowerBasic allows inline assembler. So when performance is critical, one can switch to assembler at any time even mixing BASIC with Assembler.

Let’s Start With a Hello World App

#COMPILE EXE
#DIM ALL

FUNCTION PBMAIN () AS LONG

    MSGBOX "Hello World"

END FUNCTION

The above EXE is only 7 kilobytes in size, most of that being embedded runtime code (which is still small).

Now Let's Create a Real Window (Dialog)

In this example, I will create a window (Dialog) with some controls on it and some menus:

#COMPILE EXE
#REGISTER NONE
#DIM ALL        '  This is helpful to prevent errors in coding
#INCLUDE "win32api.inc"     ' Win 32 API include file !
' ----------------------------------------------------------

' ----------------------------------------------------------
%Form1_FILE                                     = 500
' ----------------------------------------------------------
%Form1_NEWFILE                                  = 505
%Form1_OPENFILE                                 = 510
%Form1_SAVEFILE                                 = 515
%Form1_SAVEAS                                   = 520
%Form1_SEPARATOR_525                            = 525
%Form1_EXIT                                     = 530
' ----------------------------------------------------------
%Form1_EDIT                                     = 600
' ----------------------------------------------------------
%Form1_CUT                                      = 605
%Form1_COPY                                     = 610
%Form1_PASTE                                    = 615
' ----------------------------------------------------------
%Form1_HELP                                     = 700
' ----------------------------------------------------------
%Form1_HELP1                                    = 705
%FORM1_LABEL1             = 100
%FORM1_BUTTON1            = 105
%FORM1_CHECK1             = 110
%FORM1_LISTBOX1           = 115
%FORM1_COMBOBOX1          = 120
' --------------------------------------------------
DECLARE SUB CreateDialog_Form1(BYVAL hParent&)
DECLARE CALLBACK FUNCTION Form1_DlgProc
' --------------------------------------------------
DECLARE SUB Form1_NEWFILE_Select()
DECLARE SUB Form1_OPENFILE_Select()
DECLARE SUB Form1_SAVEFILE_Select()
DECLARE SUB Form1_SAVEAS_Select()
DECLARE SUB Form1_EXIT_Select()
DECLARE SUB Form1_CUT_Select()
DECLARE SUB Form1_COPY_Select()
DECLARE SUB Form1_PASTE_Select()
DECLARE SUB Form1_HELP1_Select()
DECLARE CALLBACK FUNCTION CBF_FORM1_BUTTON1()
DECLARE CALLBACK FUNCTION CBF_FORM1_CHECK1()
DECLARE CALLBACK FUNCTION CBF_FORM1_LISTBOX1()
DECLARE CALLBACK FUNCTION CBF_FORM1_COMBOBOX1()
' --------------------------------------------------
GLOBAL App_CurrentDlg AS DWORD
GLOBAL hForm1&    ' Dialog handle
GLOBAL hForm1_Menu0&  ' Global Handles for menus
GLOBAL hForm1_Menu1&
GLOBAL hForm1_Menu2&
GLOBAL hForm1_Menu3&
' *************************************************************
'                   Application Entrance
' *************************************************************
FUNCTION PBMAIN ()AS LONG
    LOCAL Count&
    CreateDialog_Form1 0
    ' Dialogs Handle in variable - hForm1&
    DIALOG SHOW MODELESS hForm1& , CALL Form1_DlgProc
    DO
        DIALOG DOEVENTS TO Count&
    LOOP UNTIL Count&=0
END FUNCTION
' *************************************************************
'                    Application Dialogs
' *************************************************************
SUB CreateDialog_Form1(BYVAL hParent&)
    LOCAL Style&, ExStyle&
    LOCAL N&, CT&        '  Variables used for Reading Data in Arrays for Listbox and Combobox
    Style& = %WS_POPUP OR %DS_MODALFRAME OR %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU OR %DS_CENTER
    ExStyle& = 0
    DIALOG NEW hParent&, "Your Dialog", , , 280,  200, Style&, ExStyle&, TO hForm1&
    ' ---------------------------
    MENU NEW BAR TO hForm1_Menu0&
    ' ---------------------------
    ' ---------------------------
    MENU NEW POPUP TO hForm1_Menu1&
    MENU ADD POPUP, hForm1_Menu0& ,"&File", hForm1_Menu1&, %MF_ENABLED
    ' ---------------------------
    MENU ADD STRING, hForm1_Menu1&, "&New File",  %Form1_NEWFILE, %MF_ENABLED
    MENU ADD STRING, hForm1_Menu1&, "&Open File",  %Form1_OPENFILE, %MF_ENABLED
    MENU ADD STRING, hForm1_Menu1&, "&Save File",  %Form1_SAVEFILE, %MF_ENABLED
    MENU ADD STRING, hForm1_Menu1&, "Save File &As",  %Form1_SAVEAS, %MF_ENABLED
    MENU ADD STRING, hForm1_Menu1&, "-",  %Form1_SEPARATOR_525, %MF_ENABLED
    MENU ADD STRING, hForm1_Menu1&, "E&xit",  %Form1_EXIT, %MF_ENABLED
    ' ---------------------------
    MENU NEW POPUP TO hForm1_Menu2&
    MENU ADD POPUP, hForm1_Menu0& ,"&Edit", hForm1_Menu2&, %MF_ENABLED
    ' ---------------------------
    MENU ADD STRING, hForm1_Menu2&, "Cu&t",  %Form1_CUT, %MF_ENABLED
    MENU ADD STRING, hForm1_Menu2&, "&Copy",  %Form1_COPY, %MF_ENABLED
    MENU ADD STRING, hForm1_Menu2&, "&Paste",  %Form1_PASTE, %MF_ENABLED
    ' ---------------------------
    MENU NEW POPUP TO hForm1_Menu3&
    MENU ADD POPUP, hForm1_Menu0& ,"&Help", hForm1_Menu3&, %MF_ENABLED
    ' ---------------------------
    MENU ADD STRING, hForm1_Menu3&, "&Contents",  %Form1_HELP1, %MF_ENABLED
    MENU ATTACH hForm1_Menu0&, hForm1&
    CONTROL ADD LABEL, hForm1&,  %FORM1_LABEL1,  "Label  1", 28, 136, 216, 32, _
        %WS_CHILD OR %WS_VISIBLE OR %SS_CENTER OR %WS_BORDER, 0
    CONTROL ADD "Button", hForm1&,  %FORM1_BUTTON1,  "Button  1", 12, 8, 80, 24, _
        %WS_CHILD OR %WS_VISIBLE OR %BS_PUSHBUTTON OR %WS_TABSTOP, 0 CALL CBF_FORM1_BUTTON1
    CONTROL ADD CHECKBOX, hForm1&,  %FORM1_CHECK1,  "Check  1", 12, 40, 92, 24, _
        %WS_CHILD OR %WS_VISIBLE OR %BS_AUTOCHECKBOX OR %WS_TABSTOP, 0 CALL CBF_FORM1_CHECK1
    ' - - - - - - - - - - - - - - - - - - - - - - - - -
    DIM LISTBOX1_List(4) AS LOCAL STRING
    DATA "Item 1","Item 2","Item 3","Item 4","Item 5"
    FOR N&=0 TO 4
        CT&=CT&+1
        LISTBOX1_List(N&)=READ$(CT&)
    NEXT N&
    ' - - - - - - - - - - - - - - - - - - - - - - - - -
    CONTROL ADD LISTBOX, hForm1&,  %FORM1_LISTBOX1, LISTBOX1_List(), 12, 72, 108, 48, _
        %WS_CHILD OR %WS_VISIBLE OR %LBS_NOTIFY OR %LBS_SORT OR %LBS_NOINTEGRALHEIGHT OR %WS_VSCROLL OR %WS_TABSTOP, _
        %WS_EX_CLIENTEDGE CALL CBF_FORM1_LISTBOX1
    ' - - - - - - - - - - - - - - - - - - - - - - - - -
    DIM COMBOBOX1_List(4) AS LOCAL STRING
    DATA "Item 1","Item 2","Item 3","Item 4","Item 5"
    FOR N&=0 TO 4
        CT&=CT&+1
        COMBOBOX1_List(N&)=READ$(CT&)
    NEXT N&
    ' - - - - - - - - - - - - - - - - - - - - - - - - -
    CONTROL ADD COMBOBOX, hForm1&,  %FORM1_COMBOBOX1, COMBOBOX1_List(), 136, 16, 112, 52, _
        %WS_CHILD OR %WS_VISIBLE OR %CBS_DROPDOWNLIST OR %CBS_SORT OR %WS_VSCROLL OR %CBS_NOINTEGRALHEIGHT OR %WS_TABSTOP, _
        %WS_EX_CLIENTEDGE CALL CBF_FORM1_COMBOBOX1
END SUB
' *************************************************************
'                             Dialog Window Procedure
'                             for Form Form1
'                             uses Global Handle - hForm1&
' *************************************************************
CALLBACK FUNCTION Form1_DlgProc
    SELECT CASE CBMSG
        CASE %WM_COMMAND
            ' Process Messages to Controls that have no Callback Function
            ' and Process Messages to Menu Items
            SELECT CASE LOWRD(CBWPARAM)    ' Parse by Ctrl ID #
                CASE  %Form1_NEWFILE                                      ' Popup Menu Item Selected
                    Form1_NEWFILE_Select
                CASE  %Form1_OPENFILE                                     ' Popup Menu Item Selected
                    Form1_OPENFILE_Select
                CASE  %Form1_SAVEFILE                                     ' Popup Menu Item Selected
                    Form1_SAVEFILE_Select
                CASE  %Form1_SAVEAS                                       ' Popup Menu Item Selected
                    Form1_SAVEAS_Select
                CASE  %Form1_EXIT                                         ' Popup Menu Item Selected
                    Form1_EXIT_Select
                CASE  %Form1_CUT                                          ' Popup Menu Item Selected
                    Form1_CUT_Select
                CASE  %Form1_COPY                                         ' Popup Menu Item Selected
                    Form1_COPY_Select
                CASE  %Form1_PASTE                                        ' Popup Menu Item Selected
                    Form1_PASTE_Select
                CASE  %Form1_HELP1                                        ' Popup Menu Item Selected
                    Form1_HELP1_Select
                CASE ELSE
            END SELECT
        CASE %WM_SHOWWINDOW
            IF CBWPARAM THEN
                App_CurrentDlg=CBHNDL
            ELSE
                App_CurrentDlg=0
            END IF
        CASE %WM_ACTIVATE
            IF LOWRD(CBWPARAM)<>0 THEN
                App_CurrentDlg=CBHNDL
            ELSE
                App_CurrentDlg=0
            END IF
        CASE %WM_DESTROY
        CASE ELSE
    END SELECT
    FUNCTION = 0
END FUNCTION
' *************************************************************
'  Application Callback Functions (or Procedures) for Controls
' *************************************************************
' ------------------------------------------------
SUB Form1_NEWFILE_Select()

END SUB
' ------------------------------------------------
SUB Form1_OPENFILE_Select()

END SUB
' ------------------------------------------------
SUB Form1_SAVEFILE_Select()

END SUB
' ------------------------------------------------
SUB Form1_SAVEAS_Select()

END SUB
' ------------------------------------------------
SUB Form1_EXIT_Select()

END SUB
' ------------------------------------------------
SUB Form1_CUT_Select()

END SUB
' ------------------------------------------------
SUB Form1_COPY_Select()

END SUB
' ------------------------------------------------
SUB Form1_PASTE_Select()

END SUB
' ------------------------------------------------
SUB Form1_HELP1_Select()

END SUB
' ------------------------------------------------
CALLBACK FUNCTION CBF_FORM1_BUTTON1
    IF CBCTLMSG=%BN_CLICKED THEN

    END IF
    IF CBCTLMSG=%BN_SETFOCUS THEN

    END IF
    IF CBCTLMSG=%BN_KILLFOCUS THEN

    END IF
END FUNCTION
' ------------------------------------------------
CALLBACK FUNCTION CBF_FORM1_CHECK1
    IF CBCTLMSG=%BN_CLICKED THEN

    END IF
    IF CBCTLMSG=%BN_SETFOCUS THEN

    END IF
    IF CBCTLMSG=%BN_KILLFOCUS THEN

    END IF
END FUNCTION
' ------------------------------------------------
CALLBACK FUNCTION CBF_FORM1_LISTBOX1
    LOCAL CVal&
    ' Return Current Selection in CVal&
    CONTROL SEND CBHNDL , CBCTL, %LB_GETCURSEL, 0,0 TO CVal&
    IF CBCTLMSG=%LBN_SELCHANGE THEN

    END IF
    IF CBCTLMSG=%LBN_DBLCLK THEN

    END IF
    IF CBCTLMSG=%LBN_SETFOCUS THEN

    END IF
    IF CBCTLMSG=%LBN_KILLFOCUS THEN

    END IF
END FUNCTION
' ------------------------------------------------
CALLBACK FUNCTION CBF_FORM1_COMBOBOX1
    LOCAL CVal&
    ' Return Current Selection in CVal&
    CONTROL SEND CBHNDL , CBCTL, %CB_GETCURSEL, 0,0 TO CVal&
    IF (CBCTLMSG=%CBN_SELCHANGE) OR  (CBCTLMSG=%CBN_EDITCHANGE) OR (CBCTLMSG=%CBN_EDITUPDATE) THEN

    END IF
    IF CBCTLMSG=%CBN_DBLCLK THEN

    END IF
    IF CBCTLMSG=%CBN_SETFOCUS THEN

    END IF
    IF CBCTLMSG=%CBN_KILLFOCUS THEN

    END IF
END FUNCTION

The above example EXE compiles to about 52 Kilobytes. The DDT commands used, simplify some of the API tasks for a Dialog, but you can still see the API style similarity in it though. It uses a Dialog Procedure, just like you do with a pure WIN32 app. Some of the WM_COMMAND notification messages though are parsed out by the DDT embedded runtime code and the calls are automatically forwarded to the CallBack subroutines , so you don’t have to process WM_COMMAND for all control types. The notification codes for WM_COMMAND are pushed into some system variables (i.e., CBCTLMSG) already, so you can test for the notification messages just by using these system variables. Something similar is done with the Dialog procedure with the normal parameters of a dialog procedure pushed to system variables (i.e., CBMSG). PowerBasic’s DDT also can do something similar with some WM_NOTIFY notification messages.

DDT just simplifies things a bit, rather than totally shield you from the WIN32 API. You can also integrate the WIN32 API into an application written using the DDT command set.

So When Would I Find PowerBasic Useful?

Programmers still using classic Visual Basic will find PowerBasic very useful. First, it is BASIC also, so an easier transition than say C. Second, it can be used to write DLLs for your Visual Basic programs to improve performance. It is better suited to writing WIN32 code, so at times when you require accessing the WIN32 API from Visual Basic, you may find PowerBasic helpful. PowerBasic is better geared toward accessing the WIN32 API than is classic Visual Basic. It supports things like code pointers and data pointers. It can do Threading. It directly supports TCP and UDP communications. It supports serial communications. It can do inline assembler.

Programmers who find C a challenge, but still would like to be able to write tiny, fast, WIN32 applications and DLLs will find PowerBasic helpful. PowerBasic is actually easier to work with than C, when it comes to writing WIN32 stuff. It has so many tricks in it which rival C’s raw power, but are easier to implement. Again, it is BASIC.

In future articles, I plan to write more about working with the WIN32 API using PowerBasic.

This article was originally posted at http://cwsof.com/blog?p=882

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Computer Workshop
United States United States
Chris Boss is the owner (and programmer) of a small software development business in rural Virginia, called the Computer Workshop. For the last ten years or so he has been developing tools for use by Powerbasic programmers (see: http://powerbasic.com ). His main product called EZGUI (Easy GUI) is a high level GUI engine with Visual Designer and code generator. It is in its fifth generation now. He is an experienced Windows API programmer (more low level) and has experience in writing GUI engines (forms/controls), drag and drop Visual Designers, Graphics engines (printing and to the screen) and one of his favorites is a Sprite engine (2D animated movable images). His current project is version 5.0 of his main product EZGUI, adding such features as multi-monitor support, component engine, custom control engine, superclass engine and the latest project a 3D OpenGL based custom control. One of the goals he has is to push the limits of Windows software development, while making it easy, fast execution speed, small footprint (size of executables) and code reusability while providing a more graphic experience in user interfaces, while still being able to write software which can fit on a floppy disk (small footprint), use minimal amount of memory and able to run on multiple versions of Windows from 95 to Win8.

Comments and Discussions

 
Praisethanks for this great summary Pin
Southmountain9-Mar-23 8:55
Southmountain9-Mar-23 8:55 
QuestionGreat Resource Pin
Fred Rost24-May-22 5:23
Fred Rost24-May-22 5:23 
QuestionPowerbasic Book Pin
Tony Serra23-Apr-16 9:40
Tony Serra23-Apr-16 9:40 
AnswerRe: Powerbasic Book Pin
Chris Boss8-May-16 7:12
professionalChris Boss8-May-16 7:12 
QuestionPowerbasic Pin
Tony Serra20-Apr-16 9:08
Tony Serra20-Apr-16 9:08 
AnswerRe: Powerbasic Pin
Chris Boss22-Apr-16 15:02
professionalChris Boss22-Apr-16 15:02 
GeneralMy vote of 5 Pin
Tokinabo25-Mar-16 3:10
professionalTokinabo25-Mar-16 3:10 
QuestionUpdate to code above Pin
Chris Boss24-Mar-16 7:50
professionalChris Boss24-Mar-16 7:50 
The code above uses an old style syntax in Button control callbacks which will have problems if the application uses Themes (a manifest resource added). It ownly works with an unthemed app. In future articles I will update the code to take advantage of the differences when Themes are used. While my articles will highlight the use of the PowerBasic DDT syntax at times, I mostly code using either the Windows API directly or my own GUI framework. While DDT is helpful, it also has some quirks one must be aware of, which I will try to highlight in future articles.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.