16 October 2022

And / Or versus && / ||

Are you aware between the difference between And and && and Or and ||? And and Or are mathematical operators, while && and || are logical operators, so they are quite different. The only place where the && and || operators are used are in conditional statements like If, While, and Until. And  and Or are used in mathematical expressions (calculations) and are generally not meant to be used in the conditional commands  If, While, and Until.

The following example shows the difference:

Local Int x = 2, y = 3
' The inefficient way using And:
If x == 0 And y == 3
  ' do something
EndIf
' The efficient way using &&:
If x == 0 && y == 3
  ' do something
EndIf

The commands following the If statements aren't executed, but for different reasons.
The first If statement evaluates both expressions and then performs a mathematical operation on the result of both boolean expressions. The second If statement only evaluates the first expression and never bothers to check y == 3. Because And is a mathematical operator like +, -, *, / the expression x == 0 And y == 3 is completely calculated. First x == 0 is evaluated, which produces the boolean value False (0). Then y == 3 is evaluated, which returns the boolean value True(-1). After calculating both expressions the boolean results are combined by the And operator: 0 And -1 which then returns 0 (False), because only one expression is True. These are steps involved:

(1) x == 0 And y == 3
(2) FALSE And TRUE
(3) FALSE

The second If statement uses &&. Because && is not a mathematical operator the second expression is only evaluated if the first expression is True. Note that an AND operator requires both operands to be TRUE to result TRUE, as you can see from this AND table:

Value 1 Value 2 AND-Result
0 0 0
0 1 0
1 0 0
1 1 1

From the table you can see that if Value 1 equals 0 (FALSE) the result is always 0 (FALSE), despite Value 2. Only when Value 1 equals 1 (TRUE) Value 2 needs to be evaluated. The compiler produces code to do just that. It first evaluates the first expression x == 0 and because the result is FALSE the next expression y == 3 is never executed. This results in a considerable increase of performance, especially if the second expression calls a function. For instance, in the next example the function div3() is never called if x is 0 ( otherwise div3() would generate a division-by-zero exception):

Local Int x = 0
If x != 0 && div3(x) == 3
  ' do something
EndIf
Function div3(ByVal n As Int) As Int
  foo = 3 / n

The same is true for Or and ||. Look at this OR-table, the result is always TRUE if the first value is TRUE.

Value 1 Value 2 OR-Result
0 0 0
0 1 1
1 0 1
1 1 1

In the next example the second expression (y == 4) isn't evaluated because the first expression (x == 2) is already TRUE, making the result TRUE despite the result of the second expression.

Local Int x = 2, y = 3
If x == 2 || y == 4
  ' do something
EndIf

If we used the mathematical operator Or, first both expressions would be evaluated and then the boolean results would be or-ed, an unnecessary extra operation.

The And operator may have a purpose in a conditional statement, but it would be used as a mathematical operator in the expression, for instance to test if certain bits are set:

Local Int x = %111      ' binary notation of 7
If x And %11 > 0        ' test if lower 2 bits are set
  ' do something
EndIf

Here x (=7) is mathematically And-ed with 3 which results in 3, and 3 is larger than 0. The If statement will be executed.

Conclusion
Rather than using And and Or, use the logical operators && and || in conditional statements.

19 July 2022

Numeric/string conversions

Ever wondered how numbers are printed? Check out the following snippet:

Dim i As Int = 2345
Print i

Before the number is printed to the active window, the number is converted to a string using a hidden call of the Str(i) function. The same is true for printing a Date (numeric) value; the date-value is converted to a string before it is output in the Debug Output window:

Dim d As Date = Now
Debug.Print d
Debug Str(d)            // short for Debug.Print

The output is (at the time of writing this blog) is:

18-7-2022 17:03:14
18-7-2022 17:03:14

The snippet demonstrates that the date is converted using a hidden call of the Str() function. It is not possible to print any numeric value without converting it to a string first. GFA-BASIC 32 uses hidden calls to the Str() function to convert the numeric value(s). The need for a numeric-to-string conversion before printing to screen (or printer) is that the underlying output functions require strings. For instance, to put the value on the screen the Print command uses the API function TextOut(hDc, x, y, strAddr, strLen), which requires a string.

Another implicit to string conversion is demonstrated in this example:

Dim s As String, i As Int = 2345, d As Date = Now
s = i & " " & d
Debug.Print s   // shows:  2345 18-7-2022 17:12:44

The & string operator allows to concatenate numeric and string values without converting them to a string explicitly. Still, under the hood, everything is converted to a string first. A nasty habit of the Str() function is to insert a space in front of converted value (a VB compatible setting). To prevent the space insertion use Mode StrSpace "0" somewhere at the beginning of the program.

The Str() and CStr() functions
The Str() function converts the value argument using a classic-style method, the output of the function is not language dependent. The produced string does not contain locale info for punctuation to separate the thousands and decimals in the value. A floating point is converted to a string with a single dot as separator. If the value is too large or too small the exponent notation is used.

s = Str( 1 / 3)    // argument is a Double
Debug s            // 0.333333333333333
s = Str(_minDbl)   // a very small number
Debug s            // -1.79769313486232e+308

The output of Str() can not be manipulated. However, there is another function that converts the numeric value according the user's regional or language settings, the CStr() function.

Dim f As Float = 1000.2345
Debug.Print CStr(f)     // output: 1000,234

Here the output follows the regional setting for the Netherlands where the decimal part is separated with a comma. The CStr() function follows the rules defined by the OLE API function VariantChangeTypeEx() that handles coercions between fundamental data-types, including numeric-to-string and string-to-numeric coercions. One of the parameters of the API is the LCID, a value that uniquely identifies the language to use. GFA-BASIC 32 stores the LCID value at start-up, by querying the user's default LCID from the OS. The LCID value is stored in the Mode structure and cannot be changed directly. The only way to modify the internal LCID is by using the Mode Lang command. By specifying a 3 letter language abbreviation GB32 will change the internal LCID value accordingly.

Dim sMyLanguage As String = Mode(Lang)
Mode Lang "DEU"         // change to German, LCID is changed.

If you're not satisfied with the numeric format produced by CStr() you can switch to the Format() function. The Format() function not only allows language depending conversion, but you can fine tune the output for your particular needs. When your user inputs a value in a language dependent value, for instance by using a TextBox, your program must be able to convert the string to a numeric datatype in GFA-BASIC's internal format using one of the C*() conversion functions, like CInt($).

String to numeric
Converting from string to numeric, is done using the Val*() functions or the other C*()-conversion functions. The Val* functions accept strings containing numeric values in the classical format, ie. the only punctuation allowed is a dot in floating point values. To convert to a Double use either Val() or its synonym ValDbl():

Dim d As Double, sValue As String = "1000.234567"
d = Val(sValue)
d = ValDbl(sValue)

To convert to a 32-bit integer use ValInt() and for a 64-bit integer ValLarge().

The Val() functions are not suited for language dependent formatted values. In the next snippet, the number is formatted according the regional settings of the Netherlands and is then converted to a Double using CDbl().

Dim d As Double, sValue As String = "1.000,23"
d = CDbl(sValue)
Debug d         // output: 1000.23

CDbl() uses the same  API function VariantChangeTypeEx() to process the string and change it into a floating-point value. To parse a language dependent formatted numeric string use any of the following functions:

Bool = CBool(); Byte = CByte(); Currency = CCur(); Date = CDate(); Double = CDbl(); Short = CShort(); Integer = CInt() or Long = CLong(); Handle = CHandle(); Large = CLarge(); Single = CSng() or Single = CFloat(); String = CStr(); Variant = CVar()

These function not only parse strings, but can also be used to convert any other data-type using an explicit cast. For instance:

Dim i As Int, b As Bool = True
i = CInt(b)     // i becomes -1, same as i = b

These explicit casts produce the same code as simply assigning one datatype to another.

Conclusion
CStr() produces a language dependent formatted string using the current LCID value. The C* conversion functions use the LCID language value for conversion to a numeric datatype. Format() offers more possibilities to format a numeric value. Str() and Val*() function use classical formatted values.

25 April 2022

Variables and parameters

Let’s discuss some basic issues of variables and parameters and explain auto-complete information about variables and parameters.

This blog post is a sequel to: Where are variables stored?

Variable declaration
Before a variable can be used it must have been declared explicitly. A declaration introduces the variable-name into the compiler’s database. The declaration requires a name for the variable and a datatype so that the compiler knows what to do with that variable; an integer variable is handled completely different than a string variable. Usually, the type is specified in a declaration statement. If the type is omitted the variable gets the default type Variant.

Global i As Int, v      ' an integer and a Variant

Not specifying a type introduces a Variant that might quickly cause confusion and problems. Variant-operations use different functions than – for instance – integer and floating-point operations. When a Variant is used to store a numeric value, simply incrementing it would require the call of a special Variant-Increment function in the runtime. Incrementing a simple data type as Int and Float is an (almost) atomic operation and requires only one CPU or FPU instruction. Therefor, it requires some attention when declaring variables. An error is quickly made as shown in the next line:

Global pic1, pic2 as Picture  ' probably not wanted

This line declares two variables. Two Picture variables are required, but pic1 is a Variant!
Instead use this:

Global Picture pic1, pic2

Before the introduction of auto-complete it was difficult to note these errors. Now auto-complete shows you the type of the variable. Here the type of pic1 variable that was wrongly declared:

Screenshot 2022-03-04 Variant AC

Global, local and static
Other statements to declare variables are Local, Dim, and Static. The Local statement declares a variable that only exists in a procedure. When Dim is used inside a procedure it declares local variables, when it is used in the main part of the program it introduces global variables. (Note that the main part of the program can have local variables as well using the Local statement.) When a procedure returns the local variables go out of scope and the variables are removed from the stack or and the dynamic variables are released (their memory is deleted).
The variables declared with Static are global but only locally visible. They are not freed when they go out of scope, they keep their contents.

Initialization while declaring
Declaring a variable adds it to the compiler’s database, a declaration does not introduce any executable code! A common practice is to collect the declaration of global variables in a separate procedure often called Init or something like that. Note that such a procedure doesn’t need to be executed, i.e. called from the main-part of the program. The procedure would not contain any executable code.
However, this changes if the declaration is used to initialize the variable with a value:

Global s As String = "Hello"

Now the declaration statement contains executable code that needs to executed when the program is run. The statement introduces the variable s into the compiler’s database and produces code to copy “Hello” into the string variable. If the program uses a procedure to declare globals that also initialize the variables, the procedure should be executed when the program is run. The procedure must also be run if the contains array declarations.

A special case is the Static local-variable, which is usually initialized while being declared. The initialization code is executed only once: the first time the Static statement is executed. (This is accomplished by guarding the Static statement by a hidden global boolean variable. After executing the Static statement the hidden boolean is set to true and the statement is never executed again.) Here is an excerpt form gfawinx.lg32’s WinDpi function:

Function WinDpi(hWnd As Handle) As Long

  Static Long pfnGetDpiForWindow = GetProcAddress( _
    GetModuleHandle("user32.dll"), "GetDpiForWindow")

  If pfnGetDpiForWindow       ' works from Windows 8.1
    WinDpi = StdCall(pfnGetDpiForWindow)(hWnd)
  Else
    WinDpi = GetDeviceCaps(Screen.GetDC, LOGPIXELSX)
    Screen.ReleaseDC
  EndIf
EndFunc

The pfnGetDpiForWindow is only initialized once with the function pointer to GetDpiForWindow() API or null if it isn’t supported. If the WinDpi() function is executed again, the pfnGetDpiForWindow variable is still pointing to the API or it is still null. If the API isn’t supported by the Windows version, the DPI of the screen-device context is returned.

Simple datatype parameters
When declaring procedure parameters you need to decide whether to pass a value or variable by value (ByVal) or by reference (ByRef). In general, a parameter is passed by value unless the passed variable needs to be modified. A by value parameter is pushed on the stack by the caller and popped from the stack by the called procedure. Passing a 32-bit integer by value requires 4 bytes of stackspace, passing a Variant by value takes 16 bytes (the size of a Variant).
When a variable is passed by reference the storage location of the variable – a 32-bit memory address -  is pushed on the stack. A Variant passed by reference takes only 4 bytes of stackspace. However, a by reference variable requires an additional step from the compiler: it needs to obtain the address of the variable before pushing it on the stack.

Dynamic datatype parameters
How about passing an array, hash, string, variant, or object (OCX) parameter? Well, an array is simple, it can only be passed by reference. A hash can not be passed without problems due to a bug in GFA-BASIC.

Passing a string by reference is faster than passing it by value. A by value string is first copied in the calling procedure and then the (hidden) copy is passed by reference. It isn’t possible to copy an entire string on the stack! Because the string is first copied, it takes a malloc to allocate the string memory and a memcpy to copy the string’s characters. So, it can be (much) faster to pass a string by reference, you only need to make sure you don’t change the contents of the by reference string parameter. 
Auto-complete cannot differentiate between these types of string parameters and always presents a string parameter with the Ref clause.

Screenshot 2022-03-11 085036

A COM object variable is best passed by value, it only takes 4 bytes to pass the contents of a COM variable. A COM or Ocx variable is a 32-bits integer pointing to the actual COM object. The only need for a by reference COM parameter is when the object must be set to Nothing.

Passing a Variant by value may cause trouble and even a program crash if not handled properly. The rule of thumb is:

Don’t write to a by value Variant parameter (don’t use the by value variant parameter as a convenient extra local variable).

Explanation of variant parameter issue
Often a subroutine parameter is used as an extra local variable that can be written to. For instance, the ByVal s parameter in the procedure foo above can be used to temporarily store a string, s is a copy of the string passed to the procedure. Writing to s won’t affect the string in the caller. A variant containing a string that is to be passed to a procedure by value does not copy the string before invoking the procedure.
Dim vnt = "Hello"
foo(vnt)        ' by value
Trace vnt       ' wrongly displays Hello

Proc foo(ByVal v As Variant)
  v = "GFA BASIC GFA BASIC GFA BASIC"

This code sample produces problems. The vnt variable stores a pointer to an OLE string containing “Hello”. When passed by value the parameter v is a copy of vnt, a 16 bytes data-structure with type information (VT_BSTR) and a pointer to the OLE string “Hello” on the stack. Assigning a new string to v will release the OLE memory currently pointed to by v. The new OLE string’s memory address is stored in v, together with the new data type (again a VT_BSTR). When leaving a procedure parameters aren’t cleared, so the foo procedure does not free the new contents of v. The variant’s 16 bytes occupying the stack are simply popped off the stack, leaving the new OLE string unreferenced. After returning from calling foo there is nothing that holds a pointer to the new OLE string and the OLE memory will never be released, the program is leaking memory.

Now, why does Trace vnt display “Hello”? After executing foo, the vnt variable is still referencing the OLE memory allocated by the assignment of “Hello”. The OLE string was released in foo when the new string was assigned, but the original variable vnt is never updated. The variable vnt still references the memory bytes where Hello was stored, bytes that weren’t actually cleared when released. The variable vnt references released OLE memory. When vnt goes out of scope, at the end of the program, it is released by GFA-BASIC by calling the OLE system function VariantClear(). Since the variable vnt points to released memory, the program may crash.

The type of procedures and parameter-defaults
To declare a subroutine you can choose between a Procedure, Sub, Function, or FunctionVar. The rule of thumb here is to use a Procedure or Function, unless you explicitly need a Sub or FunctionVar. The Sub is needed for event procedures where the parameters are passed by reference, the default behavior for a Sub. However, using a Sub as a general procedure might cause problems due to a flaw in the default by reference behavior. See the link at the end of this post for more information. If you use a Sub for something else than event subs make sure to use an explicit ByRef or ByVal clause in the declaration of the parameters.

Default behavior of procedures and functions:

Type of subroutine  Default ByVal or ByRef  Default datatype  Default return datatype
Procedure ByVal Double -
Sub ByRef in event subs,
otherwise flawed
Variant -
Function ByVal Double Ref Variant
FunctionVar ByRef Variant Ref Variant

As you can see, a function’s default return type is a by reference Variant. This means that the caller of the function passes a Variant by reference, the variant is ‘owned’ by the caller. This is illustrated by the following example:

Proc foo()
  Dim v As Variant
  v = GetValue()        ' passes v by reference
EndProc
Function GetValue()     ' default is variant
  GetValue = 10         ' this references v from foo
EndFunc

The GetValue = 10 assignment writes to the v variable from foo() directly.

Here you see the auto-complete information of the function-variable GetValue:

Screenshot 2022-03-06 101409

If the function’s return type is String, Object (or other Ocx type) or a user defined type, the caller passes a by reference variable to the called function.
A function cannot return an array or Hash datatype.
Note - Each function automatically gets a ‘local’ variable with the function’s name and type. This function-variable can only be used to assign a value, it is not available as a real local variable that can be used to read from; consequently auto-complete won’t show the function variable in a ‘read-context’.

Finally
Pay attention when declaring variables to not introduce unwanted Variants. Explicitly use ByVal or ByRef when declaring subroutine parameters, and also explicitly specify the datatype (either by name or by postfix). Auto-complete always shows by reference for string parameters, even if they are passed by value. The default return type of a function is a by reference Variant. Don’t use a Variant parameter as a general local variable, ie. don’t write to the variant.

See also: Function and Sub parameters & Passing default ByRef parameters to a Sub

13 March 2022

Update 2.62 March 2022

A picture says more than 1000 words, therefor a screenshot with notes of improvements and fixes of the IDE (running on Windows 11):

Update 262

Runtime Updates   
Since last year virus-scanners report the patched runtime (GfaWin23.Ocx) as malicious - a false positive! Consequently, it is no longer possible to release a runtime binary with bug fixes. A new way of fixing bugs had to be introduced. Starting with this update version 2.62 the runtime bugs are fixed on the fly, they are patched when the program is run (F5). For this to happen each new program automatically inserts the following lines:

$Library "UpdateRT"
UpdateRuntime      ' Patches GfaWin23.Ocx

The $Library “UpdateRT” command loads the compiled code with the exported procedure UpdateRuntime. The UpdateRuntime should be the first statement executed, so that the runtime is fixed before the actual program is run. By taking this approach we can continue fixing runtime bugs!
You can inspect the source code in UpdateRT.g32, which is stored in the Include directory.
For older programs these lines have to be added manually, so that they benefit form the fixes as well.

See also: Update 2.6 produces a false positive (virus scanner)

Fixed: the Branch-Optimization compiler bug
This update sets an important step forward by fixing the compiler’s Branch-Optimizing bug. When Branch-Optimizations was set to any other value than zero the program could crash in one particular situation: when a Case or Otherwise statement was directly followed by an Exit command or one of its variants (Exit, Exit For/Do, Exit Proc, etc). The EXE crashed also if ‘Full Optimization for EXE’ was set. Now this problem is fixed, your program can benefit from full branch optimization, it reduces the size of the program by 10% and increases performance.

A couple of new IDE features.

  • The editor now shows format lines (toggle with Ctrl+F7) and you can use PeekView to hoover over format lines to see what statements are connected by a format line.
  • New projects can be started from a template, ranging from a simple Hello World app to a sophisticated dpi-aware application. The templates can be found in the File | New menu-item.
  • AC (auto-complete) comes with many improvements.
  • In dark-mode the proc-line color is changed and the auto-complete list box is displayed in dark mode as well.

See Readme262.rtf for more fixes and improvements.

New Commands/Functions in gfawinx.lg32

  • PrevInstance helps in starting a program only once.
  • DlgCenter centers all GB32 dialog boxes in the main thread on top of their owner (Me).
    DlgCenterHook is used to center the dialog boxes in a additional threads.
  • WorkWidth & WorkHeight return the real size of the clientarea of a Form with a toolbar and/or statusbar.
  • OffsetXY sets the graphical offset using positive values for GB32 graphical commands.
  • VStrLen, VStrLenB, and VStrPtr return the length (in character or bytes) and the address of a string in a Variant.
  • AutoFreePtr returns a COM object for storage of handles and (memory) pointers that need proper releasing, even when the program halts with a runtime error or Stop/End. The COM object has a Ptr (read/write) property to read and update the resource after the object is created.  

See the updated CHM help-file for detailed information about these new features.
Note – gfawinx.lg32 is loaded automatically in new projects. Optionally, this can be disabled in the Extra tab of GFA-BASIC 32 Properties.

The new App_Close event
Probably the most important new feature of this update is the introduction of an application close event: the App_Close event sub. This event sub is automatically called after the program is closed. It provides a way to free global resources that might never be released otherwise. This happens when developing a program and the program stops abruptly with a runtime error. This happens all the time. When a runtime error occurs the IDE shows a message box telling about the error and puts an arrow on the line that caused the error. Maybe you don’t always realize, but after execution stopped no more code is executed and allocated resources won’t be released. For instance:

OpenW 1
' Create global resources
Do
  Sleep
Until Me Is Nothing
' Delete resources: might never be reached!

The releasing of resources also fails when a program ends with the End or Stop statement, no more code is run after these commands. Any API handles, memory pointers, bitmaps, and other global resources are not released and the program is leaking memory. When the program is run again and then stops again in the middle of the execution the leakage accumulates and soon you might experience unexpected errors. Even more, there are situations where the program can’t be run again, because of locked handles (for instance mutex handles). By using App_Close this leaking is over:

OpenW 1
' Create global resources
Do
  Sleep
Until Me Is Nothing

Sub App_Close()
  ' Delete resources: guaranteed to be released!
EndSub

Now put the program-lines that free the resources in the App_Close sub and the global resources are guaranteed to be released in all situations. 

The AutoFreePtr object
The implementation of App_Close is only possible through the introduction of the AutoFreePtr object. Instead of using the App_Close event sub a resource can also be assigned to an AutoFreePtr, which will call a predefined or custom procedure to free the resource. Because App_Close can only be used for globally stored resources, any local resource can be automatically released by using an AutoFreePtr. For instance, when a procedure temporarily allocates some memory that needs to be freed at the end of procedure:

Proc Something
  Local pmem As Long, Mem As Object
  pmem = mAlloc(100)            ' alloc some memory
  ' Allocate an AutoFreePtr object and
  ' let it call mFree() automatically
  Set Mem = AutoFreePtr(pmem, AfpMfree)
  ' use pmem or Mem.Ptr
  pmem = mShrink(pmem, 50)      ' resize memoryblock
  Mem.Ptr = pmem                ' assign new pointer
EndProc                         ' No mFree call necessary!

The AutoFreePtr object provides a Ptr property (Long - Get/Put) to read the assigned pointer or handle and to re-assign a new pointer/handle. 
The AutoFreePtr can free several predefined pointer or handle types. By specifying an Afp* constant you can instruct the AutoFreePtr object to invoke that specific release function for you. See the helpfile for more info on which types can be freed automatically.
Instead of specifying a constant to execute a predefined freeing-function, you can set a custom procedure to call when the AutoFreePtr object goes out of scope.

Proc Something2
  Local pmem As Long, Mem As Object
  pmem = mAlloc(100)            ' alloc some memory
  Set Mem = AutoFreePtr(pmem, ProcAddr(FreeMem))
  ' use pmem or Mem.Ptr
EndProc                         ' mFree executed in FreeMem()
Proc FreeMem(ByVal ptr As Long)
  ~mFree(ptr)
EndProc

Start using App_Close and AutoFreePtr to develop without leaking memory and handles.

Download the new update: GFA-BASIC 32 for Windows: Download

12 November 2021

Code Performance Timing

This post discusses performance timing of a piece of code. Basically, there are two methods of timing, either by using the _RDTSC function or the Timer function. _RDTSC timing uses the CPU’s internal clock and Timer (QueryPerformanceCounter) uses the preferred, most reliable and portable timing method selected by Windows. Note that code using _RDTSC isn’t portable, it cannot be used in production code to perform timing, instead always use Timer (QueryPerformanceCounter).

Setting the scene
Recently I converted a piece of GFA-BASIC 32 code to assembler and wondered how much faster it was. I used the _RDTSC function to time the execution-speed of the code. The structure of the program was this:

Dim t As Large
$StepOff
. db 0x0F, 0xAE, 0xE8       // LFENCE opcode
t = _RDTSC
' execute GB32 code
. db 0x0F, 0xAE, 0xE8       // LFENCE opcode
t = _RDTSC - t : Debug "GB32-code:"; t
. db 0x0F, 0xAE, 0xE8       // LFENCE opcode
t = _RDTSC
' execute assembler code
. db 0x0F, 0xAE, 0xE8       // LFENCE opcode
t = _RDTSC - t : Debug "Asm-code:";t
$Step

The $StepOff command disables the inclusion of debugging code; the GB32-code following $StepOff is compiled without instructions to call a Tron proc. It also disables the possibility to break (or stop) the program. The compiler switch $Step(On) re-enables the inclusion of Tron calls (and the possibility to break). Usually, these compiler switches are only used temporary for a small piece of code that needs to run at optimal speed in the IDE. A compiled to EXE (LG32 or GLL) program does not include the $StepOn functionality.

The test program gave me the following results: the GB32-code executed at ~4500 ticks and the assembler at ~3600 ticks. But what do these values mean? Are these the real CPU clock cycles necessary to execute the code?

The _RDTSC function
The answer to this question is no, the returned timing values did not represent the real number of assembler code clock-cycles as is advertised. The test-values simply returned the passed time between both _RDTSC calls. _RDTSC reads the time-stamp counter (TSC) of the CPU-cores. The value gives the number of elapsed ticks of the CPU’s internal clock. Maybe, in the past, in the era of one-core processors that only executed one app a time (MSDOS), the value might have represented the actual number of assembler clock-cycles. But not anymore. On a multi-core CPU the task is most likely split to run on multiple cores and the code is executed much faster than it would have done on one core. A consequence of splitting the code on multiple cores is that the _RDTSC (assembler) instructions could be executed on different cores as well, and might return different values. Only when the cores synchronize their time-stamp clocks (TSC) the _RDTSC values are reliable. You can test if the CPU supports the _RDTSC instruction by testing bit 4 of the value in _CPUIDD:

Debug Btst(_CPUIDD, 4)  ' must give -1 (True)
Debug InvariantTSC()    ' must return True

To check if the TSC is invariant use:

Function InvariantTSC() As Bool Naked
  _EAX = 0
  . push ebx            ; always save ebx
  . mov eax, $80000007  ; Advanced Power Management Information
  . CPuid               ; invoke cpuid
  GetRegs               ' store the register contents
  . pop ebx             ; restore ebx to access local vars
  InvariantTSC = Btst(_EDX, 8)
EndFunc

If all is well you can use the _RDTSC for performance timing, only, what are we measuring exactly? The timing values I measured were always different and sometimes ten times more than average… The reason for these irregular values is for instance multitasking, memory latency, and CPU power-saving. The test code will probably be interrupted by the OS to process other applications, writing and reading to memory costs time, and due to power-saving the CPU won’t execute the code at its highest clock frequency. These issues will remain regardless of the timing method you choose. I take closer at these issues at the end of this post.

Flush the CPU before using _RDTSC
There is one other thing related to the use of _RDTSC. The CPU does not guarantee that the machine code is executed in the order it is compiled. In other words, the _RDTSC command could be executed when the code to test has already started executing. To overcome this problem the CPU must be flushed before invoking the rdtsc assembler instruction. This is either done using the cpuid instruction or the lfence assembler instruction. We’ll use the lfence instruction, because the cpuid destroys the registers eax, ebx, ecx, and edx, where ebx is essential to GB32 and destroying it might give access violation errors.

For a proper working of TSC timing insert the lfence instruction before each rdtsc instruction:

. db 0x0F, 0xAE, 0xE8       // LFENCE opcode
t = _RDTSC

The lfence instruction is not part of the GB32 built-in assembler, so we must insert the opcode bytes of lfence into the code stream manually. (You could use cpuid, but first save ebx (push ebx) and restore it afterwards (pop ebx), see the InvariantTSC() code above for an example.)

Convert the TSC values to micro-seconds
To be able to compare timing results among different PCs we need to convert the timing values to a standard unit, seconds or micro-seconds. To convert 4500 clock-cycles to micro-second, the number must be divided by the CPU’s clock frequency. On my PC the CPU’s internal clock has a base frequency of 1.51 GHz, as reported by the Windows Settings. However, I can obtain the frequency (in MHz) from the CPU as well:

Function GetCPUMhz() As Long Naked
  _EAX = 0              ' clear global variable
  . push ebx            ' save, ebx is used for accessing local vars
  . mov eax, 0          ' get CPUID level
  . CPuid
  . cmp eax, $16        ' support level $16?
  . jnz .end            ' no, return 0
  . mov eax, $16        ' get CPUID level $16
  . CPuid
  GetRegs               ' copy registers (_EAX, ..)
  .end:
  . pop ebx             ' restore ebx to access local vars
  GetCPUMhz = _EAX      ' set the return value
EndFunc

The GetCPUMhz function returned 1500 MHz (1.5 GHz). So, my ~4500 clock ticks take ~4500 / 1500000000 seconds, or, by multiplying the result with 10E6 to convert to micro-seconds, gives ~30 µs. The assembler version of my code took ~24 µs. (The values are an average after running the test code multiple times.)

Should you use _RDTSC?
Microsoft strongly discourages using the RDTSC processor instruction, because “you won’t get reliable results on some versions of Windows”, see Acquiring high-resolution time stamps - Win32 apps | Microsoft Docs. Instead, Microsoft encourages you to use the  QueryPerformanceCounter() API. However, these warnings apply to using RDTSC in production code, not to a temporary test situation on one PC.

Using QueryPerformanceCounter
The alternative to RDTSC is the portable QueryPerformanceCounter() API together with the QueryPerformanceFrequency() API. Well, that’s easy in GFA-BASIC 32, because these APIs are implemented by the the _TimerCount and _TimerFreq functions resp. Instead of using _RDTSC you could use _TimerCount, for instance:

$StepOff
t = _TimerCount
' execute code
t = _TimerCount - t : Debug "Ticks:"; t

For my test code the intervals with _TimerCount were ~30 and ~24 ticks, which are the same values as the reported _RDTSC intervals in micro-seconds. So, let’s see what these timer count values mean. The values tells us that the clock used by QPC was incremented by 30 and 24 ticks resp. As it turns out, QPC uses a different clock than RDTSC. The frequency of the QPC clock is returned by _TimerFreq and is - on my Surface 4 - 1000000 Hz (1 MHz) . So, the 30 timer count ticks correspond to 30 µs (Interval / _TimerFreq * 10E6), and the 24 ticks to 24 µs. So, as might be expected, both methods return (approximately) the same timings.

At start-up Windows decides how to implement QPC. It might use the CPU’s TSC for this purpose, but it might also use another clock provided by the PC’s hardware. Windows selects the most reliable method to obtain timing intervals. As you can see, on my PC Windows selects an alternative time source for QPC, the 1MHz clock.

GB32 has the right function for you: Timer
Now we know how to time intervals, we can use the GFA-BASIC 32 Timer function rather than divide the _TimerCount interval by _TimerFreq. The Timer function returns the current time in seconds as a Double:

Timer <=> _TimerCount / _TimerFreq 

To convert to micro-seconds multiply the interval from two Timer calls with 10E6.

t# = Timer
' execute code
t# = Timer - t# : Debug "Time in µs:"; Round (t# * 10E6)

After changing my code to use Timer the results were again 30 µs and 24 µs.
How fast are my test-results, or how fast is 1 µs? On my PC 1 µs takes 1500 TSC (CPU) ticks and 1 QPC tick. Code that operates below 1500 TSC ticks or 1 µs can not be measured with QPC (at least on my computer). You can calculate your Timer’s resolution as follows:

Debug "Timer-resolution:"; 1 / _TimerFreq * 10e6;" µs"

Other timing issues
Whether you use _RDTSC (with lfence) or Timer (QPC) the timing results simply return the elapsed time as measured by one of the internal hardware clocks. The interval is not the exact time necessary to accomplish the task. Because Windows is a multi-tasking OS the code is split over multiple cores and these cores might be interrupted by the Windows scheduler to execute other running processes. These issues can be addressed by limiting the test code to one core by using SetThreadAffinityMask() API and reducing the chance that that core is interrupted by using SetThreadPriority() API. The structure of such a program could look like this:

Dim Mask As Long = SetThreadAffinityMask(GetCurrentThread(), 1)
~SetThreadPriority(GetCurrentThread(), 2)   ' THREAD_PRIORITY_HIGHEST
Try
  $StepOff
  Dim t# = Timer
  ' execute code
  t# = Timer - t# : Debug "Time in µs:"; Round (t# * 10E6)
  $StepOn
Catch
  MsgBox ErrStr("Timing code")
EndCatch
~SetThreadAffinityMask(GetCurrentThread(), Mask)
~SetThreadPriority(GetCurrentThread(), 0)   ' THREAD_PRIORITY_NORMAL

This might give more reliable results if you have multiple cores. (Again, you won’t do this in production code. It will affect your application's performance by restricting processing to one core or by creating a bottleneck on a single core if multiple threads set their affinity to the same core when calling QueryPerformanceCounter.)

Making the test code run on one core still doesn’t return exact timing values. The executed code must be loaded into the CPU and reading the code from RAM memory might be slow(er) or fail (page hit faults) and the code might have to be re-read from memory again taking some extra time. In addition, the tested code might load and save values to memory that suffer from memory latency as well. Finally, on a laptop the actual CPU frequency might be lower than it’s base frequency, due to CPU power savings. Unfortunately, timing is not an exact science.

See also: Acquiring high-resolution time stamps - Win32 apps | Microsoft Docs.

See also: CPUID on Wikipedia

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.