PowerBasic
Introduction
PowerBasic is the ultimate Basic compiler for Windows! It was originally 
Bob Zale's project TurboBasic 1.0 over at Borland in 1985. After Borland discontinued 
the project following release 1.1 
because it feared it could not compete with Microsoft's QuickBasic, Borland transferred the rights to the code 
to Bob, who left and built a company to market it. PowerBasic 2.0 was published 
in 1989 by Spectra Publishing. In addition to the regular release, PB 2.1 was 
released as shareware in 1996 as FirstBasic. Bob started his own company, PowerBasic 
Inc, to publish PowerBasic 3.0 in 1993. 
In addition to command-line-based programs (PB/CC, PB/DOS, etc.), PowerBasic 
Inc offers PowerBasic for Windows (ex-PB/DLL) which is a compiler to build either DLLs or EXEs for Windows. 
Scroll down to see what the standard "Hello, world!" program looks 
like in PB/DLL. Being a small shop, PowerBasic doesn't have the means to promote 
PB/DLL much, so it has to be one of the most under-marketed gems out there.
Must haves
    - A good editor from which you can build and run your PB application: 
    I recommend either UltraEdit (see below on how to compile your .BAS file 
    directly from UE), the JellyFish 
    Editor (Didn't find features 
    about JF that UltraEdit doesn't have, though) by Paul Squires which integrates with the Lynx Project Explorer 
    to display a hierarchical map of the files in a project (although it's neat, 
    just like the CodeSmart add-in for the VB IDE, I don't like Lynx because 
    it isn't displayed inside 
    UltraEdit, but rather, alongside, and also because it requires building 
    a project file instead of just showing up in UltraEdit as you open a PB 
    file), or PBCEd, 
    the PowerBasic Code Editor by Heber Jorge da Silva, both built on the CodeMax 
    OCX.
 
 Yet other alternatives are PowerIDE from KGP 
    Software (I don't like it because of its lack of the instant 
    one-block indenting, but it does have a killer feature, though: 
    A tiny dialog lists all the subs/functions in the open source file, and 
    you can jump to any them by just selecting it in that dialog. Very nice, 
    and a basic alternative to Lynx), RAD 
    Developer, or Jose Roca's SciEditor 
    (a.k.a. SED) based on a rewrite in PB of the open-source Scintilla edit 
    widget
- A GUI builder to avoid having to draw the UI by hand: I recommend that 
    you build the GUI with the DDT extensions offered by PB/DLL 6 with the help 
    of a dialog editor (eg. Microsoft Developer Studio and James Fuller's RC2DTT.EXE), use the EZGUI Dialog Designer (freeware), or consider 
    buying EZGUI 2.0 . See section below. Seems 
    like PowerBasic 
    gave up on PBGen, and replaced it with with PBForms 
    to generate DDT instructions
- A database engine such as Advantage 
    Systems' Tsunami (really a hashing 
    system; ttds, a client-server 
    version of the Tsunami Record Manager is available here), Borland's InterBase/BDE, 
    Planet Squires' Cheetah or CodeBase 
    for PowerBasic. 
    More options here.
- A grid objet: There's quite a lot of such objects available commercially 
    (eg. VSFlexGrid, SIGrid, 
    Farpoints Spread, etc.), but you can also use Erik Christensen's free source, 
    Roberto Valoi's Editable Listview, 
    VBAccelerator's SGrid, 
    Edwin's Visual Designer which 
    has a small grid included, Elias' e-grid32, 
    or wxGrid). 
    Ideally, it can print nice tables so you don't waste time reformating data 
    for output. Also check how to interact with a SQL server (ideally, you can 
    just perform SELECTs and INPUTs)
- The Librarian by Bare Point Systems: 
    Source code library designed with EZ_GUI and Vista 
Software's Artemis 5 database engine.
- A preview/print object: Don Dickinson's ddoc 
    Print and Preview, DLLPrint
- A DLL bundler to ship a single EXE : EXE 
    Bundle, PEBundle (list 
    here)
- A recorder to build a demo (PB 
    Recorder)
- Unfortunately, neither PowerBasic nor a third-party has written a book 
    on PB/DLL, so you'll have to rely on books written for Visual Basic or VC++ 
    users to learn how to work with PB/DLL, and make the necessary adjustements. 
    A pity for such a great tool. Recommended books include...
- And remember to check out the excellent source code vault over at PowerBasic 
    here, 
    and over at (RIP) BASICally 
    speaking. If you read German, there's a PowerBasic 
    Quick-Referenz (commercial)
PowerBasic Pros and Cons
Pros
    - Can compile small, fast, stand-alone EXE
- Small development environment
- Doesn't hide the logic under tons of auto-generated code ā la MFC or 
    .Net
Cons
    - Not an OO language, and no support for .Net as of May 2003, so knowledge of Win32 API required. 
    Get your copy of Petzold's 
    (or the older 
    edition if you want to play with the widgets introduced since Win95), 
    and download its examples 
    rewritten in PB
- WYSIWYG GUI designers still evolving (multiple tools). No native tool 
    as good as Visual Basic for GUI design
- No book available besides the PBDLL.PDF (or its printed equivalent), 
    forcing users to buy books on Visual Basic or C, and make the necessary conversion 
    to learn PowerBasic
- 16-bit compiler, although it PB/DLL 5 and 6 build 32-bit programs. Don't 
    know if that could be an issue
Hello, world!
The easiest way
    - #COMPILE EXE
    -  
    - Function PbMain() AS LONG
    -     MsgBox "Hello, World!"
    - End Function
The easier way
Here, we'll use the DDT extensions introduced with PB/DLL 6 to create a dialog 
box:
    - #Compile Exe
    - #REGISTER NONE
    - #DIM ALL
    - #include "win32api.inc"
    -  
    - %IDLABEL = 1000
    -  
    - CallBack Function IDD_DIALOG1_CB As Long
    -   
    -   Select Case CbMsg
    -  
    -                 Case 
    %WM_COMMAND
    -                       Select 
    Case CbCtl
    -                                 Case 
    %IDOK
    -                                         DIALOG 
    END CBHNDL
    -                       End Select
    -       
    -   End Select
    -   
    - End Function
    -  
    - Function PbMain() As Long
    -   Local hDlg As Long
    -   Local lRetVal As Long
    -   
    -   Dialog New %NULL, "Your base are belong to us", 
    0, 0, 186, 95, _
    -             %DS_MODALFRAME 
    Or %DS_CENTER Or %WS_POPUP Or %WS_CAPTION Or %WS_SYSMENU, _
    -             %WS_EX_TOOLWINDOW, 
    TO hDlg
    -  
    -   Control Add Label, hDlg, %IDLABEL, "Hello, world!", 
    71,39,70,8
    -   Control Add Button, hDlg, %IDOK, "OK", 65,74,50,14
    -  
    -   Dialog Show Modal hDlg, Call IDD_DIALOG1_CB TO lRetVal-   
    - End Function
The hard way
Here, we'll build a full-fledged window ā la Petzold (a trip down memory 
lane):
    - $COMPILE EXE
    - $INCLUDE "WIN32API.INC"
    -  
    - FUNCTION WinMain (BYVAL hInstance AS LONG,  BYVAL hPrevInstance 
    AS LONG,  lpCmdLine AS ASCIIZ PTR,  BYVAL iCmdShow AS LONG) AS 
    LONG
    -  
    -         LOCAL Msg         AS 
    tagMsg
    -         LOCAL wndclass    AS 
    WndClassEx
    -         LOCAL hWnd        AS 
    LONG
    -         LOCAL szAppName   AS 
    ASCIIZ * 80
    -         
    -         szAppName              = 
    "HelloWin"
    -         
    -         wndclass.lpfnWndProc 
      = CODEPTR( WndProc )
    -         wndclass.cbSize        = 
    SIZEOF(WndClass)
    -         wndclass.style         = 
    %CS_HREDRAW OR %CS_VREDRAW
    -         wndclass.cbClsExtra 
       = 0
    -         wndclass.cbWndExtra 
       = 0
    -         wndclass.hInstance     = 
    hInstance
    -         wndclass.hIcon         = 
    LoadIcon( hInstance, "HELLOWIN" )
    -         wndclass.hCursor       = 
    LoadCursor( %NULL, BYVAL %IDC_ARROW )
    -         wndclass.hbrBackground 
    = GetStockObject( %WHITE_BRUSH )
    -         wndclass.lpszMenuName 
     = %NULL
    -         wndclass.lpszClassName 
    = VARPTR( szAppName )
    -         wndclass.hIconSm       = 
    LoadIcon( hInstance, BYVAL %IDI_APPLICATION )
    -         
    -         RegisterClassEx wndclass
    -  
    -         hWnd = CreateWindow(szAppName, 
    _               ' 
    window class name
    -                           "The 
    Hello Program", _     ' window caption
    -                           %WS_OVERLAPPEDWINDOW, 
    _    ' window style
    -                           %CW_USEDEFAULT, 
    _          ' initial x position
    -                           %CW_USEDEFAULT, 
    _          ' initial y position
    -                           %CW_USEDEFAULT, 
    _          ' initial x size
    -                           %CW_USEDEFAULT, 
    _          ' initial y size
    -                           %NULL, 
    _                   ' 
    parent window handle
    -                           %NULL, 
    _                   ' 
    window menu handle
    -                           hInstance, 
    _               ' 
    program instance handle
    -                           BYVAL 
    %NULL)               ' 
    creation parameters
    -  
    -         ShowWindow hWnd, iCmdShow
    -         UpdateWindow hWnd
    -  
    -         WHILE GetMessage(Msg, 
    %NULL, 0, 0)
    -                 TranslateMessage 
    Msg
    -                 DispatchMessage 
    Msg
    -         WEND
    -  
    -         FUNCTION = msg.wParam
    -  
    - END FUNCTION  ' WinMain
    -  
    - FUNCTION WndProc (BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, BYVAL wParam 
    AS LONG, BYVAL lParam AS LONG) EXPORT AS LONG
    -  
    -         LOCAL hDC       AS 
    LONG
    -         LOCAL LpPaint   AS 
    PaintStruct
    -         LOCAL tRect     AS 
    Rect
    -  
    -         SELECT CASE wMsg
    -         
    -                 CASE 
    %WM_CREATE
    -                 
    -                 CASE 
    %WM_PAINT
    -                         hDC 
    = BeginPaint(hWnd, LpPaint)
    -                         GetClientRect 
    hWnd, tRect
    -                         DrawText 
    hDC, "Hello, Windows 95!", -1, tRect, %DT_SINGLELINE OR %DT_CENTER 
    OR %DT_VCENTER
    -                         EndPaint 
    hWnd, LpPaint
    -                         FUNCTION 
    = 0
    -                         EXIT 
    FUNCTION
    -                 
    -                 CASE 
    %WM_DESTROY
    -                         PostQuitMessage 
    0
    -                         FUNCTION 
    = 0
    -                         EXIT 
    FUNCTION
    -         
    -         END SELECT
    -  
    - END FUNCTION
Why use dialogs instead of windows?
A dialog doesn't require registering a window class, and it can be easily 
designed using WYSIWYG tools such as those presented below.
From Chris Boss: Dialogs benefit from the extra processing of messages 
by the DefDialogProc function, (rather than DefWindowProc). A number of common 
messages are processed differently by this function. This extra processing basically 
benefits a Dialog with standard controls in areas such as the keyboard (ie. 
Tabbing). 
The disadvantage to Dialogs is that you don't have access to the Dialogs 
Window procedure, but only a Dialog procedure (they are different). For example, 
a Dialog procedure will not get a WM_CREATE message, but will get the WM_INITDIALOG 
message. A Window (non dialog) will get the WM_CREATE message, but not the WM_INITDIALOG. 
Such things as MDI are not well suited to use with Dialogs. [...] On the other 
hand Dialogs are easier and will handle more than 90% of your needs.
From Chris Boss, again : DDT is dependant upon the Windows Dialog 
engine which doesn't give you access to the Dialog Class Window procedure, but 
just a Dialog procedure called by the Class Window procedure. While you could 
subclass a DDT Dialog, it is just easier to use SDK created Dialogs if you want 
more control.
DDT (and any Windows Dialog engine created windows) use the DefDialogProc 
API function internally, where as SDK windows normally use the DefWindowProc 
API function. There is a significant difference between the two functions ! 
Since, DDT uses the Windows Dialog engine, one advantage it has is the a number 
of common tasks are already handled by the Windows dialog engine (ie. WM_NEXTDLGCTL 
message). When using SDK style windows you need to write your own code to process 
some messages, that the Windows Dialog engine handles already.
If your Dialogs are more oriented to standard type Dialogs using  standard 
controls, then DDT may be better suited. If your Dialogs need to do some custom 
stuff (ie. MDI, non-rectangular dialog using regions) then SDK windows are better 
suited.
Most Dialogs can be created with DDT and development is faster and easier. 
There are instances where SDK style Dialogs are a must though and a programmer 
should learn how to do both.
Also the Windows Dialog engine (which includes DDT) uses a portion of 
the available 40 bytes (30 bytes are used) for the extra window bytes , which 
means you have less of these bytes available for custom storage for the windows 
extra bytes. Windows 95/98 limits you to 40 bytes (I tested this and it is so 
and it applies to 32 bit windows and not just 16 bit). If you need more bytes 
for the extra window bytes, then you will have to do extra work with DDT, since 
you will have to create your own Global UDTs to store data. There are situations 
where the extra data space available for SDK windows is very useful.
Lastly, the Windows Dialog engine adds an extra amount of overhead when 
processing messages, since it has both a Window Procedure (the Dialog Class 
handles this) and a Dialog Procedure. Unless you subclass the Dialog, DDT is 
doing extra processing (albeit very little, but something) than an SDK Window 
class. If your Dialog requires the optimum speed (we are talking about extremely 
fast), then SDK style windows will save a few CPU cycles because it has less 
overhead. 
To be practical though, the extra overhead when using the Windows Dialog 
engine (or DDT) is negligable. It is there, but it is so small that in most 
instances it is insignificant !
The main point here is :
    - There is a difference between DDT and SDK style Dialogs. DDT should 
    handle more than 90% (or more) of the situations we may face. Yet, there 
    are some instances that SDK style coding is better.
- The beauty of PB, is that you have the option to use either method 
    if you choose. Use DDT first and in the rare instance that DDT doesn't give 
    you the control and power you need, then use SDK style code.
Chris Boss :-) : Here are a few more minor differences between 
DDT and SDK : DDT uses Dialog Units, while SDK uses Pixels. Dialog Units has 
the advantage of autoscaling your Dialog based on the end users Font setting 
(Small Fonts/Large Fonts), while SDK does not (You have to write your own autoscaling 
code).
On the other hand, Dialog Units (DDT) prevent you from having exact positioning 
of controls to an exact pixel. For example, try a column of ten buttons evenly 
spaced. The odds are you will have a tough time getting them to have the exact 
same number of pixels between them.
The reason for this, is that DDT defines the default font to MS Sans Serif 
8 while the Windows Dialog engine uses the System font instead (if no font is 
defined). The system font divides more evenly into exact pixels (a Dialog Unit 
is 2 x 2 pixels when using small fonts - character size is 8 x 16 pixels), while 
DDT's MS Sans Serif font doesn't divide well (character size is 6 x 13 pixels).
In most cases this doesn't cause a problem, but when the dialog requires 
some very exact and simetrical positioning of controls, DDT will be a hair off. 
This is nothing new though, since C programmers who create Dialogs as resources 
and who use CreateDialog (API) have suffered with this problem long before PB 
added DDT. 
This doesn't mean DDT is bad or at fault, it is just a reality of dealing 
with the Windows Dialog engine and using Dialog Units. As I said, DDT works 
for 90% (or more) of the needs for a Windows programmer.
You might find it interesting, that when I had to make a choice of whether 
to use the Windows Dialog engine or CreateWindowEX in my third party tools to 
create the Dialogs, I chose the Windows Dialog engine ! The advantages of DefDialogProc 
(which is called by the Windows Dialog engine) far outwayed the benefits of 
SDK created windows (CreateWindow). This advantage also exists in DDT !
My recommendation is that a programmer learn both ways of writing Windows 
apps. For beginners, DDT is much easier so they should start there. At some 
point it is good to learn how to write apps using the SDK style and to experiment 
and see the differences. If at some point a programmer finds that SDK style 
code doesn't offer them any significant advantages, then just stick with DDT. 
If you find that SDK style code offers some advantages crucial to your needs, 
then by all means use it !
From Eric Pearson : To clarify... > DDT uses Dialog Units, while 
SDK uses Pixels.
That's not true if you use SDK techniques to create dialogs. In fact that's 
the way most of my own programs are written. I create a dialog in a resource 
file and link it to my program. The program then uses SDK-style techniques to 
display and control the dialogs. And as a result, almost everything is done 
in dialog units, not pixels.
I do find myself writing more and more new programs using DDT code, however. 
For most of my applications (95%?) it is more than sufficient.
My personal summary of "DDT vs. SDK"...
SDK means "using the native API", and that implies that everything 
is possible if you are willing to write enough code, and to wrestle with the 
complexities of the API.
DDT is much easier to use. It requires much less coding and knowledge 
of the Windows APIs, and it can do nearly everything.
FireFly SDK Design (ex-VisualPB, ex-PowerGUI)
    - Commercial app from PlanetSquires 
    about $100
- From the same company that makes the JellyFish editor and the Cheetah 
    database system
PwrDev
    - Brought to you by PBSoft's Edwin Knoppert
- Formerly known as "VD - Visual Designer for PowerBASIC"
- Commercial app available here
EZGUI
    - http://ezgui.com/
- EZGUI Lite (was EZGUI 1.0; $69; 122KB DLL; Not available as trialware)
- EZGUI 2.0 Visual Designer and GUI engine ($169; 122KB DLL; Not available 
    as trialware). EZGUI 3.0 will support MDI
- EZGUI Freeware Dialog Designer (Freeware DDT Visual Designer; Comes 
    in a Light and full version: The former only offers the standard widgets, 
        while the latter also offers the so-called Common Controls (Tab Control, 
        Progress Bar, UpDown.) It generates a Basic template code that you just 
        copy/paste into your source file)
PowerBasic Forms
Son of PowerGen, commercial product from PB. Dialog 
interface 
builder similar to VB, but adds DDT instructions to BAS file. As long as you 
don't mess up the instructions added by PBForm, you can edit the code back and 
forth between the PB IDE and PBForms.
Personally, the sharing of a BAS file 
between PB and PBForms and the fact that PBForms is a very recent tool... make 
me prefer other tools like EZGUI or even VD. I'm just don't trust such a recent 
tool to parse code correctly without messing up the code I added manually in 
the PB IDE.
PowerGen
PowerGen Visual 
    Designer 16-bit commercial product from 
    PowerBasic. Use a dialog editor such as Microsoft's Dialog Editor or Borland's 
Resource Workshop to generate a resource file, and PowerGen will turn it into 
a .BAS template, not unlike working with VC++ and MFCs. Create the dialog with 
any editor (Microsoft Developer Studio, etc.), save the file as an ASCII-based 
.RC file, run Microsoft's RC.EXE applet to turn into a binary .RES file, use 
PowerBasic's PBRES.EXE applet to convert the .RES file into a .PBR file, and 
add this reference into your source code (#RESOURCE "MYRES.PBR"). 
As an alternative, use James Fuller's RSRC.EXE to embed an .RES file into the 
.EXE .
    - Launch the Microsoft Dialog Editor
- Create a dialog, and save it: This creates two files, MYDLG.DLG (ASCII) 
    and MYDLG.RES (Binary). DE actually works off the .RES file, so any change 
    that you made manually in the .DLG are lost the next time you launch DE 
    and make changes there
- Make a copy of those two files, rename the .DLG file with a .RC extension, 
    and edit it as follows:
        - Remove the following lines from the resource script:
 DLGINCLUDE 
        RCDATA FIXED IMPURE
 BEGIN
 "MYDLG.H\0"
 END
- Add #include "MYDLG.H"
- Add #include "RESOURCE.H"
- Create a  file to include the resource equates, eg. MYDLG.H 
        (equates are constants that you use to refer to widgets through names 
        instead of ID numbers; equates are case sensitive)
- Run this .RC file through the RC.EXE resource compiler. This generates 
    an .RES file... which is incompatible with the original .RES that you created 
    above (hence the backup)!
- Convert this .RES file into a .PBR with the utility PBRES
- Edit your .BAS source file, add a $RESOURCE meta-statement so the PB/DLL 
    compiler includes this resource into the .EXE
Dynamic Dialog Tool (DDT)
Those new commands introduced in PB/DLL 6 let you build a dialog box in your 
source code instead of using a third-party application. Since it's a pain to 
build dialogs in code, you'll probably want to draw them in eg. MS Dialog Editor, 
and fetch coordinates of each widget. James C. Fuller's RC2DDT 
        generates a .BAS template with DDT extensions, ready to copy/paste into 
        your source file
Visual Forms Editor
Cool VB IDE rip-off designer. Proof of concept by Jules 
Marchildon. Only available as a demo, which won't let you save the dialogs you created. 
Jules Marchildon. Available 
on PowerBasic's web site.
Dialog Editor Controls 
Shareware. The demo version won't save 
files. Current release is 2.10. 
Written by Simon Whiteside. More recent editor than MS Dialog Editor, and supports 
common controls.
PWinADP
JC Fuller's PBWinADP interface 
    builder Another free (16-bit) interface builder. Create the UI in a 
RC file with a third-party editor. A PBWin ADP project is a collection of dialogs 
created in an external Dialog Editor; One dialog is set as the starting dialog; 
All dialogs should be named DLG_xxx, where xxx is a number (a description for 
each dialog in the project list.)
Visual MetaFourGL
MetaFourGL, a 
    nice (and free!) interface builder for PowerBasic. No grid to adjust widgets; 
no support for common controls or cool-bar; can't get properties of a widget 
by double-clicking on it; when generating .BAS file, says that it generated 
.BAS and .PBR, but only see .BAS and .RC; Neither Compile nor Run works (if 
try to run generated . BAS with PBDLL.EXE, err with "Required INITCOMMONCONTROLS"; 
Generated source code a bit complicated.
A project (eg. MYPROJ.GGP) includes all the forms (dialogs, eg. MYFRM1.GGF) 
that your application needs. VMGL uses. Add your code in each form. VMGL generates 
a .BAS file by combining the templates UNTITLED.GGC (code pattern used for each 
program), UNTITLE2.GGC (code pattern used for all forms other than the main 
form), and your project-specific files (MYPROJ.GGP and MYFRM1.GGF, here). You 
should not modify the templates UNTITLED.GGC and UNTITLE2.GGC, as upgrading 
to the next version of VMGL will replace those files with the latest version.
CodeMax OCX
(RIP?) Free. Available from WinMain.
Web applications
Here's how to build an EXE that you can call with eg. http://localhost/list.exe?dir=temp, 
and displays a list of OCXs in the directory you specify, each with its filename 
+ date (dd/mm/yyyy) + time (hh:mm:ss) + version number (1.0.0.0):
DDT
Should write an introduction to what is DDT (as opposed to alternatives), 
how to build dialogs (manually; with PBForms, etc.), and how to interact with 
controls
Getting the text selected in a listbox
Like most cases, you need to send a message to the listbox to get the string 
and item # that is currently selected:
    - Dim lIndex as Long
    - Control Send CbHndl, %IDC_LISTBOX1, %LB_GETCURSEL, 0, 0 to lIndex
    - LISTBOX GET TEXT CBHNDL, %IDC_LISTBOX1 TO sListItem
    - MsgBox sListItem, %MB_TASKMODAL,"You selected item#" & 
    str$(lIndex)
Setting the text of a label
In PBMain()
    - CONTROL SET TEXT hDlg, %IDC_LABEL1, "Original text"
In the dialog's event handler:
    - CONTROL SET TEXT CBHNDL, %IDC_LABEL1, "New text
List of DDT instructions
    
        - ACCEL ATTACH    Attach a table of keyboard accelerators to a 
DDT dialog
 
- CALLBACK FUNCTION       Define a Dialog/Control Callback 
Function block
- CBCTL   Return the numeric ID of the control sending a 
callback message
- CBCTLMSG        Return the numeric notification message 
parameter
- CBHNDL  Return the window handle of the parent dialog 
receiving the message
- CBLPARAM        Return the numeric value of the lParam& 
parameter of the message
- CBMSG   Return the numeric value of the message sent by the 
caller
- CBWPARAM        Return the numeric value of the wParam& 
parameter of the message
 
- COMBOBOX ADD    Add a string value to a combo box 
control
- COMBOBOX DELETE Remove a string from a combo box 
control
- COMBOBOX GET TEXT       Retrieve the default text from a 
combo box
- COMBOBOX RESET  Remove all strings from a combo box
- COMBOBOX SELECT Select a string in a combo box and make it 
the default selection
 
- CONTROL ADD     Add a custom control to a DDT dialog
- CONTROL ADD BUTTON      Add a command button to a 
dialog
- CONTROL ADD CHECK3STATE Add an auto 3-state checkbox to a 
dialog
- CONTROL ADD CHECKBOX    Add an checkbox to a dialog
- CONTROL ADD COMBOBOX    Add a combo box to a dialog
- CONTROL ADD FRAME       Add a frame control to a 
dialog
- CONTROL ADD IMAGE       Add a non-resizing image control to 
a dialog
- CONTROL ADD IMAGEX      Add an image control to a 
dialog
- CONTROL ADD IMGBUTTON   Add a non-resizing image button to a 
dialog
- CONTROL ADD IMGBUTTONX  Add an image button to a 
dialog
- CONTROL ADD LABEL       Add a text label to a dialog
- CONTROL ADD LINE        Add a line control to a dialog
- CONTROL ADD LISTBOX     Add a list box control to a 
dialog
- CONTROL ADD OPTION      Add an option button to a 
dialog
- CONTROL ADD SCROLLBAR   Add a scroll bar control to a 
dialog
- CONTROL ADD TEXTBOX     Add a text box control to a 
dialog
 
- CONTROL DISABLE Disable a control so that it no longer 
accepts user interaction
- CONTROL ENABLE  Enable a control so that it can receive user 
interaction
- CONTROL GET CHECK       Get the Check State of a 3-state, 
checkbox, or option button
- CONTROL GET CLIENT      Get the client area dimensions of a 
control
- CONTROL GET LOC Get the location of the specified control in 
a dialog
- CONTROL GET SIZE        Get the size of a control in the 
specified dialog
- CONTROL GET TEXT        Get the text from a control
- CONTROL GET USER        Retrieve a value from the user data 
area of a DDT control
- CONTROL HANDLE  Return a window handle for a given control 
ID
- CONTROL KILL    Remove a control from a dialog
- CONTROL POST    Place a message into the message queue of a 
control (non-blocking)
- CONTROL REDRAW  Schedule a control to be redrawn
- CONTROL SEND    Send a message to a control and wait for it 
to be processed
- CONTROL SET CHECK       Set the Check State for a 3-state or 
checkbox control
- CONTROL SET COLOR       Set the foreground and background 
color of a control
- CONTROL SET FOCUS       Set the keyboard focus to the 
specified control
- CONTROL SET IMAGE       Change the icon or bitmap displayed 
in an IMAGE control
- CONTROL SET IMAGEX      Change the icon or bitmap displayed 
in an IMAGEX control
- CONTROL SET IMGBUTTON   Change the icon or bitmap displayed 
in an IMGBUTTON control
- CONTROL SET IMGBUTTONX  Change the icon or bitmap displayed 
in an IMGBUTTONX control
- CONTROL SET LOC Move the control to a new location in the 
dialog
- CONTROL SET OPTION      Set the Check State for an option 
(radio) control
- CONTROL SET SIZE        Change the size of a control
- CONTROL SET TEXT        Change the text in a control
- CONTROL SET USER        Set a value in the user data area of 
a DDT control
- CONTROL SHOW STATE      Change the visible state of a 
control
 
- DIALOG DISABLE  Disable a dialog so that it no longer 
responds to user interaction
- DIALOG DOEVENTS Process pending window or dialog messages 
for modeless dialogs
- DIALOG ENABLE   Enable a dialog so that it responds to user 
interaction
- DIALOG END      Close and destroy the specified dialog
- DIALOG FONT     Specify the default DDT font and point 
size
- DIALOG GET CLIENT       Return the client size of the 
specified dialog
- DIALOG GET LOC  Return the location of the specified 
dialog
- DIALOG GET SIZE Return the size of the specified 
dialog
- DIALOG GET TEXT Retrieve the text in a dialog or window 
caption
- DIALOG GET USER Retrieve a value from the user data area of 
a DDT dialog
- DIALOG NEW      Create a new dialog in memory, ready for 
display
- DIALOG PIXELS   Convert pixels (device units) into dialog 
units
- DIALOG POST     Place a message in the dialog message queue 
(non-blocking)
- DIALOG REDRAW   Force a dialog and all child controls to be 
redrawn immediately
- DIALOG SEND     Send a message to a dialog and wait for it 
to be processed
- DIALOG SET COLOR        Set the background color of a dialog 
to a specific RGB color
- DIALOG SET ICON Change both the dialog icon in the caption, 
and the icon shown in the ALT+TAB task list
- DIALOG SET LOC  Change the position of a dialog
- DIALOG SET SIZE Change the size of a dialog
- DIALOG SET TEXT Set the text in a dialog or window 
caption
- DIALOG SET USER Set a value in the user data area of a DDT 
dialog
- DIALOG SHOW MODAL       Display and activate a modal 
dialog
- DIALOG SHOW MODELESS    Display and activate a modeless 
dialog
- DIALOG SHOW STATE       Change the visible state of a 
dialog
- DIALOG UNITS    Convert dialog units into pixels
 
- INPUTBOX$       INPUTBOX$ displays a dialog box containing a 
prompt
 
- LISTBOX ADD     Add a string value to a list box 
control
- LISTBOX DELETE  Remove a string from a list box 
control
- LISTBOX GET TEXT        Retrieve the default text from a 
list box control
- LISTBOX RESET   Remove all strings from a list box
- LISTBOX SELECT  Select a string in a list box and make it 
the default selection
 
- MENU ADD POPUP  Add a popup child menu to an existing 
menu
- MENU ADD STRING Add a string or separator to an existing 
menu
- MENU ATTACH     Attach a menu to a given dialog
- MENU DELETE     Delete a menu item from an existing 
menu
- MENU DRAW BAR   Redraw the menu bar for a given dialog
- MENU GET STATE  Return the state of a specified menu 
item
- MENU GET TEXT   Return the text associated with a given menu 
item
- MENU NEW BAR    Create a new menu bar
- MENU NEW POPUP  Create a new popup menu
- MENU SET STATE  Set the state of a specified menu item
- MENU SET TEXT   Set the text of a given menu item
 
- MOUSEPTR        Change the mouse pointer (cursor) to a new 
shape
 
- MSGBOX  Display a message box and get the users Ok/Cancel 
selection
- MSGBOX  Display an informational message box (discard the 
users selection)
Excerpt from the HLP file
You can convert between Dialog Units and Pixels with the built in DIALOG 
PIXELS and DIALOG UNITS statements.
    
    All of the control and dialog message equates are located in the DDT.INC 
file.  This file is simply a subset of the much larger WIN32API.INC file and is 
provided only for convenience.  Therefore, the use of these two files is 
mutually exclusive.
If your code processes one of these event messages, it should return 
non-zero to Windows, by setting FUNCTION = 1 within the Control Callback.  This 
tells Windows that it should not need to process that message any further.  If 
you return zero, Windows will pass this message on to your Dialog Callback.  If 
the message is still unhandled by your Dialog Callback, the DDT dialog engine 
itself will handle the message on your behalf.  This can add unnecessary 
overhead to the performance of your application.
CONTROL ADD BUTTON, hDlg, %IDOK, "OK", 34, 32, 40, 14, %BS_DEFAULT OR 
%WS_TABSTOP CALL OkButton
In the generally case, a dialog Callback Function should return TRUE 
(non-zero) for all %WM_COMMAND messages it processes.  However, this rule cannot 
be equally applied to other types of messages, since the return value will be 
message-specific.
In some cases, especially when dealing with Common Controls and custom 
controls, it can become necessary to return an additional result value to the 
dialog engine through a special data area referred to by the name 
DWL_MSGRESULT.
The default style comprises the combination of %WS_POPUP, %WS_CAPTION, 
%DS_SETFONT, %DS_NOFAILCREATE, %DS_MODALFRAME, and %DS_3DLOOK.  These equates 
are equivalent to a style of &H080C00D4.  The extended style default is 
zero.
If you explicitly specify %WS_CAPTION in your DIALOG NEW statement, then 
DDT will interpret the width and height values as client dimensions, rather then 
as overall dialog dimensions.  This can be very useful for the times when you 
need to build a dialog with particular client dimensions.
Purpose Add a custom control to a DDT dialog.
Syntax  CONTROL ADD classname$, hDlg&, id&, txt$, x, 
y, xx, yy [, [style&] [, [exstyle&]]] [[,] CALL 
callback]  
When the Callback Function receives a %WM_COMMAND message, the identity of 
the control sending the message can be found with the CBCTL function.  Use the 
CBCTLMSG function to retrieve the notification message value in your callback.  
However, many Windows common controls send %WM_NOTIFY messages rather than the 
more conventional %WM_COMMAND messages.  In such cases, the meaning of the 
message parameters CBWPARAM and CBLPARAM will vary according to the type of 
notification message being processed.
Pointers and calling DLLs
More infos here on the solutions available to make calls to DLLs written 
in C.
    - LONG, POINTER
- CODEPTR(FunctionName)
- STRPTR
- VARPTR
- ASCIIZ
- PTR
- AS ANY
- BYREF, BYVAL
Managing data
More infos here.
MySQL
SQLite
Code by Nathan Evans. When 
performing heavy changes such as creating thousands of records, remember to 
send the "BEGIN;" and "COMMIT;" SQL instructions before 
and after making changes, respectively; Otherwise, you will get pathetic performances
SQLTools
From PerfectSync. Eases use of ODBC 
from PowerBasic to connect to SQL servers.
PowerTree
DvBTree
Cheetah
VB/ISAM
Tsunami Record Manager
This is a free one-DLL database application for Windows by Timm Motl (Advantage 
Systems, Timm Motl). To use Tsunami, 
simply copy its header file trm.inc and its engine trm.dll in the same directory 
as your PB/DLL application, and add #include "trm.inc" in the source 
file. That .DLL is the only file that is required to work with a Tsunami data 
file. Tsunami is a page-structured file. Depending on the average size of the 
records that Tsunami will have to handle, you'll have to decide on the size 
of the page size in your Tsunami file, ranging from 1K to 8K. 
    - LOCAL PageSize    AS LONG
    - LOCAL Compression AS LONG
    - LOCAL KeySegments AS LONG
    - LOCAL FileDef     AS STRING
    - LOCAL KeyNo       AS LONG
    - LOCAL KeyFlags    AS LONG
    - LOCAL OverWrite   AS LONG
    - LOCAL lResult as LONG
    - LOCAL InputString AS STRING
    - LOCAL Count       AS QUAD
    - LOCAL FileSize    AS QUAD
    - LOCAL Record      AS STRING
    - LOCAL sResult as String
    -  
    - PageSize = 1
    - Compression = %TRUE
    - KeySegments = 1
    - KeyNo = 1
    - KeyFlags = %NO_COMPRESSION
    -  
    - FileDef = MKBYT$(PageSize) + MKBYT$(Compression) + MKBYT$(KeySegments) + 
    "Account Number           " 
    + MKBYT$(KeyNo) + MKWRD$(1) + MKBYT$(6) + MKBYT$(KeyFlags)
    - OverWrite = %TRUE
    - lResult = trm_Create("MYFILE.DAT", FileDef, OverWrite)
    -  
    - hFile = trm_Open(Path + "TRMDEMO.DAT", %FALSE)
    -  
    - InputString = "  3934DOWNIN                  DANA 
                  4850 
    FREDRICK ST              CORAL 
    SPRINGS   FL33071"
    - lResult = trm_Insert(hFile, InputString)
    -  
    - Count = trm_Count(hFile)
    - FileSize = trm_FileSize(hFile)
    -  
    - Record = trm_GetFirst(hFile, CurrKeyPath)
    - lResult = trm_Result(hFile)
    - sResult = sResult + TRIM$(MID$(Record,  1,  6)) ' Acct No
    - sResult = sResult + TRIM$(MID$(Record,  7, 24)) ' Last Name
    - sResult = sResult + TRIM$(MID$(Record, 31, 18)) ' First Name
    - sResult = sResult + TRIM$(MID$(Record, 49,  1)) ' Middle Initial
    - sResult = sResult + TRIM$(MID$(Record, 50, 30)) ' Street
    - sResult = sResult + TRIM$(MID$(Record, 80, 16)) ' City
    - sResult = sResult + TRIM$(MID$(Record, 96,  2)) ' State
    - sResult = sResult + TRIM$(MID$(Record, 98, 5)) ' Zip Code
    -  
    - Record = trm_GetNext(hFile)
    -  
    - Record = trm_GetEqual(hFile, CurrKeyPath,"  3934")
    -  
    - lResult = trm_CloseAll
 
Common Controls
Those are additional widgets introduced since Windows 95. When using DDT, 
you will have to rely on the CONTROL ADD 
classname instruction.
RichText
    - LOCAL hRichEd AS LONG        
    - LOCAL CC1 AS INIT_COMMON_CONTROLSEX
    
 
- hRichEd = LoadLibrary("RICHED32.DLL")
    
        
    - CC1.dwSize=SIZEOF(CC1)
    
        
    - CC1.dwICC=%ICC_WIN95_CLASSES
    
        
    - INITCOMMONCONTROLSEX CC1
Tips and Tricks
Zipping
A PowerBasic interface to zLib.dll is here.
Checking if a file exists
    - FUNCTION Exist(File$) AS LONG
    -   LOCAL Dummy&
    -   Dummy& = GETATTR(File$)
    -   FUNCTION = (ERRCLEAR = 0)
    - END Function
Note: Some routines given in the PowerBasic forum such as FileExists() 
don't work when used in a DIR$ loop.
Writing CGI programs
Creating Dynamic 
Web Pages with PB/CC
Regex
To extract the section of a string
    - Dim sFull as String, sMask as String, lPosVar as Long, lLenVar as Long
    - sFull = "I can be reached at john@acme.com"
    - sMask    = "[a-z_.-]+@[a-z_.-]+"
    - REGEXPR sMask IN sFull TO lPosVar, lLenVar
    - Msgbox MID$(sFull, lPosVar, lLenVar)
To extract a tag
A tag is defined as a sub-pattern of a mask. In this example, we'd like to 
extract just the actual content of the Description metatag of a web page, ie. 
ignore the rest of the mask (let's pretend the web page was read into sFull). 
As of June 2004, it appears that the REGEXPR or REGREPL functions can't do this, 
so we'll use other functions:
    - sInput = "blablabla<meta name=""description"" 
    content="some cool doco">blablabla"
    - 'REMAIN() = return stuff after this pattern
    - 'EXTRACT() = return stuff until this pattern
    - MsgBox EXTRACT$(REMAIN$(sInput,"description"" content="""),""">")
Replacing a section in a string
    - sFull = ""
    - sMask = ""
    - sReplaceMask = ""
    - REGREPL sMask IN sFull WITH sReplaceMask TO iPosvar, sNewFull
    - MsgBox sNewFull
Compiling from UltraEdit
Advanced | Tool Configurations: 
    - Command line = D:\PBWin70\Bin\PBWin.exe %F /I.\;D:\PBWIN70\WINAPI (The 
    /I switch is used to tell UltraEdit where to locate the include files; Note 
    that ".\" is required to tell UE to start search in the current 
    work directory. Also, if you installed PB using long filenames for directories, 
    remember that PB/DLL is a 16-bit compiler... So, you should use the 8.3 
    notation such as d:\progra~1\pbdll60\bin\Pbdll.exe  %F)
- Working directory = %P
- Menu Item Name = PB/DLL
Click on Insert, and OK
PowerBasic add-on for UltraEdit
To tell UltraEdit how to handle PowerBasic files, just edit WORDFILE.TXT 
located in UltraEdit's directory, and add this section (Note: Make sure no L11 
section already exists):
    - /L11"PowerBasic" Line Comment Num = 2' Line 
    Comment = ' Block Comment On Alt = #PBFORMS File Extensions = BAS INC
    - /Delimiters = ~!@^*()+=|\{}[]:;"'       ,.<>
    - /Function String = "%*^{Sub^}^{Function^}*("
    - /C1="STATEMENTS"
    - ABS ACCEL ACCEPT ACCESS ACODE$ ADD ADDR ALIAS ALL AND ANY APPEND ARRAY 
    ARRAYATTR AS ASC ASCEND ASCIIZ ASCIZ ASM AT ATN ATTACH ATTRIB
    - BAR BASE BAUD BDECL BEEP BIN$ BINARY BIT BITS% BITS& BITS? BITS?? 
    BITS??? BREAK BUTTON BYCMD BYCOPY BYREF BYTE BYVAL
    - CALC CALL CALLBACK CALLSTK CALLSTK$ CALLSTKCOUNT CASE CATCH CBCTL CBCTLMSG 
    CBHNDL CBLPARAM CBMSG CBWPARAM CBYT CCUR CCUX CD CDBL 
    - CDECL CDWD CEIL CEXT CHDIR CHDRIVE CHECK CHECK3STATE CHECKBOX CHOOSE 
    CHOOSE$ CHOOSE% CHOOSE& CHR$ CINT CLIENT CLNG CLOSE CLSID$ 
    - CODEPTR COLLATE COLOR COMBOBOX COMM COMMAND$ CON CONNECT CONST CONST$ 
    CONTROL COS CQUD CREATE CSET CSET$ CSNG CTSFLOW CUR CURDIR$ 
    - CURRENCY CURRENCYX CUX CVBYT CVCUR CVCUX CVD CVDWD CVE CVI CVL CVQ CVS 
    CVWRD CWRD
    - DATA DATACOUNT DATE$ DECLARE DECR DEFAULT DEFBYT DEFCUR DEFCUX DEFDBL 
    DEFDWD DEFEXT DEFINT DEFLNG DEFQUD DEFSNG DEFSTR DEFWRD DELETE 
    - DESCEND DIALOG DIM DIR$ DISABLE DISKFREE DISKSIZE DISPATCH DLL DLLMAIN 
    DLLMAIN& DO DOEVENTS DOUBLE DRAW DSRFLOW DSRSENS DTRFLOW 
    - DTRLINE DWORD
    - ELSE ELSEIF EMPTY ENABLE END ENVIRON$ EOF EQV ERASE ERR ERRAPI ERRCLEAR 
    ERROR ERROR$ EXE EXIT EXP EXP10 EXP2 EXPLICIT EXPORT EXT 
    - EXTENDED EXTRACT$
    - FILEATTR FILECOPY FILENAME$ FILESCAN FILL FINALLY FIX FLOW FLUSH FOCUS 
    FONT FOR FORMAT$ FRAC FRAME FREEFILE FROM FUNCNAME$ FUNCTION
    - GET GET$ GETATTR GLOBAL GOSUB GOTO GUID GUID$ GUIDTXT$
    - HANDLE HEX$ HIBYT HIINT HIWRD HOST
    - ICASE ICON IDN IF IFACE IIF IIF$ IIF% IIF& IMAGE IMAGEX IMGBUTTON 
    IMGBUTTONX IMP IN INCR INOUT INP INPUT INPUT# INPUT$ INPUTBOX$ 
    - INSERT INSTR INT INTEGER INTERFACE INV ISFALSE ISNOTHING ISOBJECT ISTRUE 
    ITERATE
    - JOIN$
    - KILL
    - LABEL LBOUND LCASE$ LEFT LEFT$ LEN LET LIB LIBMAIN LIBMAIN& LINE 
    LISTBOX LOBYT LOC LOCAL LOCK LOF LOG LOG10 LOG2 LOINT LONG LOOP 
    - LOWRD LSET LSET$ LTRIM$
    - MACRO MACROTEMP MAIN MAIN& MAKDWD MAKINT MAKLNG MAKPTR MAKWRD MAT 
    MAX MAX$ MAX% MAX& MCASE$ MEMBER MENU MID$ MIN MIN$ MIN% MIN& 
    - MKBYT$ MKCUR$ MKCUX$ MKD$ MKDIR MKDWD$ MKE$ MKI$ MKL$ MKQ$ MKS$ MKWRD$ 
    MOD MODAL MODELESS MOUSEPTR MSGBOX
    - NAME NEW NEXT NONE NOT NOTHING NOTIFY NULL
    - OBJACTIVE OBJECT OBJPTR OBJRESULT OCT$ OF OFF ON OPEN OPT OPTION OPTIONAL 
    OR OUT OUTPUT
    - PARITY PARITYCHAR PARITYREPL PARITYTYPE PARSE PARSE$ PARSECOUNT PBLIBMAIN 
    PBLIBMAIN& PBMAIN PBMAIN& PEEK PEEK$ PIXELS POINTER POKE 
    - POKE$ POPUP PORT POST PRESERVE PRINT PRINT# PRIVATE PROFILE PROGID$ 
    PTR PUT PUT$
    - QUAD QWORD
    - RANDOM RANDOMIZE READ READ$ RECORDS RECV REDIM REDRAW REGEXPR REGISTER 
    REGREPL REM REMAIN$ REMOVE$ REPEAT$ REPLACE RESET RESUME 
    - RETAIN$ RETURN RGB RIGHT RIGHT$ RING RLSD RMDIR RND ROTATE ROUND RSET 
    RSET$ RTRIM$ RTSFLOW RXBUFFER RXQUE
    - SCAN SCROLLBAR SDECL SEEK SELECT SEND SERVER SET SETATTR SETEOF SGN 
    SHARED SHELL SHIFT SHOW SIGNED SIN SINGLE SIZE SIZEOF SLEEP SORT 
    - SPACE$ SPC SQR STATE STATIC STATUS STDCALL STEP STOP STR$ STRDELETE$ 
    STRING STRING$ STRINSERT$ STRPTR STRREVERSE$ SUB SUSPEND SWAP 
    - SWITCH SWITCH$ SWITCH% SWITCH&
    - TAB TAB$ TAGARRAY TALLY TAN TCP TEXT TEXTBOX THEN THREAD THREADCOUNT 
    THREADID TIME$ TIMEOUT TIMER TO TOGGLE TRACE TRIM$ TRN TRY 
    - TXBUFFER TXQUE TYPE
    - UBOUND UCASE UCASE$ UCODE$ UDP UNION UNITS UNLOCK UNTIL USER USING USING$
    - VAL VARIANT VARIANT# VARIANT$ VARIANTVT VARPTR VERIFY VERSION3 VERSION4 
    VERSION5
    - WEND WHILE WIDTH WIDTH# WINMAIN WINMAIN& WITH WORD WRITE WRITE#
    - XINPFLOW XOR XOUTFLOW
    - ZER
    - /C2"PRECOMPILER"
    - !
    - #BLOAT #COMPILE #DEBUG #DIM #ELSE #ELSEIF #ENDIF #IF #INCLUDE #OPTION 
    #PBFORMS #Register #Resource #Stack #Tools $Bel
    - $BLOAT $COMPILE $DEBUG $DIM $ELSE $ELSEIF $ENDIF $IF $INCLUDE $OPTION 
    $REGISTER $RESOURCE $STACK $TOOLS
    - /C2"DEFINITIONS"
    - $CR $CRLF $BS $DQ $EOF $ESC $FF $LF $NUL $SPC $TAB $VT
    - %BLACK %BLUE %CYAN %GRAY %GREEN %LTGRAY %MAGENTA %RED
    - %DEF
    - %ERR_BADFILEMODE %ERR_BADFILENAME %ERR_BADFILENAMEORNUMBER %ERR_BADRECORDNUMBER 
    %ERR_COMMERROR %ERR_DEVICEIOERROR %ERR_DEVICETIMEOUT 
    - %ERR_DEVICEUNAVAILABLE %ERR_DISKFULL %ERR_DISKMEDIAERROR %ERR_DISKNOTREADY 
    %ERR_FARHEAPCORRUPT %ERR_FILEALREADYEXISTS 
    - %ERR_FILEISOPEN %ERR_FILENOTFOUND %ERR_ILLEGALFUNCTIONCALL %ERR_INPUTPASTEND 
    %ERR_INTERNALERROR %ERR_NOERROR %ERR_OUTOFMEMORY 
    - %ERR_PATHFILEACCESSERROR %ERR_PATHNOTFOUND %ERR_PERMISSIONDENIED %ERR_RENAMEACROSSDISKS 
    %ERR_STRINGSPACECORRUPT 
    - %ERR_SUBSCRIPTPOINTEROUTOFRANGE %ERR_TOOMANYFILES
    - %PB_CC32 %PB_DLL16 %PB_DLL32 %PB_EXE %PB_REVISION %PB_REVLETTER %PB_WIN32
    - %VARCLASS_ASC %VARCLASS_BYT %VARCLASS_CUR %VARCLASS_CUX %VARCLASS_DBL 
    %VARCLASS_DWD %VARCLASS_EXT %VARCLASS_FIX %VARCLASS_GUID 
    - %VARCLASS_IFAC %VARCLASS_INT %VARCLASS_LNG %VARCLASS_QUD %VARCLASS_SNG 
    %VARCLASS_STR %VARCLASS_TYPE %VARCLASS_VRNT %VARCLASS_WRD
    - %VT_ARRAY %VT_BLOB %VT_BLOB_OBJECT %VT_BOOL %VT_BSTR %VT_BYREF %VT_CARRAY 
    %VT_CF %VT_CLSID %VT_CY %VT_DATE %VT_DISPATCH %VT_EMPTY 
    - %VT_ERROR %VT_FILETIME %VT_HRESULT %VT_I1 %VT_I2 %VT_I4 %VT_I8 %VT_INT 
    %VT_LPSTR %VT_LPWSTR %VT_NULL %VT_PTR %VT_R4 %VT_R8 
    - %VT_SAFEARRAY %VT_STORAGE %VT_STORED_OBJECT %VT_STREAM %VT_STREAMED_OBJECT 
    %VT_UI1 %VT_UI2 %VT_UI4 %VT_UI8 %VT_UINT %VT_UNKNOWN 
    - %VT_USERDEFINED %VT_VARIANT %VT_VECTOR %VT_VOID
    - %WHITE
    - %YELLOW
Timing a task
    - LOCAL Start       AS DWORD
    - LOCAL Finish      AS DWORD
    - LOCAL TotalTime   AS DWORD
    - Start = GetTickCount
    - Finish = GetTickCount
    - TotalTime = TotalTime + (Finish - Start)
Coordinates
Check where a user double-clicked in a dialog box
    - SELECT CASE CBMSG
    -     Case %WM_LBUTTONDBLCLK
    -         LOCAL sEvent AS ASCIIZ 
    * 32, iX AS INTEGER, iY AS INTEGER
    -         
    -         iX = LOWRD(CBLPARAM)
    -         iY = HIWRD(CBLPARAM)
    -  
    -         sEvent = "x=" 
    + Str$(iX) + ", y=" + str$(iY)
    -  
    -         Call SetWindowText (CBHNDL,sEvent)
Icons
Setting the title bar icon
    - DIALOG SEND hDlg, %WM_SETICON, %ICON_SMALL, LoadIcon(BYVAL %NULL, BYVAL 
    %IDI_WINLOGO)
Adding an icon in the resource file, and loading it
    
        - In the .RC file, add references to icon files: MYICON               ICON 
           DISCARDABLE     "cool.ico"
- Use RC.EXE to convert this .RC file into a .RES file
- Use PBRES.EXE to turn the .RES file into a .PBR
- In your program, add a reference: #RESOURCE "MYRES.PBR"
- Load the icon: Call SendMessage (hDlg, %WM_SETICON, %ICON_SMALL, 
        LoadIcon(GetModuleHandle("MYPRG.EXE"), "MYICON"))
Displaying an icon
    - hDC = GetDC (CBHNDL)
    - Call DrawIcon (hDC, 33, 180, hIcon)
    - Call ReleaseDC (CBHNDL, hDC)
Static
Selecting a different font
    - LOCAL hOldFont AS LONG
    - LOCAL hNewFont AS LONG
    -  
    - hNewFont = CreateFont(0,0,0,0, %FW_HEAVY        ,%TRUE,%FALSE,%ANSI_CHARSET,%OUT_TT_PRECIS,0, 
    _
    -     %PROOF_QUALITY,%DEFAULT_PITCH,%FF_DONTCARE,"Arial")
    -  
    - CONTROL Send CBHNDL, %IDC_LABEL,%WM_GETFONT,0,0 to hOldFont
    - CONTROL Send CBHNDL, %IDC_LABEL, %WM_SETFONT, hNewFont,0
    - CONTROL Set TEXT hDlg, %IDC_LABEL, "PowerBasic Rules!"
    - CONTROL Send CBHNDL, %IDC_LABEL,hOldFont,0,0
Changing the font
    - LOCAL hOldFont AS LONG
    -  
    - CONTROL Send CBHNDL, %IDC_LABEL,%WM_GETFONT,0,0 to hOldFont
    - CONTROL Send CBHNDL, %IDC_LABEL, %WM_SETFONT, GetStockObject( %SYSTEM_FIXED_FONT 
    ),0
    - CONTROL Set TEXT hDlg, %IDC_LABEL, "PowerBasic Rules!"
    - CONTROL Send CBHNDL, %IDC_LABEL,hOldFont,0,0
RichEdit widget
Change color and font
    - Type charformat2a
    -     cbSize As Long
    -     dwMask As Dword
    -     dwEffects As Dword
    -     yHeight As Long
    -     yOffset As Long
    -     crTextColor As Dword
    -     bCharSet As Byte
    -     bPitchAndFamily As Byte
    -     szFaceName As Asciiz * %LF_FACESIZE
    -     wpad2 As Integer
    -     wWeight As Word
    -     sSpacing As Integer
    -     crBackColor As Dword
    -     lcid As Long
    -     dwReserved As Dword
    -     sStyle As Integer
    -     wKerning As Word
    -     bUnderlineType As Byte
    -     bAnimation As Byte
    -     bRevAuthor As Byte
    -     bReserved1 As Byte
    - End Type
    -  
    - LOCAL MyFormat AS charformat2a
    - LOCAL sFaceName AS ASCIIZ * 64
    - sFaceName = "Verdana"
    -                                         
    - MyFormat.cbSize = SizeOf(MyFormat)
    - MyFormat.dwMask = %CFM_FACE OR %CFM_COLOR
    - MyFormat.szFaceName = sFaceName
    - MyFormat.crTextColor = RGB (0,0,255)
    -  
    - CONTROL Send CBHNDL, %IDC_RICHEDIT1, %EM_SETCHARFORMAT, 0,VARPTR(MyFormat)
Threads
    - #COMPILE EXE
    - #DIM ALL
    - #REGISTER NONE
    - #INCLUDE "WIN32API.INC"
    -  
    - GLOBAL hDlg                              AS 
    LONG
    -  
    - FUNCTION ThreadInst(BYVAL lVal AS LONG) AS LONG
    -     LOCAL lRet AS LONG
    -  
    -     'Need WHILE, or will execute once and go
    -     While %TRUE
    -         Call 
    SetWindowText (hDlg, TIME$)
    -         SLEEP 
    1000
    -     Wend
    - End FUNCTION
    -  
    - CALLBACK FUNCTION IDD_DIALOG1_CB AS LONG
    -     'Nichts
    - END FUNCTION
    -  
    - FUNCTION PBMain () AS LONG
    -     LOCAL hThread AS LONG, lRetVal AS LONG
    -  
    -     THREAD CREATE ThreadInst(0) 
    TO hThread
    -     
    -     DIALOG NEW 0, "About to be erased...", 
    0, 0, 279, 169, _
    -             %DS_MODALFRAME 
    OR %DS_CENTER OR %WS_POPUP OR %WS_CAPTION OR %WS_SYSMENU, _
    -             %WS_EX_TOOLWINDOW, 
    TO hDlg
    -  
    -     DIALOG SHOW MODAL hDlg,CALL IDD_DIALOG1_CB TO 
    lRetVal
    -     
    -     THREAD CLOSE hThread TO retVal
    -         
    - END FUNCTION
Timer
    - #COMPILE EXE
    - #DIM ALL
    - #REGISTER NONE
    - #INCLUDE "WIN32API.INC"
    -  
    - GLOBAL hDlg                              AS 
    LONG
    -  
    - CALLBACK FUNCTION IDD_DIALOG1_CB AS LONG
    -   
    -     SELECT CASE CBMSG
    -         Case 
    %WM_TIMER
    -             Call 
    SetWindowText (hDlg, TIME$)
    -     END SELECT
    - END FUNCTION
    -  
    - FUNCTION PBMain () AS LONG
    -     LOCAL hThread AS LONG, lRetVal AS LONG
    -     DIALOG NEW 0, "About to be erased...", 
    0, 0, 279, 169, _
    -             %DS_MODALFRAME 
    OR %DS_CENTER OR %WS_POPUP OR %WS_CAPTION OR %WS_SYSMENU, _
    -             %WS_EX_TOOLWINDOW, 
    TO hDlg
    -  
    -     Call SetTimer(hDlg, BYVAL 
    &H0000FEED, 1000, BYVAL %NULL)
    -  
    -     DIALOG SHOW MODAL hDlg,CALL IDD_DIALOG1_CB TO 
    lRetVal
    -         
    -     KillTimer hDlg, &H0000FEED
    - END FUNCTION
TreeView
    - Add a control in your dialog box
 
 CONTROL ADD "SYSTREEVIEW32", 
    hDlg, %IDC_TREE1, "Tree1", 7, 7, 202, 72, _
 %WS_CHILD 
    or %WS_VISIBLE or %TVS_HASBUTTONS or %TVS_HASLINES or _
 %TVS_LINESATROOT 
    or %TVS_SHOWSELALWAYS, %WS_EX_CLIENTEDGE, _ , 'CALL IDC_TREE1_CB
 
- In the dialog callback function, add items to the tree:
 
 GLOBAL 
    CurrentItem AS STRING
 GLOBAL hDlgTree As Long
 
 FUNCTION 
    TVInsertItem(byval hTree as long, byval hParent as long, sTxt as string) 
    as long
 local tv_insert as TV_INSERTSTRUCT
 local 
    tv_item as TV_ITEM
 
 if hParent then
 tv_item.mask 
         = %TVIF_CHILDREN OR %TVIF_HANDLE
 tv_item.hItem 
        = hParent
 tv_item.cchildren 
    = 1
 TreeView_SetItem 
    hTree, tv_item
 end if
 
 tv_insert.hParent 
                 = 
    hParent
 tv_insert.Item.Item.mask       = 
    %TVIF_TEXT
 tv_insert.Item.Item.pszText    = 
    strptr(sTxt)
 tv_insert.Item.Item.cchTextMax = 
    len(sTxt)
 
 function = TreeView_InsertItem(hTree, 
    tv_insert)
 
 end function
 
 CALLBACK FUNCTION IDD_DIALOG1_CB 
    AS LONG
 
 LOCAL hTree AS LONG, hRoot AS LONG, hParent AS LONG
 LOCAL 
    lpNmh As NMHDR Ptr
 LOCAL lpTV As NM_TREEVIEW PTR
 LOCAL szTxt As AsciiZ 
    * 61
 STATIC tItem As TV_ITEM
 
 hDlgTree 
    = GetDlgItem(CbHndl,%IDC_TREE1)
 
 SELECT CASE CBMSG
 CASE 
    %WM_INITDIALOG
 CONTROL 
    HANDLE CBHNDL, %IDC_TREE1 to hTree
 hRoot 
    = TVInsertItem(hTree, 0, "Inbox")
 hParent 
    = TVInsertItem(hTree, hRoot, "From GROUCHO: ")
 hParent 
    = TVInsertItem(hTree, hRoot, "From ZEPPO: ")
 hRoot 
    = TVInsertItem(hTree, 0, "Outbox")
 hParent 
    = TVInsertItem(hTree, hRoot, "From HARPO: ")
 hParent 
    = TVInsertItem(hTree, hRoot, "From CHICO: ")
 
 case %WM_NOTIFY
 lpNmh 
    = CblParam
 
 Select Case @lpNmh.Code
 Case %TVN_SELCHANGED
 lpTV 
    = CblParam
-         tItem.hitem = @lpTV.ItemNew.hItem
    -         tItem.mask = %TVIF_TEXT
    -         tItem.pszText = VarPtr(szTxt)
    -         tItem.cchTextMax = 61&
    -         Call TreeView_GetItem(hDlgTree, 
    tItem)
    -         CurrentItem = szTxt
 
-       Case %NM_DBLCLK
 CONTROL 
    SET TEXT CBHNDL, %IDC_RICHEDIT1, CurrentItem
 
-       Case %NM_RCLICK
 Call 
    TreeView_DeleteItem (hDlgTree, tItem.hitem)
 
 End 
    Select
Listbox
    - 'Get list of items in listbox
    - CONTROL SEND CBHNDL, %IDC_LIST1, %LB_GETCOUNT, 0, 0 TO lResult
    -  
    - 'Get currently select item (doesn't work for multiple-selection listboxes)
    - CONTROL SEND CBHNDL , %IDC_LIST1, %LB_GETCURSEL, 0,0 TO lResult
    - 'Other way
    - LISTBOX GET TEXT CBHNDL, %IDC_LIST1 TO sTemp          
    -  
    - 'Get select items in multiple-selection listbox
    - lResult=SendMessage(GetDlgItem(CBHNDL,%IDC_LIST1), %LB_GETCOUNT,0,0) 
     ' get count of items
    - IF lResult>0 THEN
    -     lResult=lResult-1
    -     FOR iIndex=0 to lResult   ' zero indexed
    -         IF (SendMessage(GetDlgItem(CBHNDL,%IDC_LIST1), 
    %LB_GETSEL, iIndex, 0)) THEN
    -         sTemp = SPACE$(SendMessage(GetDlgItem(CBHNDL,%IDC_LIST1), 
    %LB_GETTEXTLEN, iIndex, 0) + 1)
    -         iTemp=SendMessage(GetDlgItem(CBHNDL,%IDC_LIST1), 
    %LB_GETTEXT, iIndex, STRPTR(sTemp))
    -         MsgBox sTemp
    -     END IF
    -     NEXT iIndex
    - END If
    -  
    - 'Another way, by iterating through array of pointers to selected items
    - lResult = SendMessage(hList, %LB_GETSELCOUNT, 0, 0)
    - IF  lResult <> %LB_ERR THEN
    -     REDIM lArray(lResult - 1) AS LONG
    -     ReDim sTemp(lResult - 1) AS String
    -     lResult = SendMessage(hList, %LB_GETSELITEMS, 
    lResult, VARPTR(lArray(0)))
    - END IF
    -  
    - FOR iIndex= 0 TO UBOUND(lArray)
    -     lResult = SendMessage(hList, %LB_GETTEXT, lArray(iIndex), 
    STRPTR(sTemp(iIndex)))
    -     MsgBox sTemp(iIndex)
    - Next iIndex
    -  
    - 'Empty listbox
    - Call SendMessage(hList, %LB_RESETCONTENT, 0, 0)  
    -  
    - 'Check if string exists
    - Control Send hDlg,%HDList,%LB_FINDSTRINGEXACT, -1,ByVal StrPtr(Msg$) 
    To Li&
    - 'Add string
    - Call SendMessage(hList, %LB_ADDSTRING, 0, BYVAL STRPTR(txt))
    - 'Alternative
    - ListBox Add hDlg, %ID_LIST1, Txt
Checking the number of items
    - iIndex = SendMessage (hControl, %LB_GETCOUNT, 0, 0) 
Avoid STRING variables
From Chris Boss
If you attempt to use string variables in PB DLL, your program will be 
using the Windows OLE string engine. An awful lot of processing goes on in the 
background, when you use string variables. The C code is using the CHAR type, 
which if my memory serves me, would be comparable to a Byte array in PB and 
not a string variable. Because the OLE string engine (which even VB uses) is 
significantly slower than working with a byte array, the PB DLL code should 
be significantly slower than the C code.
Displaying a Unicode string
MsgBox only accepts ANSI/ASCII strings. To display a Unicode string, either 
convert it with the Win32 API WideCharToMultiByte(), or use this:
    - DECLARE FUNCTION SetWindowTextW LIB "USER32.DLL" ALIAS "SetWindowTextW" 
    (BYVAL hwnd AS LONG, BYVAL lString AS LONG) AS LONG
    -  
    - LOCAL hDlg AS LONG
    - LOCAL sMyString AS ASCIIZ * 64
    - LOCAL lResult AS LONG
    -  
    - lResult = SetWindowTextW (hDlg, BYVAL VARPTR(sMyString))
Note that the SetWindowText defined in PB/DLL's WIN32API.INC is the ANSI 
version (SetWindowTextA).
An alternative: Declare FUNCTION MessageBoxW LIB "USER32.DLL" ALIAS 
"MessageBoxW" (BYVAL hwnd AS DWORD, lpText AS ASCIIZ, lpCaption AS 
ASCIIZ, BYVAL wType AS LONG) AS LONG
Computing the length of an ASCIIZ
Len(sMyString) doesn't work until you used sMyString = SPACE$(128) to fill 
it up with spaces. The reason is that, after an ASCIIZ variable is created, 
it is initialized by PowerBasic with a 0... which leads Len() to consider that 
the size of the string is equal to 0. Use SizeOf(sMyString) instead.
TPC/IP - Connecting to a web server to download headers
    - ERRCLEAR
    -  
    - LOCAL sBuffer as String
    - LOCAL nTCP as INTEGER
    - LOCAL n AS INTEGER
    - LOCAL sBigBuffer as String
    - LOCAL sServer as String
    -         
    - nTCP = FREEFILE
    -  
    - sServer = INPUTBOX$("WWW server?")
    -  
    - TCP Open PORT 80 AT sServer AS nTCP
    - If ERR THEN MSGBOX "Server could not be reached!"
    -         
    - TCP PRINT nTCP, "HEAD / HTTP/1.0"
    - TCP PRINT nTCP, ""
    - TCP PRINT nTCP, ""              
    -         
    - Do
    -     TCP LINE nTCP, sBuffer
    -     sBigBuffer = sBigBuffer + chr$(13) + sBuffer
    - Loop While (Len(sBuffer) <> 0)
    -         
    - TCP CLOSE nTCP
    -  
    - MsgBox sBigBuffer
Note: Under 98/98SE, at least with PB/Win 7.04, you may trigger an error 
when setting the port using aliases such as "http" instead of setting 
its numeric value, eg. "PORT 80". The following always triggered error 
57 under 98SE:
    - ERRCLEAR
    -  
    - 'Replace with PORT 80
    - TCP OPEN "http" AT "www.acme.com" AS #1 TIMEOUT 
    5000
    - If ISFALSE ERR Then
    -     MsgBox "Online"
    -     TCP Close #1  
    - Else
    -     MsgBox "Offline",,"Err = " 
    & str$(ERR)
    - END IF
Drawing text in the main window
    - CASE %WM_PAINT
    -     hDC = BeginPaint(hWnd, LpPaint)
    -     GetClientRect hWnd, tRect
    -                         
    -     For iIndex = 1 TO 10
    -         sBufferA = "Test" 
    + str$(iIndex)
    -         lResult = TextOut(hDc, 
    100, iIndex * 50, sBufferA, Len(sBufferA))
    -     Next iIndex
    -  
    -     For iIndex = 1 TO 10
    -         tRect.nLeft = 100
    -         tRect.nTop = 100 (iIndex/2)
    -         tRect.nRight = 200
    -         tRect.nBottom = 600
    -         DrawText hDC, "Line 
    " + str$(iIndex), -1, tRect, %DT_SINGLELINE OR %DT_CENTER 'OR %DT_VCENTER
    -     Next iIndex
    -  
    -     EndPaint hWnd, LpPaint
    -  
Creating child windows, and sending messages to them
    - GLOBAL hButton AS LONG
    - GLOBAL hList AS LONG
    -  
    - %Button = 100
    - %List  = 101
    -  
    - hButton = CreateWindow("BUTTON", "Click me", %WS_VISIBLE 
    OR  %WS_CHILD OR %BS_DEFPUSHBUTTON, _ 
    -                       10, 
    10, 100, 100,hWnd, %Button, hInstance, %NULL)
    -  
    - hList = CreateWindow("LISTBOX", "", %WS_VISIBLE 
    OR  %WS_CHILD OR %LBS_EXTENDEDSEL OR %LBS_HASSTRINGS OR %LBS_STANDARD, 
    _
    -                       110, 
    10, 150, 150, hWnd, %List, hInstance, %NULL)
    -  
Converting from ANSI to Unicode, and sending a pop-up message
    - LOCAL sToHostA as ASCIIZ * %STR_MAX_SIZE
    - LOCAL sToHostW as ASCIIZ * (%STR_MAX_SIZE * 2)
    -  
    - LOCAL sBufferA AS ASCIIZ * %STR_MAX_SIZE
    - LOCAL sBufferW AS ASCIIZ * (%STR_MAX_SIZE * 2)
    -  
    - sBufferA = "All your base are belong to us"
    -         
    - SELECT CASE wMsg
    -     Case %WM_CREATE   
    -         GetUserName sToHostA, 
    %STR_MAX_SIZE
    -  
    -         lResult = MultiByteToWideChar 
    (%CP_ACP, 0&, sToHostA, -1, sToHostW, Len(sToHostA) * 2)
    -         lResult = MultiByteToWideChar 
    (%CP_ACP, 0&, sBufferA, -1, sBufferW, Len(sBufferA) * 2)
    -  
    -         lResult = NetMessageBufferSend 
    ("", sToHostW, "", sBufferW, %STR_MAX_SIZE * 2)
Reading from a sequential file
    - LOCAL strHosts() AS STRING
    -  
    - ERRCLEAR
    - hFile = FREEFILE
    - OPEN "HOSTS.DAT" FOR INPUT AS hFile
    - IF ERR = 53 THEN
    -     MSGBOX "Error: No HOSTS.DAT in local directory. 
    Exiting."
    -     EXIT FUNCTION
    - END IF
    -  
    - iIndex = 0
    - WHILE NOT EOF(hFile)
    -     REDIM PRESERVE strHosts(iIndex + 1) AS STRING
    -     LINE INPUT #hFile, strHosts(iIndex)
    -     iIndex = iIndex + 1
    - WEND
    -  
    - CLOSE hFile
Writing to a sequential file
    - OPEN "OPEN.DTA" FOR OUTPUT AS #1
    - PRINT# 1, sBufferA
    - CLOSE 1
DDT - Obtaining selected items in listbox
    - CALLBACK FUNCTION DlgProc()
    -  
    -         LOCAL lReturn AS LONG
    -             
    -         SELECT CASE CBMSG
    -         
    -                 CASE 
    %WM_COMMAND
    -                 
    -                         SELECT 
    CASE CBCTL
    -                         
    -                                 CASE 
    %IDLIST
    -                                         IF 
    (CBCTLMSG = %CBN_SELCHANGE) THEN
    -                                                 CONTROL 
    SEND CBHNDL , CBCTL, %LB_GETCURSEL, 0,0 TO lReturn
    -                                                 CONTROL 
    SEND CBHNDL, CBCTL, %LB_GETTEXT, lReturn, VARPTR(sToHostA)
    -                                         END 
    IF
    -                                         
    -                                         FUNCTION=0
    -                                         EXIT 
    FUNCTION
    -                         
    -                         END 
    SELECT
    -                 
    -         END SELECT
    -  
    - END FUNCTION
Displaying the host's workgroup/domain
    - FUNCTION StrToAnsi(BYVAL u AS LONG) AS STRING
    -  
    -     DIM Buffer AS STRING
    -     DIM x      AS STRING
    -     DIM l      AS LONG
    -  
    -     l = lstrlenW(BYVAL u)
    -     x = PEEK$(u, l * 2)
    -  
    -     Buffer = SPACE$(LEN(x) \ 2)
    -  
    -     WideCharToMultiByte %CP_ACP, _               ' 
    code page
    -                       %NULL, 
    _                 ' 
    performance and mapping flags
    -                       BYVAL 
    STRPTR(x), _       ' Unicode string to convert
    -                       LEN(x), 
    _                ' 
    len of Unicode string
    -                       BYVAL 
    STRPTR(Buffer), _  ' buffer for ANSI string
    -                       LEN(Buffer), 
    _           ' len of ANSI 
    buffer
    -                       BYVAL 
    %NULL, _           ' default 
    for unmappable chars
    -                       BYVAL 
    %NULL              ' 
    default flag
    -  
    -     IF LEN(Buffer) = 0 THEN
    -         Buffer = "[none]"
    -     END IF
    -  
    -     FUNCTION = RTRIM$(Buffer)
    -  
    - END FUNCTION
    -  
    - LOCAL MyWKSTA_INFO_100 AS WKSTA_INFO_100 PTR
    -  
    - lResult = NetWkstaGetInfo(BYVAL %NULL, 100, MyWKSTA_INFO_100)
    - MsgBox "System name: \\" & StrToAnsi(@MyWKSTA_INFO_100.wki100_langroup)
Networking
Enumerating the list of shared drives on the LAN
To achieve this, the EnumAll() function below is called recursively so as 
to list all the shared drives for each host, and exit until all the entries 
have been parsed:
    - Function EnumAll (nr AS NETRESOURCE) as String
    -     LOCAL hEnum   AS LONG
    -     LOCAL Entries AS LONG
    -     LOCAL nSize   AS LONG
    -     LOCAL ec      AS LONG
    -     LOCAL x       AS 
    LONG
    -  
    -     'Static because this string but live through 
    each recurrent call
    -     Static sStuff as String
    -  
    -     DIM n(1 to 256) AS NETRESOURCE
    -  
    -     Entries = 256
    -     nSize   = SIZEOF(nr) * Entries
    -  
    -     ec = WNetOpenEnum(%RESOURCE_GLOBALNET, %RESOURCETYPE_DISK, 
    %NULL, nr, hEnum)
    -  
    -     ec = WNetEnumResource(hEnum, Entries, n(1), 
    nSize)
    -  
    -     FOR x = 1 TO Entries
    -         IF (n(x).dwUsage AND 
    %RESOURCEUSAGE_CONTAINER) THEN
    -             EnumAll 
    n(x)
    -         Else
    -             sStuff 
    = sStuff & LTRIM$(n(x).@lpRemoteName) & $CRLF
    -         END IF
    -     NEXT
    -  
    -     'We're done parsing through, so return the list
    -     Function = sStuff
    - END Function
    -  
    - Function PBMain
    -         MsgBox EnumAll BYVAL 
    %NULL
    - End Function
Working with Win32 APIs
    
        | C | PB/DLL | 
    
        | hWnd | BYVAL hWnd AS WORD | 
    
        | lpStr | lpStr AS ASCIIZ PTR | 
    
        | UINT Style | BYVAL Style AS WORD | 
    
        | CHAR | BYTE | 
    
        |   |   | 
    
        |   |   | 
    
        |   |   | 
    
        |   |   | 
    
        |   |   | 
VARPTR = address where a variable is stored in memory
STRPTR = pointer to a string
CODEPTR = address of a routine
AS STRING
AS STRING * 10
AS ASCIIZ * 10 'Same as above, but last 
character is ASCII 0, ie. up to 9 characters available
AS ... PTR 'Pointer, eg. AS STRING PTR = char *
DIM MYSTR AS STRING
DIM 
MYSTRPTR AS STRING PTR
MYSTRPTR = STRPTR(MYSTR)
@MYSTRPTR = "Test" 
'Same as MYSTR = "Test"
INCR MYSTRPTR 'Opposit is DECR MYSTRPTR
DIM MYSTRUCTPTR(1 to 10) AS MYSTRUCT PTR
FOR I = 1 TO 10
    @MYSTRUCTPTR(I).FIELD1 
= "Item " + STR$(I)
NEXT I
@MYARRAY[I].FIELD1 'For arrays 
created by other languages
Equates different from numeric constants? Equates have global scope
This is not allowed: VARPTR(lMyLong) = 
Instead: 
DIM lMyLong AS LONG
DIM pMyLongPtr AS LONG PTR
pMyLongPtr = VARPTR(lMyLong)
@pMyLongPtr = 1 'same as lMyLong = 1
ASCIIZ = address of a string
An ASCIIZ string is very different from a STRING. It's actually the same 
thing as a string in C: The variable contains an address (pointer) to the memory 
cell where the first character is located.
    - LOCAL sMyString AS ASCIIZ * 128
    - LOCAL pMyString as ASCIIZ PTR
    - pMyString = VARPTR(sMyString)
    -  
    - MoveMemory @pMyString, BYVAL se100.sv100_name, 1
    - 'This also works
    - MoveMemory BYVAL pMyString, BYVAL se100.sv100_name, 1
- 'This also works
    
    - MoveMemory BYVAL VARPTR(sMyString), BYVAL se100.sv100_name, 1
Round trip
    - LOCAL sStart AS ASCIIZ * 64
    - LOCAL sFinish AS ASCIIZ * 64
    - LOCAL MyWKSTA_INFO_100 AS WKSTA_INFO_100
    -  
    - sStart = "Hi there"
    - 'wki100_langroup is a LONG, ie. contains the address of a variable
    - MyWKSTA_INFO_100.wki100_langroup = VARPTR(sStart)
    - MoveMemory sFinish, BYVAL MyWKSTA_INFO_100.wki100_langroup, SIZEOF(sFinish)
    - MsgBox sFinish
Questions
How to set a pushbutton clickable through ENTER in DDT and PB 7.03?
This code generated by PBForms won't let me just hit ENTER as an alternative 
to a mouse click:
    - 'Originally tried without the long list of parameters as last parameter, 
    to no avail
    - CONTROL ADD BUTTON, hDlg, %IDC_CANCEL, "Cancel", 140, 35, 
    55, 20, _
    -         %WS_CHILD Or %WS_VISIBLE 
    Or %WS_TABSTOP Or %BS_TEXT Or _
    -         %BS_DEFPUSHBUTTON Or 
    %BS_PUSHBUTTON Or %BS_CENTER Or %BS_VCENTER, _
    -         %WS_EX_LEFT Or %WS_EX_LTRREADING 
       
    -  
    - Dialog  Send        hDlg, %DM_SETDEFID, 
    %IDC_CANCEL, 0
What's the difference between DIM and LOCAL?
Saw both in samples. DIM is required when declaring arrays (LOCAL triggers 
an error), eg. Dim rEntry(1) As RASENTRYNAME .
While using DDT, widgets coordinates are wrong
The problem is that Win32 APIs such as GetClientRect() or MoveWindow() use 
pixels (which are device-dependent) while DDT instructions "dialog units" 
(which are pretty much device-independent.) Either use only DDT instructions 
such as CONTROL SET SIZE and the like, or use DIALOG PIXELS ... UNITS and DIALOG 
UNITS ... TO PIXELS to convert coordinates between Win32 and DDT.
Here's a sample:
    - 'Instead of using GetClientRect and converting from pixels to DUs
    - DIALOG GET CLIENT CBHNDL TO R.nRight, R.nBottom
    - CONTROL ADD LISTBOX, CBHNDL, 500,, 0, 0, 85, R.nBottom, etc.
FYI, PBForms and 
Edwin Knoppert's (PBSoft) VisualDesigner 
show widget coordinates in dialog units.
When I start PBForms with a maximized dialog
... the PBForms application is covered with my dialog and can't be accessed.
Right-click on the PBForms icon in the taskbar, Minimize, then Restore.
Where can I find more infos on using the Win32 API?
A version is here, 
but there may be more recent versions on MS' site.
How to emulate OOP in PB?
    - Keep all the code pertaining to an "object" in its .BAS file 
    and #include it in the main .BAS file
- use STATIC local variable (their value is kept even after leaving the 
    routine)
- Prefix all routines (subs & functions) with the name of the object, 
    eg. Grid_Init(), Grid_Add(), etc.
- Good practise : Use #DIM ALL, add a prefix to global variables (eg. 
    Glb_X)
How does PB compare to other Basic languages like IBasic, PureBasic and RealBasic?
IBasic
PureBasic
RealBasic
Rapid-Q
I need a good graphics library
FastGraph
I need to write a COM server
(From Lance Edmonds, sept 2002) PowerBASIC does not natively support the 
creation of COM components (yet?!), but you might be able to do it using some 
of the tools available from http://www.jazzagesoft.com 
Are there any wrappers to make it easier to work with widgets?
Especially when using common controls which can be troublesome.
Sometimes, the PB/DLL compiler gets stuck
Remember, PB/DLL is a 16-bit compiler, and Windows' 16-bit engine can go 
haywire. In NT, open Task Manager, and kill WOWEXEC.EXE.
When declaring APIs, why is the ALIAS part required?
Is PbMain() the equivalent to C's main()...
... and, if yes, do I use it when my Windows application only uses a dialog 
box instead of a full-fledged window with? IOW, can I get rid of WinMain() and 
WinProc() ?
What is DDT? 
DDT: Dynamic Dialog Tools(tm)...  With DDT, you'll build complete 
GUI applications easier than ever.  Don't struggle with form designers. 
 Don't fight with resource scripts. Don't get lost in a sea of API calls! 
 With PB/DLL's DDT, a few simple Basic Statements will build a complete 
Graphical User Interface!
* DIALOG NEW creates an empty dialog box.  CONTROL ADD places buttons, 
icons, frames, bitmaps, combo boxes, labels, list boxes, text/edit boxes, even 
custom controls and more.  Add menus.  Then display it with DIALOG 
SHOW.  It really is just that easy!  Perhaps best of all, DDT is dynamic. 
 Change dialogs and controls "on-the-fly"!  Alter the size, 
position, and style at will.  With DDT, we put the Power in your hands. 
That's precisely where it belongs!
I get Error 480: Parameter mismatch, may need ByCopy when calling a SUB
You defined a SUB, but called it with parentheses:
    - Sub MyFunc (sMyString AS STRING)
    -     'nichts
    - End Sub
    -  
    - FUNCTION PBMAIN () AS LONG
    -     DIM sMyString AS STRING
    -  
    -     MyFunc (sMyString)
    -  
    - END FUNCTION
Use "MyFunc sMyString", or "Call MyFunc(sMyString)" instead.
Error 420 when calling an API function
Functions return a value. Add an lResult = .
How to maximize a dialog box at creation time?
Tried using 0,0 as width,height and ORing %WS_MAXIMIZE, to no avail.
How to add an icon in the title bar of a dialog bar?
Tried a bunch of switches in PBForm, but none worked.
Temp
Watch out : contrary to what it says in the online help of 7.02, search _is_ case sensitive by default (didn't find </title> because it was </TITLE> !:!!!!!!
1. REGEXPR statement 
"While it is possible for more than one match to be found in a particular target string, REGEXPR first selects one or more matches which start at the leftmost possible position, then returns the longest of those.  Use the \s special escape operator to force a match on the shortest match."
=> 
'Doesn't work
sMask = "\s[0-9]+"
2. "Tags/sub-patterns:
(parentheses) Parentheses are used to match a Tag, or sub-pattern, within the full search pattern, and remember the match.  The 
matched sub-pattern _can be retrieved later in the mask_ (or in a replace operation with REGREPL), with \01 through \99, based upon the left-to-right position of the opening parentheses."
=> "Retrieved later", ie. possible to extract subtag with REGEXPR instead of REGREPL?
        
3. "Parentheses may also be used to force precedence of evaluation with the alternation operator.  For example, "(Begin)|(End)File" would match either "BeginFile" or "EndFile" [...]".
=> Shouldn't the pattern be "(Begin|End)" instead?
4. "Note: Parentheses may not be used with ? + * as any match repetition could cause the tag value to be ambiguous.  To match repeated expressions, use parentheses followed by \01*."
=> Er... that is, sMask = "<content=""()\01*"">"?
5. REGREPL only works if the mask matches the entire original string:
'Doesn't work...
'sFull = "blablabla<content=" + $DQ + "mycontent" + $DQ + ">blablabla"
sFull = "<content=" + $DQ + "mycontent" + $DQ + ">"
sMask = "<content=\q(.+)\q>"
sReplaceMask = "\01"
REGREPL sMask IN sFull WITH sReplaceMask TO lPosvar, sNewFull
Resources