13 September 2021

Update 2.6 produces false positive viruscheck

The September 2021 update version 2.6 causes problems with some virus scanners like F-Secure, Secure Point, and some others. The file that causes the problem is GfaWin23.Ocx version 2.39, the GFA-BASIC runtime. After investigating the problem I’m sure the scanners report a false positive. My PC and the files on it do not contain a virus. The GfaWin23.Ocx file can be used without any problems, the GFA-BASIC 32 installer will not install a virus.

GfaWin23.Ocx version 2.39 is a patched 2.38, which is not trapped by any virus scanner according to VirusTotal. To my surprise, patching (changing) only one byte in the binary already causes 5 out of 66 online virus scanners to complain. This is the first time patching causes problems. I patched the GfaWin32.exe as well, and this file does not raise any problems.
I have contacted the vendors of the virus scanners to notify them of the false positive and hope they will add it to their exception list. Until then I will no longer release a patched GfaWin23.Ocx.

Use version 2.38 instead, reinstall
As a GFA-BASIC 32 developer you can ignore the virus scanner’s report, but when you release your software with the GfaWin23.Ocx runtime your customers face the same false positive virus reports. Therefor, I advise you to restore the previous version (2.38) of GfaWin23.Ocx, which does not give any problems.

The download page now contains installer version 2.6a, which contains version 2.38 of the runtime. Please download 2.6a and reinstall GFA-BASIC 32 version 2.6.

Apply the runtime bug fixes yourself 
Version 2.39 did fix a number of bugs, you now must do without. However, you can apply those fixes yourself while running your program. There are 4 bugs that need to be fixed:

  • Alert box displays wrong background color.
  • The mouse pointer over Ocx controls is not the control’s default mouse, but the Form’s mouse pointer. Especially, the TextBox Ocx shows a constantly flipping mouse pointer (arrow <–> I-beam).
  • The FileName property after using CommDlg.ShowFolders returns a string with a terminating null character.
  • The RichEdit Ocx property set CharFormat causes an exception when the string contains a 9.

You can apply all fixes or only the ones your program requires. The following code must be inserted at the beginning of your code (it requires the gfawinx.lg32 library):

'
' Patches for GfaWin23.Ocx 2.38 - 13-Sep-2021 (SH)
' Bug fixes to apply on the fly in a program.
' Note - Patches are only applied once: on first RUN only.
'
$Library "gfawinx"

If Round(DllVersion * 100) == 238       ' Only apply to 2.38

  ' Fix: Alert box background color
  If Peek($1800193C) != 6 Then PokeProcess1 $1800193C, $06

  ' Fix: Get FileName after CommDlg.ShowFolder
  If Peek($1801BCEF) != $4A Then PokeProcess $1801BCEF, Chr($4A, $89, $06, $89, $56, $10)

  ' Fix: MousePointer in Ocx Controls to default mouse
  If Peek($18013B5D) != $5C Then PokeProcess1 $18013B5D, $5C

  ' Fix: Rtf.CharFormat set fails if string contains a 9
  If Peek($1805BA4D) != $7F Then PokeProcess1 $1805BA4D, $7F

EndIf

When GfaWin23.Ocx keeps being rejected by virus scanners, I will add these and possible new bug patches to a single routine in gfawinx.lg32.

06 September 2021

Update 2.6–September 2021

Besides the obvious bug fixes, the update version 2.6 comes with some new features. In short, de editor can be switched to dark mode, the UI of the IDE has a new look, projects can be saved to a version history and libraries have been updated.

Bug Fixes
If possible, all known bugs have been fixed. This is true for the IDE, the CHM help, as well as for the runtime GfaWin23.ocx. The CHM Help is updated with the latest new commands and functions. Fixed is the bug where the CHM Help-viewer crashed when typing in the edit-field of the Index tab.
There were three known bugs in the runtime; the Alert box didn’t use the correct colors all the time, the FileName property after using ShowFolders didn’t provide the folder’s name correctly, and the mouse pointer didn’t display correctly over the TextBox Ocx. These issues have been fixed in the updated runtime version 2.39.

The libraries gfawinx.lg32 and direct2d.lg32
The extension library gfawinx contains some new functions and commands:

  • SplitPath parses a given path specification like C’s _splitpath().
  • DirExist checks for the existence of a path (UNC included) and does a much better job than GetAttr.
  • CharW returns a wide character string from a given address.
  • The string functions FindFirstOf and FindLastOf return the first or last position of any of the characters from a given set.
  • The LoadRichEdit50W function modifies the runtime to use msftedit.dll with the rich edit window class RichEdit50W (rather than the current RichEdit20A class from RichEd20.dll). See the updated CHM helpfile for more information.
  • The high-resolution timer object TimerQ has been re-developed, because Windows 10 suddenly stopped supporting the older implementation.

The direct2d library version 1.13 updates only a few functions;

  • D2GetRT can create a rendertarget for a memory DC. Syntax:
    Set memdcRT = D2GetRT(hdc, width, height)
  • D2Bitmap can load an image without ignoring the alpha channel.

More advanced Direct 2D features are under development.

UI changes to the IDE
The IDE has a smoother user-interface and new (a bit larger) images for toolbar, auto-complete, and list-boxes. In addition, the editor can be switched to a dark mode for those that appreciate light-on-dark editing.

Screenshot 2021-09-05 Dark Mode

The default dark mode syntax colors are chosen from a palette of colors that display well on a dark background, for more color suggestions see Colormind - Generate dashboard template colors. The dark background isn’t pure black, but it’s RGB color value is $121212. More information on the editor’s colors can be found in The IDE's Color Dialog blog.

Save to History
The toolbar displays an additional button (see picture above) for the Save to (Version) History feature. The command adds the current program to a history zipfile with a descriptive name that allow you to summarize the changes and new features of that program. By regularly storing the program in the version history file, you can easily return to a previous version of the program. By default, the history file is named progname + “History.zip” and it is stored in the program’s directory. However, the Extra tab in the Properties dialog box allows you to provide a dedicated directory (another (network) drive perhaps) to save the history files in one place. After selecting the command you are presented with this dialog box (Prompt):

Screenshot 2021-09-06 History Save

The dialog shows the filename and timestamp of the current file. The timestamp is the first element added to the descriptive name when the file is added to the zipfile. This makes sorting the zipfile easier. The second filename specifies the path and filename of the history file to which it is added. The edit line is used to provide a description of changes and new features of the file to store. The description starts with your Windows username, which could be necessary when multiple developers work on the same code. Because this information makes up the name of the file in the zipfile it can not include specific characters as \:/*?<>|”. To separate items use the ; or – characters. After saving you can open the zip file with the File Explorer.

$Compiler command
This new command adds the current compiler settings to the program, so you can use different compiler settings for your projects. Just type $Compiler on an empty line and the IDE inserts the compiler settings as a long integer value (in hex format) into the code line.

$Compiler 0x0008A734 ' Br:4 BrExe:1 Mul:3 Fp:3 ChkArr:1 Bswp:0 Num2Str:1 FpWait:0 FpCons:0 Trace:2

The line includes a comment specifying the compiler settings that are represented by the $Compiler value. The meaning of the abbreviations can be found in the helpfile.

Download: GFA-BASIC 32 for Windows: Download (gfabasic32.blogspot.com)

02 September 2021

The IDE’s Color Dialog

The IDE features a custom Color Dialog box to select the foreground and background colors for Ocx controls and Forms. The Color Dialog box is also used to select the editor’s syntax colors in the GB32’s Properties dialog box. Using the Color Dialog box you can select any color you want. However, the Color Dialog box presents a custom color palette for a quick selection. In addition the dialog box displays the system colors as they are defined on your version of Windows.

The Color Dialog box
The following picture displays the Color Dialog box after selecting the ForeColor property of a Form (frm1) in the form-editor. This dialog box will also be displayed after selecting the BackColor property.

Screenshot 2021-09-01 ColorDlg

The ForeColor property shows a color value of $80000008, which means that the color is set to the system color with index 8 (COLOR_WINDOWTEXT). The ForeColor property is used as the foreground color for drawing in the client area of the form.

The Color Dialog displays the 25 system colors in the lower two rows of the dialog box and it draws a focus rectangle around the color-cell that shows the system color for COLOR_WINDOWTEXT. The large cell at bottom-right of the dialog box displays the selected color. By clicking on the large box GB32 shows the common dialog’s color dialog box. The top 4 rows display a palette of often used RGB colors to allow a quick selection of a color. The fourth row also contains 8 additional colors otherwise not found in the 4 upper rows of the dialog’s color palette.

The BackColor property defines the color used to draw the (empty) contents of the client area. By default, GFA-BASIC 32 uses the system color COLOR_BTNFACE, the system color that is used to paint the shading of the face of command buttons. However, this system color is used for more UI-elements than just the shadow of the face of a button. By using the color-value $8000000f the window background gets the default color of a dialog box, so that the GB32 form will look consistently with system dialog boxes.

Using system colors
It is a big advantage that GFA-BASIC 32 let you use system colors for the user-interface elements of the form and Ocx-controls. The system colors may change when a custom Windows theme is installed or when Microsoft changes the system colors with a new release of Windows. Whenever the system colors change, the colors of the form change accordingly, providing a consistent look with the OS.

GFA-BASIC 32 distinguishes between a pure RGB color format and a system color by setting the high-bit of the 32-bit color value (Long). When a Long value is displayed in it’s hexadecimal format the high-bit setting is shown as $80000000. Before actually applying a color GB32 tests the high-bit of the color value and selects a system color when it is set:

If rgbcol %& $80000000 Then rgbcol = SysCol(rgbcol & $FF)

An application can specify a system color in two ways:

RGBColor $80000000 + COLOR_WINDOWFRAME
RGBColor SysCol(COLOR_WINDOWTEXT)

The first statement sets the foreground color to a system color value. The second statement converts the system color to an RGB-value beforehand. Both statements will eventually use the RGB color that belongs to the system’s color element COLOR_WINDOWTEXT. The RGB color value for any of the display elements is obtained using the GB32’s function SysCol or the API function GetSysColor(). Each aspect of the display has its own COLOR_* constant that is used as an index in these functions. These constants are built into GFA-BASIC 32. For more information see the SysCol function in the helpfile. It explains which constant represents what user-interface element. When you hoover over the system colors in the IDE Color Dialog box it shows a short description of the display element it represents.

The Editor colors
The Editor tab in the Properties dialog box (Extra | Properties) displays the same Color Dialog box to select the syntax colors.

Screenshot 2021-09-02 ColorDlg

This picture is taken after clicking the BackColor button. It shows the selected color for the background of the “Changed/Empty” syntax (ie. the background color of empty lines and the line that is being edited). By default, the background color for all syntax elements is set to system color COLOR_WINDOW ($80000005). The foreground colors of the syntax elements are custom RGB values. By keeping the background color to a system color, the GFA-BASIC 32’s editor will adapt to the new COLOR_WINDOW color if a theme is installed.

16 August 2021

Fill up the Form (Part 3)

This is part 3 of my blogs on GFA-BASIC 32 Forms. The other parts focused on creating and interacting with a Form:

In this blog I focus on drawing in the form’s client area.

Putting something on the Form
Basically, a form is used for two purposes. It is either used as a dialog box by populating it with OCX-controls, or it is used to draw graphics. Usually, these basic approaches aren’t combined, although you could draw some graphics in the dialog box form as a background for the controls. For instance, you could assign a Picture to draw in the form’s background or change the background color for the form. However, mostly these things are separated; a form is either used as an input form or as a graphics window.
When a GB32 form contains OCX controls it’s behavior changes and the form starts handling control-navigation keys (Tab and arrow-keys). This means that both the system and GB32 take control of the focus. The focus can only be set to a control, not to the client area of the form. The form’s method SetFocus does not work anymore. A form without OCX controls does not change to this behavior and allows you to set the focus to the form, making it possible to develop a text editor for instance. (BTW it is a good habit to add an OCX-Form child window to the client-area of the base-form. The base-form is the container of the OCX-controls and the OCX-Form control is used for drawing or editing.)

Drawing on a Form
An application should paint in the client area when Windows posts (or sometimes sends) a WM_PAINT message. GB32 converts the WM_PAINT message to an event which can be processed in the _Paint event sub. There are applications (like drawing programs) that need to draw interactively in the client area, for instance after a mouse-event. Even so, an application would store the new drawing coordinates and color and then updates its client area to redraw in the _Paint event sub again. This is achieved by invalidating the portion of the form that needs to be redrawn followed by a form-refresh.

frm.Invalidate x, y, w, h
frm.Refresh

The Refresh method calls the UpdateWindow() API and sends a WM_PAINT message to the form directly, bypassing the message queue. By following this procedure, the form is repainted instantly and the app conforms to the rules of painting in the client-area.

Should you use AutoRedraw?
To make things easy VB naively introduced the AutoRedraw feature. Because of VB-compatibility AutoRedraw was also built into GB32. The AutoRedraw feature allows an application to draw outside the _Paint event sub. All subsequent drawings are copied to an offscreen bitmap which is then drawn to the client-area after the form receives a WM_PAINT message. There are some drawbacks to this approach. The application does no longer follow the UI-rules for drawing and it will be difficult to maintain and expand the application. A second drawback is the drop in performance. Each GB32 graphics command is executed twice, once to draw on the screen and once to draw in the offscreen bitmap. In addition, the drawing is limited to the use of GFA-BASIC 32 commands, only GB32 commands draw in the offscreen bitmap. When you use a Windows API drawing function to draw you must draw both on the screen and on the bitmap. For this, the form provides the hDC2 property, this property returns the handle of the GDI device context for the offscreen bitmap. It is only valid after executing the AutoRedraw command. The use of an API function could look like this:

~TextOut(frm.hDC, 0, 0, "Hello, Windows!", 15)
If frm.hDC2
  ~TextOut(frm.hDC2, 0, 0, "Hello, Windows!", 15)
EndIf

If the form uses scaling, the scaling must be applied by hand. Each coordinate and size value must be converted to the current scaling of the form.
Another thing to note is the size of the offscreen bitmap. The bitmap is created when the AutoRedraw command is executed taking the current size of the client area. When a form is later enlarged (resized) the automatic redrawing of the offscreen bitmap will not entirely fill up the client area of the form. This is demonstrated in the following picture where the picture on the right is a resized version of the left form.

AutoRedraw

AutoRedraw is an option to use for a quick and dirty test program, not for a serious application.

Use Direct2D instead
The GB32 drawing commands are wrappers for the Windows GDI drawing functions. However, GDI isn’t fast and isn’t suited for dpi-aware applications. More and more GDI-drawing is replaced by Direct2D drawing. Your application can use Direct2D as well. The Include directory contains a GB32 library Direct2D.lg32 that provides access to the system’s COM-based direct2d libraries. The commands and functions provide a GB32 compatible approach to drawing, so an application can be ported to Direct2D easily. The library commands and functions start with the letters D2 followed by the GB32 name of the drawing command. To set the colors you would use D2RgbColor instead of GB32’s RgbColor command. Instead of Line you would use D2Line, etc. The usage of the library is explained in the latest (English) CHM helpfile (don’t use the very old German hlp file anymore, it is outdated and does not contain OCX-properties and all the new stuff.)  You’ll find the Direct2D information by going to the Contents-tab and opening the Creating an application chapter.

Conclusion
An application either uses the form for input using OCX-controls or draws in the client-area in a response to a _Paint event. It is recommended to not use AutoRedraw. Even better, use Direct2D for drawing.

04 July 2021

Interacting with Forms (Part 2)

This is the second post in a series about forms. In the first post we discussed the commands to create forms and their window styles. Now we’ll discuss how to interact with forms.

Owner and owned forms
The commands OpenW and Form support the creation of owned forms (windows). They provide the Owner clause to specify the ‘parent’ or owner of the new form. The following example creates an overlapped non-owned window (Win_0) and an overlapped owned window frm.

OpenW 0, 100, 100, 300, 300 : TitleW 0, "Owner"
Form Owner Win_0, frm = "Owned", 150, 170, 300, 200
Do : Sleep : Until Me Is Nothing

There are advantages and disadvantages of using a parent-owner relationship.

  • The owned form (frm) is always displayed on top of the non-owned window (Win_0). It is therefor mostly used for forms that behave as dialog boxes.
  • When one of the windows is activated the other is activated automatically.
  • When the parent window is closed the owned window is closed as well. Not the other way around. When you close form frm the Win_0 form is not closed and Me does not become Nothing. The message loop is not ended. When you close Win_0 the form frm is closed as well and Me will become Nothing.
  • The owned window is not disabled (for input) if the parent is disabled. When a program displays a modal dialog box (a form with Ocx controls that disables all other windows while on screen) all other forms have to be disabled explicitly.

Suppose a program wants to display an About box in a modal dialog box. In GB32 a modal dialog box (form) is simulated by showing an owned form and then disabling input for the other forms using the Disable property. Then, after closing the modal dialog box form the other forms are enabled again using the Enable property. Like this:

OpenW 0, 100, 100, 300, 300 : TitleW 0, "Owner"
Ocx Command cmd = "About", 10, 10, 100, 24
Form Owner Win_0, frm = "Owned", 150, 170, 300, 200
Do
  Sleep
Until Me Is Nothing
Sub cmd_Click
  ' Display frm1 (owned by Win_0) created using the Form-Editor
  LoadForm frm1, Win_0.Left + PixelsToTwipX(50), Win_0.Top + PixelsToTwipY(50)
  Win_0.Disable         ' no input allowed
  frm.Disable           ' no input allowed

Sub frm1_Destroy        ' frm1 is being destroyed
  frm.Enable            ' enable input
  Win_0.Enable          ' enable input
  Win_0.Activate        ' activate

In the form-editor the Owned property is set to True. The form only has one Ocx control; a label with an enlarged font. The LoadForm command uses the current active form as the owner for the About box. The result is this, only the About box is active and enabled:

Screenshot 2021-06-28 121116

The message loop
To interact with forms the application needs a message loop. The most common message loop uses Sleep and ends with Until Me Is Nothing (Do : Sleep : Until Me is Nothing). When all windows are closed Me – the current active form - becomes Nothing and the program will end.
Sleep waits for a queued message; a message that is posted to the window’s message queue. The OS posts only a limited amount of messages, most messages are sent to the window-procedure directly. In general, the posted messages include the input messages for keyboard and mouse, the WM_PAINT and WM_TIMER message.

After Sleep detects a new message it dispatches (relays) the message to the window’s window-procedure. So, in the end all messages (posted or sent) end up in the window procedure of the window. The window procedure is responsible for executing the event subs.

Sleep is not the only command that reads the message queue, others are the (also new) DoEvents, and the older 16-bit GFA-BASIC compatible commands GetEvent, and PeekEvent. Sleep is the preferred command to wait for and handle a message. When the Sleep command executes it first processes all pending (waiting) messages and then puts the program to sleep using WaitMessage(). When a new message arrives Sleep processes all new message(s) before it returns back to the program. Schematically, from left to right Sleep executes:

If any: Handle All Messages + Sleep(0) WaitMessage Handle All
Messages + Sleep(0)
=> return to program (loop)

As part of Handle All Events the Sleep command invokes the Windows API Sleep(0), giving up the remainder of the time slice that the scheduler assigned to the thread (process).

The other command that waits for a message is GetEvent, but this works a bit different. Upon entering the GetEvent handles one event if there is a message present in the queue. Without any pending messages GetEvent invokes WaitMessage() and halts the program. After obtaining a message it continues by processing exactly one event. If more messages are present GetEvent will process them the next time it is executed. Schematically, there are two situations:

Message In queue? Handle One Message => return to program (loop)  
No pending messages? WaitMessage Handle One Message => ret to prog

Because GetEvent handles only one message a time it is able to place GFA-BASIC 16 compatible message-info in the MENU() -array. An application can respond to the values of the MENU() array inside the GetEvent-message loop. The GetEvent command does not invoke Sleep(0). With multiple messages pending the GetEvent loop will run until all messages are handled without giving up time, which is not a real problem. After all, the Sleep command does not call Sleep(0) before it handled all pending messages.

PeekEvent is like GetEvent: it fills the MENU() array, but it doesn’t wait for a message, it returns immediately to the program.

Handle One Message (if any) => return to program (loop)

Because PeekEvent is usually placed in a (message) loop, it is likely that the CPU will be overloaded and will be running at 100%. You will need some mechanism to give up some processor time, for instance by using the Sleep(ms) API or the GB32 command Sleep ms inside the message loop.

DoEvents is a bit like PeekEvent: it doesn’t wait for a message. However, rather than processing only one message at a time, DoEvents handles all pending messages in a row before it returns to the program.

Handle All Messages + Sleep(0) => return to program

This does not mean that DoEvents - when used in a loop - will let the processor run at 100%. Before returning to the program DoEvents invokes Sleep(0) to give up CPU time to other processes. DoEvents is often used in a time-consuming loop to keep the GUI responsive.

All these commands Sleep, GetEvent, PeekEvent, and DoEvents handle the keyboard navigation between Ocx controls on the form. However before the keyboard navigation is applied GB32 invokes the Screen_Preview event sub if it is present. This event sub allows the program to intercept queued keyboard messages (WM_KEYFIRST – WM_KEYLAST) to respond to a key and/or cancel the message if it wants to.

Sub Screen_KeyPreview(hWnd%, uMsg%, wParam%, lParam%, Cancel?)

Set the Cancel? parameter to True when a keyboard message is handled and you don’t want to pass it on to the Ocx-control keyboard-navigator or to the window-procedure.

Event Subs
All message retrieval commands, including GetEvent and PeekEvent, execute event subs. (To be precise, the window procedure invokes the event subs.) Using event subs is the preferred way to respond to a message taking away the need to distinguish between posted and sent messages as in previous versions of GFA-BASIC. To handle a form-message simply add an event sub to the program. Note that when a form is being created it executes event subs while the form’s object variable isn’t initialized yet. In particular, the _Paint and _Resize events are invoked without having access to the form’s variable. This behavior is discussed in the previous blog post: Forms, the base of an app.

The _MessageProc sub event
A powerful event sub is the _MessageProc sub event. If defined, it is called for each and every message that is relayed to the window-procedure, including the posted messages.

' Called for all messages
Sub Win_0_MessageProc(hWnd%,Mess%,wParam%,lParam%,retval%,ValidRet?)

You need to know what to do if you handle a message in the _MessageProc event sub. This knowledge is acquired through the Windows SDK (now available on line). For the sent messages the program needs to specify a return value in the by reference variable retval% and set the ValidRet? variable to True. This is necessary; if the program sets ValidRet? the message is considered handled and any event subs that would otherwise be invoked will not be executed. When ValidRet? is set the form’s window procedure will return to the caller (Windows OS) immediately. To handle a posted message – one that doesn’t require a return value - you needn’t set retval%, but to prevent further handling you should set ValidRet? though.

The _Message event sub is for posted messages
The _Message event sub is intended to process queued (posted) messages only. However, the _Message event sub is called for all messages (like the _MessageProc event sub) except for WM_PAINT and WM_ERASEBKGND (!?). Because of its syntax – it misses retval% and ValidRet? - it can only be used to process posted messages, the messages that don’t require a return value:

' Process posted messages only
Sub Win_0_Message(hWnd%, Mess%, wParam%, lParam%)

Even when a program handles a message in the _Message event sub the event sub for that message is invoked as well. For instance, if WM_LBUTTONDOWN is handled in the _Message event sub and the _Click event sub is defined as well, the _Click event is also executed. If both _Message and _MessageProc subs are used both subs are invoked, first the _Message event sub followed by the _MessageProc. If a program responds to a (posted) message in the _Message event sub, it shouldn’t process it in _MessageProc again, or in some other event sub.
The practical use of the _Message event sub is limited, it is intended to port GB16 code that uses the MENU()-array or _hWnd, _Mess, _wParam, _lParam to GFA-BASIC 32. Because the _Message event sub is called from the window-procedure a program can handle all posted messages when the message loop uses Sleep or DoEvents.

30 June 2021

Forms, the base of an app (Part 1)

A Windows program usually has one or more windows to interact with the user. In GB32 the windows are called forms. A Form is actually a normal window created with the Windows API function CreateWindowEx(). However, a GB32 form adds additional features to the window. For instance, a form supports keyboard tabbing from one Ocx control to the other, it’s settings are accessible through properties and methods, and a form is able to execute event subs. A Form is a normal window with a COM-wrapper. A Form is accessible through a COM object variable; you either provide your own name to a form like frmMain, or you use a predefined form variable like Win_x or Dlg_x. The name of the form-variable depends on the command used to create the form.

Creating a Form with OpenW    
There are several ways to create a form. You might want to use one of the ‘good old’ GFA-BASIC commands OpenW, ParentW, and ChildW. Or you could use the new Form statement to create a Form in code. To load a form created in the Form Editor use the LoadForm statement. In addition, you could use the Dialog command to convert dialogs from older GB versions to GB32 forms.

Note The ParentW and ChildW commands allow you to create a multiple-document-interface (MDI) program, but this type of program isn’t very popular anymore. These days, multiple documents programs are single windows that display a TabStrip Ocx control with multiple tabs that have an Ocx Form child window attached to each tab. I won’t discuss these window types in this post.

As in previous versions of GFA-BASIC the OpenW takes a window number from 0 to 31 to identify the window. After creating the window GB32 wraps the window in a Form object and assigns it to one of the predefined form-variables Win_0 to Win_31. This does not mean that you are limited to 32 windows. The OpenW command accept numbers larger than 31 but these forms are accessed using the form-array syntax Form(n), where n is the number used in the window creation command. For example:

OpenW Center 50, 0, 0, 300, 300
Form(50).AutoRedraw = 1

Note that the forms created with a number from 0 to 31 can be accessed using the Form() array syntax as well. So, there are two ways to access the Form object for a window with number 1: Win_1 and Form(1). This is practical in situations where a Form must be accessed through an index rather than through a fixed Form variable like Win_x.

One of the advantages of using OpenW is the easy way of defining the window-styles. To create an overlapped unowned window OpenW provides the following syntax (without the MDI and Owned options):

  • OpenW [options] [#]n [, x, y, w, h][, attr]

If you omit the attr argument GB32 creates a default window – visible, sizeable, and with a caption and a system menu – using the following window styles for the dwStyle parameter of CreateWindowEx: WS_OVERLAPPED, WS_CAPTION, WS_CLIPSIBLINGS,  WS_CLIPCHILDREN, WS_THICKFRAME, WS_SYSMENU, WS_MINIMIZEBOX, WS_MAXIMIZEBOX, and WS_VISIBLE. Some of these default styles can be disabled by using the options argument as we’ll see.

The attr argument is an integer whose bits determine the window styles. The next table shows the possible attributes. Note that the meaning of the bits are inherited from the very first GFA-BASIC versions, and may look a bit strange or appear missing (bit 8):

Bit    Value Description Property
0, 1 1, 2 Set one of these bits for a vertical scrollbar, sets WS_VSCROLL ScrollBars = basVert
2, 3 4, 8 Set one of these bits for a horizontal scrollbar, sets WS_HSCROLL ScrollBars = basHorz
4 16 Displays a caption bar, sets the WS_CAPTION style Caption = “Title”
5 32 Adds a system-menu, sets the WS_SYSMENU style ControlBox = True
6 64 Displays the minimize button in the title bar, sets the WS_MINIMIZEBOX style. With WS_SYMENU the button is dimmed only. MinButton = True
7 128 Displays the maximize button in the title bar, sets the WS_MAXIMIZEBOX style. With WS_SYMENU the button is dimmed only. MaxButton = True
9 512 Sets the WS_THICKFRAME to make the window sizeable Sizeable = True

When attr = –1 all bits are set and the form is created with the following styles:  WS_OVERLAPPED, WS_VSCROLL, WS_HSCROLL, WS_CAPTION, WS_CLIPSIBLINGS,  WS_CLIPCHILDREN, WS_THICKFRAME, WS_SYSMENU, WS_MINIMIZEBOX, WS_MAXIMIZEBOX, and WS_VISIBLE. In short, the same styles as used when the attr argument is omitted, except it now includes the scrollbar styles. These styles can be changed at runtime using the Form’s properties Visible (or Hide/Show), ScrollBars, Caption, ControlBox, MinButton, MaxButton, and Sizeable.

With the introduction of GB32 the syntax of OpenW is changed so that even more window-styles (options) may be specified. For instance, in the example above the option Center is used to position the form in the center of the (main) screen. GB32 supports the following options that maybe combined:

Option Description Property/Method
Center Centers the form on the main(!) display. Center
Hidden Does not show the form, removes the WS_VISIBLE style from the dwStyle parameter. Hide or Visible = False
Full Creates a maximized window on the main(!) display, excludes Hidden. Sets the WS_MAXIMIZE bit of the dwStyle parameter of the CreateWindowEx() API. FullW
Fixed Removes the WS_THICKFRAME dwStyle  
Client3D Sets the WS_EX_CLIENTEDGE bit of the dwExStyle parameter of the CreateWindowEx() API Appearance, set bit 1
Tool Sets the WS_EX_TOOLWINDOW bit of the dwExStyle parameter of the CreateWindowEx() API  
Top Sets the WS_EX_TOPMOST bit of the dwExStyle parameter of the CreateWindowEx() API OnTop = True
Help Sets the WS_EX_CONTEXTHELP bit of the dwExStyle parameter of the CreateWindowEx() API HelpButton = True
Palette Sets the WS_EX_PALETTEWINDOW bit of the dwExStyle parameter of the CreateWindowEx() API  
NoCaption, NoTitle Excludes a title bar that otherwise would be created. ControlBox = False, Caption = “”
Default Sets the x, y coordinates to CW_USEDEFAULT (-127)  

The different options may also be set at runtime after the form has been created using properties or methods, see table.

There is another command to create a form: the Dialog command, but it is primarily used to convert dialog boxes from older GB sources to GB32 forms. Syntax:

  • Dialog [#]n, x, y, w, h, tit$ [,flag [,height, font$] ]

The Dialog command is followed by a sequence of Ocx control definitions that make up the user-interface of the dialog box. The dialog definition is ended with an EndDialog statement. The meaning of the arguments of Dialog depend on the settings specified with the DlgBase command. There are only 32 dialog definitions allowed (0 <= n <= 31) and the predefined form variables are Dlg_0Dlg_31. When a form is created using Dialog, the IsDialog property returns True.

The Form command
The Form command is an alternative to OpenW and allows you to assign any name to the form.

  • Form [options] fname [= title$, x, y,  w, h ]

There is a disadvantage though. The Form command does not allow you to specify the window attributes (the window styles), you can only set the options as summarized in the previous table. The form gets the same default window styles as OpenW: WS_OVERLAPPED, WS_CAPTION, WS_CLIPSIBLINGS,  WS_CLIPCHILDREN, WS_THICKFRAME, WS_SYSMENU, WS_MINIMIZEBOX, WS_MAXIMIZEBOX, and WS_VISIBLE. Using the options argument some of these can be modified, see table.

The Form command does not support the scrollbar window style, because it does not support the attr argument. To add scrollbars use the ScrollBars property after the form has been created:

Debug.Show
Form Center frm = "Title" , 4, 8 , 200 , 300
frm.ScrollBars = basBoth
Do
  Sleep
Until Me Is Nothing
Sub frm_ReSize
  Debug "size event"
Sub frm_Paint
  Debug "paint event"

Some of the form properties that change a window (ex)Style can not be applied dynamically to an existing window. In this case the form has to be destroyed and then recreated with the new window-styles. So, the window is actually created twice. As a consequence the ReSize and Paint event subs are invoked twice before the main message loop is entered, see below “Hidden DoEvents while creating a form”.

The LoadForm command
This command is used to load a form defined using GFA-BASIC 32’s Form Editor.

  • LoadForm frm [options] [, x, y]    (x and y in Twips)

The options specify the same settings as with the other commands. In addition, you can specify the position of the form in pixel coordinates. All the window styles (attributes and options) can be selected using the Properties listbox in the sidebar. Still, you can override some of these properties using the options argument. For instance, using the option Hidden a form can be loaded without making it visible. 
This command initializes its form object variable during execution; the variable is accessible in the events that are executed while LoadForm loads the form.
This is the only command that initiates a Load event that can be used to initialize the Ocx controls of the form or to do any other initialization.

LoadForm frm1
Do
  Sleep
Until Me Is Nothing
Sub frm1_Load
  ' Do initialization here
EndSub

Hidden DoEvents while creating a form
The commands that create a form invoke a hidden DoEvents message loop to process all pending (posted) messages that are a result of the creation of the window. In particular, if the program contains Paint and ReSize event subs, the subs may be called when the hidden DoEvents is executed. In this case, the form’s object variable is not yet initialized (except for the LoadForm command, see above). For instance, trying to access the Win_1 variable in a Win_1_Paint event sub will cause a runtime error, because the first time the Paint event sub is called the variable is still Nothing. The form’s object variable Win_1 is not initialized before OpenW has finished. Fortunately, The Me form variable is valid as soon as the actual window has been created and can be used in the event subs, even when called from the hidden DoEvents.

What is Me?
Me is a global form-variable that references the current ‘active’ form, or the form that is currently being processed. In the event subs Me references the form for which the event sub is invoked. In the message loop Me references the latest form for which a message is received. With multiple windows Me might reference an inactive window, because Me is set to the window that receives a message and it can not be predicted in what order messages are sent or posted. However, Me always references a form, whether it is the top (active) window or some other window. So, do not expect Me to reference the active form in the message loop. When all windows are closed Me becomes Nothing and the main message loop is ended.

Who has the focus?
When a form contains Ocx controls that can have the focus (Command, TextBox, etc) the focus should be set to the first Ocx control in the tabstop (or creation)-order before entering the message loop. Windows will automatically set the focus to a control when the window is being used (resized, (de-)activated, minimized, etc). GB32 does not set the focus to the first control after creating the form,  but to conform to the behavior of Windows, set the focus explicitly at the start of the application.

Conclusion
OpenW and Form are much alike, although OpenW gives more control over the window styles through the attributes argument. During the creation of a form some event subs will be invoked without the form-variable being initialized, but Me is valid. Only LoadForm invokes the Load event sub. The Dialog command is used for porting older sources.

25 April 2021

The gfawinx library

It’s been a while since GFA Software Technologies GmbH released GFA-BASIC 32. At that time (2001) it could not be foreseen how Windows - and PC-use in general - would evolve. Consequently, GB32 misses runtime functions to coop with the latest Windows developments and the introduction of better hardware. To fill that gap gfawinx.lg32 comes with a set of commands and functions to access and use the newer features. The name gfawinx stands for “GFA-BASIC 32 expansion” library, gfawinx contains several categories. When you load the gfawinx.g32 code file in the editor the Procs sidebar shows the current groups:

Screenshot 2021-04-23 112115

Download latest version
Previous gfawinx versions – until and including the version of 16 Feb 2021 - contain an error that may lead to problems when loading the library into a program. If you are planning to use gfawinx make sure you have a version later than 16 Feb 2021. Please check the date of the file in the Include directory.

Download a newer version if necessary here and unzip the file to the Include directory.

What’s the problem? One of the library functions uses a DLL function declared using the Declare command. However, when a lg32 library uses a declared function it must also export that DLL function using an $Export Decl command.The program that is importing the library must be able to add that declared function to the program’s table of DLL functions. Gfawinx used a declared function, but did not export it. The current version of gfawinx doesn’t use Declare anymore, but uses GetProcAddress() to obtain the function’s address and calls it using StdCall()(). Something to remember when you are developing your own libraries.

The String functions
The Wide and Ansi functions are mainly used with Windows API functions that take a UNICODE string. An example of both functions was discussed in Task Dialog as a replacement for Alert, which demonstrates the use of a Windows API taking UNICODE strings as parameters. This blog post also contained an example of StrToArr, which is used to convert a string, containing sub-strings separated with any character, to an array. StrToArr is an addition or replacement for the Array ar$() = $ command.
The Replace string function is a VB(A) compatible function, much easier to use then the Split command of GB32. The Replace function is optimized for speed and takes the string arguments by reference if the arguments are string variables. Only in case a string literal is passed GB32 will pass the string parameter by value (making a copy first and then pass that copy). This is achieved by making Replace a FunctionVar rather than a Function.

FunctionVar and Sub anonymous String and Variant parameters
When a parameter of a FunctionVar or Sub does not explicitly specify ByVal or ByRef the parameter is anonymous. In this case the compiler decides how to pass the parameter to the FunnctionVar or Sub. If possible, the compiler generates code to pass the String or Variant by reference, otherwise the parameter is passed by value. To be able to pass by reference the caller must pass a variable; a literal is always copied to a temporary locally variable which is then passed by reference. Note that a ByVal parameter always gets a copy of the data that is passed, which requires the compiler to insert additional code to create a copy first. Passing by reference is almost always faster. Using anonymous parameters can speed up the execution of a procedure or function considerably.

An example that shows the difference:

$Library "gfawinx"
Dim src$, find$, new$, result$
result$ = Replace(src$, find$, new$)     ' variables, fast
result$ = Replace("GFA-BASIC", "A", "x") ' literals, slower

ErrStr is a convenient function to obtain the relevant Err-properties as a string. Usually, ErrStr will be used in a Try/Catch handler to present information about the trapped error to the user. The problem is to decide which Err-properties contain the relevant error information. ErrStr examines the properties and only returns the properties that contain the actual error information. ErrStr takes one string parameter that is added to the error information. This parameter is perfectly suited for the current proc-name, which is easily inserted using the keyboard short cut App+P. Here App+P inserts “main” into text:

$Library "gfawinx"Screenshot 2021-04-23 131437
Try
  Error 3
Catch
  MsgBox ErrStr("main")
EndCatch

The program produces the following message box:

The $ = HexDump(addr%, size%) string function returns a memory dump starting from the specified address addr% and with a length of size%. The result of this debugging aid can easily be displayed in the Debug Output window or a MsgBox.

The Windows functions
Here we find the Windows API and form-windows related commands and functions. Let’s first examine the WinVer function which returns the Windows OS version. In the latest version of gfawinx it returns the value obtained using the RtlGetVersion() API in a hexadecimal format $XXYY, where XX represents the major version and YY the minor version:

$Library "gfawinx"
Print Hex(WinVer())  // A00 (Win 10)

There is a lot of information to be found on how to obtain the Windows version. The method used by WinVer() does not depend on the manifest file as other methods do and should be safe to use with future releases of Windows. (Note - only the latest version of gfawinx - later than 16 Feb 2021- contains this method.)

GetWorkArea is a useful command when using multiple monitors where the Screen.Work* properties don’t work. GetWorkArea returns the work area of the monitor containing the largest area of a given window.
GetProcAddr returns the address of a function from a specified DLL. It works a little different than the GetProcAddress() API, because it first checks if the required DLL is already loaded.
The GetClientSize command returns the size of the client area, useful if _X and _Y are not available (in API windows).
ModifyStyle and ModifyExStyle make it easy to change the windows Style or ExStyle on the fly. They allow to both set and remove a windows-style simultaneously.
The Assoc() function returns system information associated with files, extensions or Prog IDs. For instance to obtain the full path of the default application that is associated with the .txt extension use:

$Library "gfawinx"
Print Assoc(".txt") // full path of default app

SaveFormPos and LoadFormPos save and load the form’s position respectively. For single monitor apps these are simple functions to store and retrieve the form’s current position in and from the registry. They are especially useful when used with multiple displays and DPI-aware applications. For detailed information see the help file.
MemStat is is a helper function to obtain memory statistics. If the argument is omitted (or is 0) it returns the memory used by the application. It is possible to trap a possible memory leak by using MemStat() when the program starts and compare the returned values at each start of the program.

The DPI-related functions
When you want to write DPI-aware applications these helper functions will be very useful. For more information on writing DPI-aware programs please see the English CHM helpfile: in the Contents section see Creating an application. Programming DPI aware programs is beyond the scope of this blog post. The gfawinx library provides the following functions.

WinDpi returns the DPI of the display for a window or form.
DpiAwareness set the DPI mode for the program.
ScaleToDpi(Sng) helps to scale coordinates.
ScaleXYWHToDpi scales ScaleLeft, ScaleTop, ScaleWidth, and ScaleHeight to a form's DPI.

The TimerQ function
The TimerQ function creates a high-resolution timer wrapped in a (minimal) COM object. This is the gfawinx implementation of the discussion in the blog High Resolution Timer . By using a COM object for a resource it can be guaranteed that the resource is freed when the program ends – either normally or through a runtime error.

Conclusion
The gfawinx library provides functions and commands that are not included in the GB32 runtime. The commands and functions are displayed in the GB32 syntax color for statements and GB32 functions. The procedures that implement the additions are optimized for speed, when possible the procedures are Naked. It is recommended that you – sometime - take a look at the code, it contains comments on implementation details you might find interesting. The library will grow over time when more ‘missing’ features will be added.

23 March 2021

Update version 2.59

Note:

After releasing update version 2.59 on 18 March 2021 a new version was released at 22 March 2021. To check if you have installed the latest release select the About-box in the menu-item Help | About and look at the version of GfaWin32.gll; it should be version 2.590.

Introduction
A new update is necessary because it fixes two major bugs in the runtime. After the last update (version 2.58 in December 2020) I found the time to investigate and fix a long-standing bug where the Ocx Form child control crashes the IDE. While fixing the bug I stumbled upon another runtime bug: the Item property of the Controls collection did not work and could crash the IDE as well. Besides these fixes, version 2.59 improves auto-complete and comes with an update of the Direct2D library.
The rest of the blog explains the runtime bugs and introduces Direct2D version 1.1.

About the fixed runtime bugs
The Ocx Form-control-bug presented itself as a crash of the IDE after one or two times running the program. For a long time, it was unclear what caused the bug. If a form contained an Ocx Form child control the program executed fine unless the Ocx Form child control was assigned to Me, or when the program iterated over the form’s controls using For Each ctrl in Form.Controls, or when the Ocx Form was used in a With statement. There were other symptoms of the bug, for instance setting the Ocx Form’ s scrollbar properties might cause a floating-point stack error. In the end it turned out that the bug was caused by the runtime when it wrongly released the Ocx Form (COM) object prematurely.

After a COM object is assigned to another object (variable) the reference count of the COM object is increased. When a COM object is set to Nothing the reference count is decreased. When the reference count is zero all resources of the COM object are freed and the variable referencing the object becomes Nothing. However, when the Ocx Form child control is referenced twice or more and is then released, the object’s underlying resources are completely freed as if the the reference count has become zero. The next time the Ocx Form object is released (for instance automatically at the end of the program) it’s resources are already freed and memory pointers have become invalid resulting in access violation errors in the runtime. GB32 tried to recover from these corrupted pointers but could not prevent an IDE crash.

As you can see, the symptoms of the Ocx Form bug were an indication of this wrong behavior. When assigning the Ocx Form frx to Me, both frx and Me reference the Ocx Form and more than one reference to the Ocx Form would eventually crash the IDE. Before knowing the cause of the bug I advised not to assign an Ocx Form to Me, now it can be done without any problem.

OpenW 1
Ocx Form frx = "", 10, 10, _X - 20, _Y - 20
Set Me = frx    ' OK now

The same problem occurred with With frx. With creates a hidden temporary object variable to which frx is assigned. So, after executing With, two object variables reference the Ocx Form frx. EndWith will release the hidden variable by setting the hidden variable to Nothing, which – with the bug present - would destroy the frx object completely, but not anymore.

With frx        ' assigns frx to a hidden local object variable
  ' use properties and methods
EndWith         ' releases the hidden variable

The For Each loop mentioned before shows the same behavior:

Local Control ctrl
For Each ctrl In Win_1.Controls
  ' use Control properties/methods
Next

Each iteration assigns a new Ocx child control object to the ctrl variable. In the process the object that is currently assigned to ctrl is released (set to Nothing). When ctrl is assigned the Ocx Form child control the next iteration will release ctrl and thus release the Ocx Form object. Due to the bug all it’s resources are freed. As said, this is now fixed in runtime (GfaWin23.ocx) version 2.38.

While investigating the Ocx Form bug I stumbled upon two other bugs, which are fixed also. First, autocomplete didn’t show the properties for Win_1.Controls and secondly, the Item property of the Controls collection failed.

Local Control ctrl
Set ctrl = Win_1.Controls.Item(1) ' OK now 

The Direct2D library version 1.1
The new Direct2D library contains several new commands and functions. The purpose of this release was to fill the gaps that were left by the previous version. Therefor, the following commands are extended to provide the same functionality as the GFA-BASIC 32 built-in drawing commands. The D2 circle and ellipse commands now support arc- and pie drawing, which required a new syntax:

D2(P)Circle x!, y!, r! [, w1%][, w2%][, Figure%] [,D2Brush] 
D2(P)Ellipse x!, y!, rx!, ry! [, w1%][, w2%][, Figure%] [,D2Brush]

The library supports the polygon commands in the same manner as the GB32 commands D2PolyLine and D2PolyFill draw a polygon directly into the render target. There is also a D2DefPoly() function to create and cache a polygon to be drawn later on when the render target is updated. The implementation of the polygon commands required the Direct2D geometries. There is whole set of commands and functions to create and draw into a geometry. Check out the help file and the examples in Samples\Direct2D directory. Many D2 help file topics contain a link to the Microsoft online SDK for further information on Direct2D issues. The examples directory also contains two SDK examples converted to GB32; D2ClockSDK.g32 and D2PathGeometry.g32.

In addition, it is important to know that the D2GetRT() syntax has changed. It won’t be noticeable if you use the default settings: Set RT = D2GetRT(), which creates a DC-render target. However, when you do use arguments, for instance to create a window render target, you need to update your code. See the help file for more information on the syntax.

This isn’t the latest Direct2D update, I will continue to work on the Direct2D library. Regularly check this site for more information.

23 February 2021

The Include directory

After installing GFA-BASIC 32 you’ll find four directories in the installation path: Bin, Doc, Include, and Samples.

image

The \Bin directory contains the GB32 binaries, the \Doc contains the original (German) doc-files that came with GFA-BASIC 32 back in 2001 (now obsolete because everything can be found in the English CHM helpfile), the \Include contains the Windows API include files, and the \Samples directory the samples g32 files, including the new Direct2D example programs.This time we’ll focus on the \Include directory only.

What is the \Include directory for?
The purpose of the \Include directory is to collect all Windows API definitions and declarations in one directory. Because of the huge amount of Windows APIs the definitions and declarations are split into multiple smaller include library files. These GB32 Windows API include-files come both with the source code and the compiled library (lg32) file. The organization of the GB32 include files follows the way the Windows SDK presents the C/C++ include header files. For instance, the C/C++ header file winuser.h has an equivalent GB32 include file winuser.inc.lg32. All include files follow this naming convention: name.inc.lg32.
You import a GB32 include library file using the $Library command, for instance:

$Library "winuser.inc"  ' .lg32 may be omitted

By default, the line doesn’t need to specify the full path, because the location of the \Include directory is pre-selected in the Properties | Extra tab dialog box.

image

You can easily add your own paths that contain your own library files. The entire string with the specified paths is stored in “lg32paths”registry key. To add a path first type a semicolon ; after the existing path and then specify the full path after the semicolon. Once a path is part of Library paths you do no longer need to type the full path in the $Library statement. Note that libraries are searched for (1) in the current program's directory, (2) in My Documents\lg32, and (3) in the paths stored in the registry key "lg32paths".

Note The \Include directory also contains the libraries gfawinx.lg32, direct2d.lg32 and variants.lg32. These are not include files and don’t have the .inc clause in their names. These are libraries that are part of the GFA-BASIC 32 updates and need an easily accessible path.

Why multiple include files?
Each include file contains only a part of the Windows API, that way you don’t need to include a single large file with many APIs just to have a few declarations. The GB32 include files only provide the APIs that are not built-in by GFA-BASIC32. (Note that the Win32API.g32 that originally came with GB is incomplete and full of errors.) Due to the amount of new APIs that come with each new Windows version, the include files in the \Include files aren’t complete either. They do however provide more declarations and definitions than the original Win32API.g32. Some of the include files are updated with the latest APIs (most specifically APIs supported by Win7 and Windows 10) like, for instance, winuser.inc.lg32. However, many haven’t been updated for a while. They tend to get an update on a ad hoc basis; when I need new APIs I add them to the include files. It is a boring job and, because of the translation from C/C++, errors are easily made. If you need an API that isn’t in one of the include library files yet, let me know (gfabasic32@gmail.com).

Contents of an include library
By default, the GB32 Windows include files only provide function declarations (Declare statements), constant definitions, and user-defined type definitions. They don’t contain executable code and besides the Declare’s they do not contribute to the size of the program. You should know that Declare-ed DLL functions are collected in a DLL-import table that do become part of the program (executable). By spreading the Declare’s over multiple include files the DLL-import table can remain small.
There is one “include” file that does contain executable code: winmacros.lib.lg32 (note the lacking .inc clause in the name). This library contains functions for often used Windows macros that are used as functions in C/C++, but are defined as macros in the C/C++ header files. Some of these function macros are collected into winmacros.lib.lg32. Among others, the library provides (Naked) functions for GET_X_PARAM, GET_Y_PARAM, MakeLParam, MakeIntResource, etc. Please take a look at the source code in winmacros.lib.g32 for an overview of the supported functions/macros.

How to locate a specific API
How do you know in which include file a specific API (type, constant, or function) is located? It might be that GB32 already supports the API as a built-in API, only function declarations and constant definitions. GB32 does not have built-in API support for user-defined API type definitions, they always have to be defined by the program or imported from an include library. The easiest way to check if GB32 supports a specific API is by using the auto-complete feature. Just type the first letters of the function or constant and check if the auto-complete pops up with the required name. If it isn’t provided by GB32 you’ll need to check the Windows SDK documentation to see which C/C++ include file provides the declaration or definition for that API and load the equivalent GB32 .inc library file.

An example of using an API
Each topic in the Windows SDK specifies in which C/C++ header file an API function, type, or constant is declared. For instance, if and API is located in the winuser.h C/C++ header file you have a big chance of finding it in the winuser.inc.lg32 file. Let’s look at an example. Suppose your program wants to process the WM_GETMINMAXINFO message. After looking up the documentation for this message, it tells you to obtain the  MINMAXINFO structure from the lParam. However, GB32 itself does not provide a definition for the MINMAXINFO user-defined type, and you need to import the type. When you go to the SDK page that describes this type you’ll find at the bottom of the page the location of the definition of this structure: the winuser.h C/C++ header file. Now you know which GB32 include library you need: winuser.inc and the program can import that library. As it happens this structure contains members of the API type POINT, which is defined in wingdi.inc. However, you won’t need to include wingdi.inc in your program, because it is imported by winuser.inc (otherwise it couldn’t be compiled). After importing winuser.inc in your program the constants and user-defined types from wingdi.inc are available as well. So, the POINT structure is available as a user-defined type in your program.

Note winuser.inc exports the constants and type definitions of wingdi.inc, but not the function declarations. If you need an API function declared in wingdi.inc you’ll need to import wingdi.inc as well. Importing both include files won’t collide with each other.

$Library "winuser.inc"
$Library "wingdi.inc"

Although both include files export the same constants and user-defined types they are added only once to the program. Now if winuser.inc would also export the function declares from wingdi.inc the internal database of GB32 may become corrupted. Therefor, winuser.inc exports all constants and types using the $Export Const * and $Export Type * statements, but the exported $Export Decl name statements must specify each exported declare separately. This is easily done using the App+E shortcut, it inserts $Export Decl lines for each Declare in the library file.

Conclusion
You can use the Windows API inc files to import Windows APIs easily. Because the (compiled) user-defined types are imported from a library the autocomplete function has direct access to their members and is fully operational without first compiling your program.