29 November 2017

The initialization of the Printer object

Each time you press F5 to run a program the Printer object is reinitialized. As part of the initialization process the program obtains information about the current default printer to prepare the Printer object for use in your program. The same happens in the start-up process of a compiled stand-alone program. When there is no default printer, or a corrupt printer-driver, the Printer’s properties and methods are undefined. You may use one of the Printer‘s properties .DeviceName, .DriverName, or .Port to obtain information about the actual printer associated with the Printer object. For instance, to check for a valid initialization:

Dim fValidPrinter? = Len(Printer.DeviceName)

When the Printer object needs to be associated with the current default printer at all times, you can compare the device-name against the current default printer returned from App.PrinterName(0). Before the printing process actually starts check for a change and re-initialize the Printer object to the new default printer:

' Before printing
If Printer.DeviceName != App.PrinterName(0) ' change of default?
  SetPrinterByName App.PrinterName(0) ' Re-initialize to default 
EndIf
' Start printing

Note - Keeping track of the current default printer this way will not preserve any changes you made to the Printer object.

Available printers and their info
The App object provides properties that enables you to gather information about all the available printers on the system. For instance, to find a printer that has the Orientation set to portrait, starting with the first non-default printer:

' Iterate over available printers
Local i%, PrtName$
For i = 1 To App.PrinterCount    ' index = 1
  PrtName =  App.PrinterName(i)
  Debug PrtName : Debug " Info: "; App.PrinterInfo(PrtName, "")
  If App.PrinterInfo(PrtName, "Orientation") == 1
    Debug " > Printer in portrait mode"
  EndIf
  Debug
Next

The current default printer is returned by the App property PrinterName(0), with the index = 0. To retrieve all printer devices iterate over App.PrinterName(i) starting with index 1.

The PrinterInfo property retrieves detailed information of the available printers. PrinterInfo is a small wrapper for the GetPrinter() API using the PRINTER_INFO_2 structure to retrieve the information (see SDK). All member names of PRINTER_INFO_2 structure can be passed as an argument to PrinterInfo(PrinterName, membername$), which returns a Variant with the member’s value. Depending on the member-name, must be one of the below, the Variant contains a string or a Long.

'Server','Printer','Share','Port','Driver','Comment','Location',
'Port','SepFile','PrintProcessor','Datatype','Parameters',
'Attributes','Priority','DefaultPriority','StartTime',
'UntilTime','Status','cJobs','AveragePPM'

In addition, PrinterInfo returns a few DEVMODE settings;

'Copies','Orientation','Duplex','Color',
'PaperLength','PaperWidth','Quality','PaperBin','PaperSize'

Finally, in case you pass an empty string - PrinterInfo(PrinterName,””) – it returns one string combining the settings of 'Server', 'Printer', 'Share', 'Port', 'Driver', 'Comment', 'Location', 'Port', 'SepFile', 'PrintProcessor', 'Datatype', and 'Parameters' as key/value pairs.

Printer Object initializing
By default the Printer object is initialized for the default printer. Using the Printer object lets you retrieve or set the settings of the default printer. Any printer output is based on the Printer object’s settings. To change the printer associated with the Printer object GB provides four commands:

Set Printer = cd                ' assign a CommDlg Object
SetPrinterByName "PrinterName"  ' specify a device name
SetPrinterHDC hdc               ' based on a printer device context
SetPrinterHDNHDM hDevName%, hDevMode% ' pass memory handles

The SetPrinterHDNHDM is at the heart of all printer changing commands. The other commands act basically all the same: they retrieve the the DEVMODE and DEVNAME structures from the input parameter and then call SetPrinterHDNHDM. The DEVMODE and DEVNAME structures provide all the information needed to properly initialize the Printer object. The CommDlg object, for instance, holds a DEVMODE and DEVNAME structure after having invoked the ShowPrint dialog box.

The Printer’s default initialization at start up requires these structures as well, and they are obtained by calling the PrintDlg() common dialog API. The PrintDlg() API function only requires two flags:

  • The PD_RETURNDEFAULT flag is used to retrieve information about the default printer without displaying the Print dialog box.
  • The PD_NOWARNING flag prevents the warning message from being displayed when there is no default printer.

At start-up the PrintDlg() is asked to return information about the current default printer without displaying the dialog box and without displaying a warning if the printer is missing. The existence of the PD_NOWARNING flag suggests it is possible (maybe there are no printers at all).
PrintDlg() may also fail because of a corrupted printer driver, or some other reason causing a delay in execution. It isn’t guaranteed that the PrintDlg() will always succeed. Consequently, it isn’t guaranteed that the Printer object is always properly initialized. When PrintDlg() fails GB initializes the Printer object’s private data to zero. This way the global Printer object is at least ‘not Nothing’, but its behavior is undefined because of the lack of actual device information.

Note - The SetPrinterHDNHDM command is useful when combining API functions and the Printer object. It takes 2 handles (hence HDN and HDM) to unlocked global-memory containing the DEVNAME and DEVMODE structures respectively. The hDevName and hDevMode arguments are handles to moveable global-memory objects allocated using GlobalAlloc(GMEM_MOVEABLE, sizeof(DEVMODE/DEVNAME)). They must be unlocked before passing to SetPrinterHDNHDM.

28 September 2017

CreateObject caching (.NET example)

Thanks to a little experiment using .NET classes from GB, I discovered a bug in the caching of dispids for IDispatch objects created using the CreateObject() function. (An update of the OCX runtime is available.) For demonstration purposes I’ll show you code accessing .NET classes I used to test caching. Caching is only available for objects created by CreateObject. I discussed the peculiarities of CreateObject in an earlier blog and if you need to freshen your memory you might want to check it out.

CreateObject and caching
The CreateObject function not only differs from VB/VBScript in creating a COM class and connecting to the IID_IDISPATCH interface, but it also sets up a Hash table to store the dispatch identifiers of the properties and method names. Each(!) object created from CreateObject is encapsulated in another hidden IDispatch object. The Object returned by CreateObject is a pointer to this hidden GB-provided COM object. The hidden object implements all 6 IDispatch functions, but only provides custom functions for GetIDsOfNames and Release. The other four (QueryInterface, Add, GetTypeInfoCount, and GetTypeInfo) are simply routed to the automation object directly. The hidden object provides the caching.
Only objects created with CreateObject are affected, that is if the class is not a GFABASIC32 Ocx, StdFont, or StdPicture type. When these terms are met, the CreateObject function creates an empty hash table and stores it in the encapsulating IDispatch object. When a property or method is executed for the first time, the identifier of the name is looked up using IDispatch.GetIDsOfNames function and saved in the hash table for future use.

Why caching is useful
The compiler converts the execution of a  property or method into a call to a runtime-library function which performs the actual execution of the property/method. The property/method is directed to the IDispatch.Invoke function. However Invoke does not accept named properties/methods, but only numeric values identifying the property or method. This integer value is called dispid. (You can retrieve a dispid yourself using the _DispId() function.) Before Invoke is executed the dispid value associated with the property/method name must the obtained by calling IDispatch.GetIDsOfNames function. Calling GetIDsOfNames each time before calling Invoke, decreases performance. This is especially true for out-of-process servers; non-DLL servers, DLLs are in-process servers, loaded into the process memory. Communicating with an out-of-process server is time consuming. Even more when the communication requires two steps, obtaining the dispid and executing the function. Once a property or method is used and its disp-ID value is known, it is logical to store the ID for future use in an easy accessible Hash table.

The encapsulating object
All IDispatch properties and methods are executed by the same runtime-library function, which is unaware of cached IDs. The GetIDsOfNames function is executed always before calling Invoke. However, the hidden Object that encapsulates the automation object, supports a custom GetIDsOfNames and first searches the hash table for the name. If it finds the name it has the ID value available directly. If the hash element isn’t found, it calls GetIDsOfNames on the external COM object and then stores the ID in the hash. When the Object goes out of scope, the custom Release function erases the hash table and the encapsulating object is freed from memory.

Example using .NET
The following code creates a .NET classes that support an IDispatch interface. The classes used are located in the mscorlib.dll, which is loaded into the process-space of the GB-application. The performance gain measured is about 15%. It will be better for out-of-process servers, the example is simply as a test for caching.

' ArrayList coclass supports multiple dual intefaces:
'  IList, ICloneable, _Object, ICollection, IEnumerable
' Togeher their exposed properties/methods make-up
' the ArrayList's class interface.
Dim oArrList As Object, v As Variant, i As Int
Set oArrList = CreateObject("System.Collections.Arraylist")
' IList
oArrList.add 3                ' IList.Add
oArrList.add 4
oArrList.add 4
Trace oArrList(0)             ' IList.Item(index)
Trace oArrList.Contains(3)    ' ILIst.Contains
' ICloneable
v = oArrList.Clone            ' ICloneable.Clone
' v is copy of ArrayList:
Trace v
' _Object interface
Trace v.ToString              ' _Object.ToString
Trace v.GetHashCode           ' _Object.GetHashCode
Set v = Nothing
' ICollection
Trace oArrList.Count          ' ICollection.Count
Trace oArrList.SyncRoot       ' ICollection.SyncRoot
' IEnumerable
For Each v In oArrList
  Trace v
Next

' Queue collection
Dim myQ As Object
Set myQ = CreateObject("System.Collections.Queue")
myQ.Enqueue "Hello"
myQ.Enqueue "World"
myQ.Enqueue "!"

26 August 2017

A little array magic

Without going into a formal description of an array, we simply state an array stores multiple values of the same type in contiguous memory. In code an array is recognized by a variable name followed by parenthesis, either with or without indices. Like any other variable an array should be declared before it can be used. (Declaring a variable introduces a variable to the compiler.) Generally, a declaration specifies a name and a type. In case of an array the declaration may include values for lower and upper boundaries up to 7 dimensions.
Array declaration and dimensioning
An array declaration always results in the creation of an array-descriptor. For a global array the descriptor is added to the program’s global data section and for a local array the compiler inserts code to allocate an array descriptor dynamically.
' Declaration and allocation separated:
Global Dim a() As Long  ' adds descriptor to data
ReDim a(6)              ' code to allocate memory

' Declaration and allocation at once:
Global Dim b(3, 1) As String
The second declaration forces the compiler to add a descriptor to the global data and to generate code to allocate memory. It is the exactly the same as Global Dim b() As String : ReDim b(3,1).
A local array variable declaration is handled differently from a global declaration. First of all, the array is not assigned a static descriptor by the compiler. The declaration of the local array let the compiler insert code to obtain (or allocate) an array descriptor dynamically when the procedure is executed. The pointer to the descriptor is stored in a hidden local memory location on the stack of the procedure. Then the address of the descriptor is passed to the same ReDim to allocate memory for the array elements.
Proc LocalArr()  ' Naked forbidden
  Dim dum$       ' prevent compiler bug
  Dim h()        ' allocates a descriptor
  ReDim h(4, 5)  ' allocates memory for descriptor
  Dim v(3)       ' 1-step: descriptor + memory
EndProc          ' destruction for h() and v() and dum$

Local arrays have the same anatomy, but they have no descriptors in the global data-section. Both the descriptor and the memory are allocated – from the heap - when the subroutine is executed. Room is reserved on the procedure stack for a (hidden) pointer to store the address of the descriptor. Later this pointer is necessary to clean the local stack and call the array-destruction code when the subroutine is left.

Local Array Destruction
Local array destruction is part of the termination handler of the procedure, that is if it has one. A Naked procedure doesn’t include termination handlers; the procedure needs to clear pointer variables manually (= the developer). However, a local array cannot be destructed manually, there is no statement to do so. The obvious Erase would only release the data memory, not the array-descriptor, leaving it on the stack. Eventually, the stack might overflow when the procedure is executed repeatedly. 

  • An array cannot be destroyed explicitly, Clr doesn’t work with arrays (and hashes).
  • Local arrays are not allowed in Naked procedures.Naked prevents the compiler from insertion of destruction code for all pointer variables (String, Variant, Object).

Be aware of two possible problems
When a subroutine contains only one or more local array variables, without any other local variables of pointer types (String, Variant, Object), the compiler ‘forgets’ to insert the array-destruction code at all. This is a bug. In this specific situation it is necessary to force the compiler to add array-destruction code. This requires the introduction of another dynamic data-type that requires destruction code. A local String is the easiest solution as is demonstrated in the example above.( A bug still waiting for resolving ….)

The other problem involves ReDim, which - unlike Dim - does not default to the Option Base setting. Instead, ReDim always uses 0 as the lower bound of the array. When Option Base 1 is the default setting for your application, you need to use ReDim ar(1 .. x) explicitly, rather than ReDim ar(x).

- Important note on a Hash
A local Hash isn’t destroyed automatically as well (Naked or otherwise). Clr cannot be used and there is no way to force the compiler to insert Hash destruction code. All local Hash variables must be released manually using Hash Erase. You might want to use Static Hash for local variables. A Hash is a (relative) time consuming type, all entries of a Hash are released one by one. Static preserves the contents and prevents time consuming destruction.

Global Array destruction
GB implements hidden destruction for releasing arrays.  A local array is destroyed on exit and a global array when the program is terminated. For a global array the descriptor is static and part of the global data-section and is an inherent part of the program. After a program exits (either as an EXE or in the IDE) the global data-section simply disappears and the descriptors with it. In case of an EXE-process all memory is released to the OS, and in the IDE the global data is destroyed after ending the program (RUN). There is no cause for memory leak on global arrays.

Anatomy of an array
In GB32 an array is described using a variable name, a descriptor, and a piece of contiguous memory to store the array data. When the compiler hits on a global array declaration it will create a mapping between the variable name and an array-descriptor stored in the global data section. This is true for in-memory compiling and when an EXE is created. A local declaration introduces a mapping between a hidden local pointer variable (32-bits pointer) and the name. The hidden variable stores the pointer to the dynamically allocated array descriptor.
An array-descriptor is a structure defining the attributes of an array. This ArrayDesc - structure is defined like this (note how the last member reserves LBound/UBound information for a maximum of 7 dimensions):

Type ArrayDesc
  -Int    Magic         ' "arry" or "ArrY"
  -Int    ptype         ' vtType (internal const)
  -Int    size          ' size of datatype
  -Int    dimCnt        ' number of dimensions
  -Int    dimCnt2       ' # of dimensions     == IndexCount
  -Int    paddr         ' address of data     == ArrayAddr()
  -Int    corr          ' correction value
  -Int    paddrCorr     ' void* addrCorr;
  -Int    anzElem       ' number of elements  == Dim?()
  -Int    sizeArr       ' size in bytes       == ArraySize()
  -Int    Idx(7 * 3)    ' == LBound()/UBound()
EndType

For global and static arrays an instance of this structure is stored in the global data section. For local arrays the structure is allocated dynamically. Important to realize is that every declaration (Dim/Global/Local/Static) of an array immediately results in an array descriptor dimmed or un-dimmed. The values of the structure members determine the status of the array. The Magic member is for internal use, although it perfectly well indicates if an array is empty – Erase-d or an empty declaration. Other members can be retrieved using  the following functions.

FunctionMember ArrayDescDescription
Dim?(a()) anzElem (element count) Returns the number of elements in the array. Erase clears this value (sets to 0). One way to determine if an array has been ‘dimmed’.
IndexCount(a()) dimCnt2Returns the number of dimensions. Returns 0 when not ‘dimmed’. Another way to determine if an array is empty.
ArrayAddr(a()) paddrReturns the memory address of the first element of the array. Returns 0 if erased or not ‘dimmed’. Can be used to determine if an array is empty.
ArraySize(a()) sizeArrReturns the size of all elements in bytes. Returns 0 if array is empty.
LBound(a()[,i=1]) Idx[]Returns the lower bound for a dimension (default is 1). Raises an error when an array is empty.
UBound(a()[,i=1]) Idx[]Returns the upper bound for a dimension (default is 1). Raises an error when an array is empty.
  • Only LBound and UBound cannot be used to inquire for an ‘un-dimmed’ array.
  • For the special case OLE Automation array-type ParamArray, LBound and UBound return 0 and -1 respectively; these functions do not raise an error (VB compatibility). The ParamArray datatype is in fact nothing more than a Variant containing an OLE/COM SafeArray.
Functions and statements that do not apply to arrays
An array variable is treated differently from any other variable type. The array’s variable name cannot be used in any other GB32 functions and statements as other variables can. For instance, the Clr a() statement is forbidden, ArrPtr() function does not return the location of the array’s variable name, but the location of the array-descriptor instead. You cannot use Pointer to redirect an array variable name to another descriptor. TypeName(ar()) cannot be used to obtain the data type of the array. Etc.
  • Generally, all GB-functions and statements that use a variable name as an argument are forbidden for arrays.
When the compiler refers to an array it refers to the descriptor directly. The compiler doesn’t preserve a mapping between the array’s variable name and a particular location of a pointer as it does with Strings for instance. The generated code simply doesn’t ‘know’ the array name anymore, only the location of descriptor.
There is only one runtime function that accepts the address of a (local hidden) pointer containing the address of an descriptor: CLEARARR() the local array destructor. This function cannot be invoked manually – not even when using assembler, because the address of the hidden variable is unknown. Asm lea eax, ar will not work, it still returns the address of the descriptor.

02 August 2017

StatusBar fix (August 2nd Update)

Fixing one bug often leads to the introduction of another. The June update introduced a bug in the runtime in the Panels.Remove method. This is fixed and the most recent update of the runtime – gfawin23.ocx – is now version 2.32.0.1202. Although I like to step away from incrementing build-numbers, I decided to give it a new build because nothing new was added, only a bug is fixed.

StatusBar redraw problem
During testing I noticed the following. In some circumstances the StatusBar isn’t updated or redrawn to reflect the new situation with a panel removed. This is not GB32 bug, but it is related to the common controls. The StatusBar is only completely reset when it receives a WM_SIZE message. Usually, this message is forwarded by the Form, the parent of the control, when it processes the WM_SIZE message. When the Panel is removed the code does not send or post this message and the Form will never receive the message. The following sample code solves this problem, where sb is the name of the StatusBar Ocx.

' Remove a StatusBar Panel:
sb.Panels.Remove
"Panel1" ' WM_SIZE recalculates and updates: PostMessage sb.Parent.hWnd, WM_SIZE, 0, 0

The lacking update and redraw is a known problem for more common controls. Usually this can be solved by posting a WM_SIZE message to the parent (without any values in the wParam and lParam). The preferred solution would be invalidating the control followed by a call to the UpdateWindow() API. However, this doesn’t recalculate and reset the panels. The status bar is simply redrawn using the ‘previous’ or current settings. This makes sense when you think about it. If the status bar would recalculate its contents each time it has to redraw, it would provide a serious performance penalty in redrawing a Form when it is manipulated. The common control needs to be told explicitly to adjust to the new situation.

28 June 2017

Update June 2017

I'm still fixing bugs and I'm still using GB32 on a daily basis. Sometimes fixes are ready to go public, this is such a day. The most important update is the GfaWin23.Ocx runtime, but the IDE also got a swing. Anyway go to the Download page for more information.
Don't hesitate to mail me when you have something to say about these updates. You can use the comments section below this post or mail me directly. You know where to find me.

Also: I updated the blog on UNICODE conversion. There was a bug in the Ansi To Unicode conversion function WStr().

25 May 2017

New Group/Mailinglist

A new mailinglist has been created: the  GFAGroup. This works the same as the previous Mailinglist that went down at the beginning of this year.
This mailinglist/group is provided by Google and thus requires a Gmail-account. After you have signed in with Google using your Gmail-account you can join the list. Although it is a group, it works much in the same way as the previous mailinglist. Questions and answers are posted through e-mail.

12 May 2017

ANSI, UNICODE, BSTR and converting

Update 28-06-2017 - Conversion from Ansi to Unicode: WStr() function.
For some reason the WStr() routine contained a stupid bug (that I now have fixed).

More info: The number of bytes to read from a BSTR-address was wrong. GFA-BASIC always uses the SysAllocStringLen(Null, lenbytes) when allocating COM String memory. The BSTR returned is preceded by a 32-bits value specifying the BSTR's number of bytes, not the number of characters! This is exactly the value needed when reading the BSTR-bytes into a String datatype using StrPeek(). So, the function should have been: StrPeek(BSTR, {BSTR-4}), see the updated function below.

Another point of confusion was about the number of terminating null-bytes that WStr() returned. The StrPeek() function in WStr() only copies the UNICODE characters from the BSTR to a String, without the two null-bytes that secretly follow a BSTR string. As a result, the UNICODE characters copied to the String datatype are followed by only one (1) null byte; the terminating null byte that each String secretly gets.
When a String of UNICODE characters is to be passed to a Wide API function, two null-bytes must be added 'manually'.
w$ = WStr("GFABASIC") + #0#0 ' assign two nullbytes

The post as it was:
In the previous post I discussed UNICODE versus ANSI in the ANSI-based GFA-BASIC. Basically, GB doesn’t support UNICODE because it expects 1-byte characters where strings are used. In UNICODE each character occupies 2 bytes and allows more than 256 characters. Conversion ANSI to UNICODE is ok, but conversion from UNICODE to ANSI might lead to a loss of characters with a value above 256. But there is more: Variants and BSTRs.
The introduction of COM in GB required the provision of a new data type, the Variant. The Variant is a 16-byte data type that holds data and a value that specifies the type of that data(LONG, CARD, DOUBLE, etc). A Variant can also be used to store (safe-) arrays, a specific COM array type, and BSTRs, special UNICODE strings. So to understand the String and BSTR/Variant in detail ….

How a String is stored
Because a BSTR is much like a GFA-BASIC String data type, I’ll first tell how a GB String is stored. You could skip this part if you already know.
Declaring (Dim) a String-variable introduces a name for a location. The String-variable itself requires four bytes to store a pointer to dynamically allocated memory for the characters. The declaration and assigning a location is handled by the compiler, the rest happens at runtime: assigning or initializing. When the String-variable is initialized a call to malloc() reserves memory for all its characters with an additional 5 bytes. The first 4-bytes are reserved to store the length of the string and the last byte for the null-byte (not included in the length value). After allocating and copying the characters, the address of the first character of the string is stored at the variable’s location, a 32-bits address or pointer.
Global a$       ' 32-bits location(=0) in data or stack
a$ = "GFABASIC" ' assign pointer (address) to location
l = Len(a$)     ' address <> 0 return length {address-4}
Clr a$ : a$= "" ' free memory, set locations to 0
- String in memory: [xxxx|cccccc…c|0]
- Initially, the variable is a null pointer, the contents of the variable’s location is 0.
- String variable points the address of the first character c.
- Length is stored in position address – 4, and does not include the terminating zero.

Obtaining the string’s length is a 2-step process. First the variable is tested for a non-null pointer and than the value of the preceding 4 bytes (string-address – 4) is returned.
- Clearing a string (or assigning an empty string “”) will free the allocated memory and reset the variable’s contents to 0.

BSTR in GB
GB does not provide a data type BSTR, but it provides limited support of hidden BSTRs to pass and obtain BSTR-strings to and from COM objects. GB handles the conversion and memory allocation for BSTRs, but it does not provide string-manipulation functions for BSTRs, or even BSTRs in Variants. More on this below.
BSTR variables are always temporary, hidden local variables used to communicate with COM properties/methods that take or return BSTR arguments. These hidden BSTR variables are always destroyed when leaving a subroutine. Even the Naked attribute won’t prevent the inclusion of the termination code.
BSTR strings are COM based strings. They are allocated from COM-memory and consequently the memory can be managed by both the provider of the COM-object provider and the client. That is the first difference. Next a BSTR contains UTF-16 coded wide characters, which I discussed in ANSI and UNICODE. The way COM stores a BSTR is much the same as GB stores a String variable. In fact, a BSTR is 32-bits location that stores a pointer to dynamically allocated memory with UNICODE formatted characters. The length of the BSTR is stored In front of the BSTR, again like GB’s String data type.

Use Variant for BSTR
Although, GB provides hidden support for BSTRs, the only way to get access to a BSTR is by using a Variant. The following example assigns a GB-String to a Variant. At runtime the code allocates a BSTR by calling SysAllocStringLen(0, Len(GB-String)) followed by copying the converted GB-String to the returned address. The address of the BSTR together with its data type is stored in the Variant. When the Variant variable goes out of scope, the BSTR from the Variant is released through a call to SysFreeString(address).
Dim vnt1 = "Hello"
Now it gets interesting. After GB invoked the SysAllocStringLen() COM API, it converts the ANSI string to UNICODE using a private conversion routine interspersing zero’s between the characters see ANSI and UNICODE. GB does not turn to the MultiByte*() APIs Windows provides, because GB supports ANSI characters only. In the conversion process to UNICODE no characters will be lost and the private function is extremely fast.
An optimized UNICODE conversion function
This knowledge makes it possible to obtain a UNICODE-string (not a BSTR) from a String argument through our own optimized conversion routine. Note
  • A UNICODE string is required if you want to use the Wide version APIs.
  • A UNICODE string does not have a length field in front of it. It is not a BSTR. It only specifies how much bytes a character occupies (2).
  • It’s memory is managed by the program through malloc() – no COM memory - and it ends with two null-bytes (although it seems 1 is ok as well).
  • The converted ANSI argument is placed in a String only because it is a convenient data type to store consecutive data.
The function makes use of the BSTR allocation and conversion functionality of the Variant.
(The $Export is there because it comes from a .lg32 file).
Function WStr(vnt As Variant) As String Naked ' Return UNICODEd string
  $Export Function WStr "(AnsiString) As String-UNICODE Naked"
  Dim BSTR As Register Long
  BSTR = {V:vnt + 8} ' BSTR address at offset 8
  Return StrPeek(BSTR, {BSTR - 4}) ' <- 28-06-2017="" font="" updated="">
EndFunc
1. A function very well suited for the Naked attribute, because it does not contain local variables that contain dynamically allocated memory that would otherwise require explicit release code.
2. The argument of the function is ByVal As Variant. This forces the caller (calling code) to create a Variant and than pass it by value by pushing 16-bytes (4 DWords) on the stack. Whether the Variant is passed by value or by reference, the calling subroutine is responsible for freeing the BSTR stored in the Variant. However, ByVal is interesting because …
3. The GFABASIC-compiler provides a hidden optimization when you pass a literal string to a ByVal As Variant. A ByVal Variant requires16 bytes to push on the stack, but the UNICODE characters the Variant points to are already converted at compile time. Therefor the following call is extremely efficient:
Dim t$ = WStr("GFABASIC")
The GFA-BASIC compiler stores the literal string “GFABASIC” as a UNICODE sequence of bytes (2 per character) and does not need to allocate (COM) memory and convert at runtime. This also relieves the caller from releasing the BSTR-COM-memory, so the calling function doesn’t need to execute Variant destruction code.
Assigning a UNICODE formatted string this way, is almost as efficient as initializing a String with an ANSI literal string. It only takes a few cycles to call and execute the WStr() function.
4. The caller provides the String variable to store the return value of the function. That is the function’s ‘local variable’ WStr is silently declared in the calling subroutine. The hidden string is passed as a ByRef variable to the function. The return value (String) is directly assigned to the hidden variable. If an exception would occur in function Wstr() the termination code of the caller will release the hidden WStr string variable. (Therefor Naked is perfect for this function: it doesnot need to provide explicit release code.)
5. Inside the function you can see two more optimizations. First the local Long variable that stores the address of BSTR is a register variable; no stack memory and copying required. The other optimization is the Shl 1 expression that multiplies the length of the BSTR by 2. This results in an integer asm add eax, eax instruction, rather than a floating point multiplication. Also a significant optimization.
6. Other mathematic operations like V:vnt+8 and BSTR-4 are relative address operations and are properly compiled into indirect addressing instructions. So, no chance here to optimize.
I went in some detail to explain the function hoping you’ll find it useful. I hope to tell more about the way the compiler constructs subroutines and performs optimizations.

10 May 2017

Error free using a library

I use libraries a lot, but there are few things that make using them a bit obscure and may lead to the non-descript message "Load lg32 error: filename.lg32" in the status bar and debug window. In addition, a compiled library may produce strange runtime errors.

BUG - Runtime errors
When you run a project which includes a library it may generate strange, seemingly unrelated error messages. In particular the error "Hash Internal Error 1/2 (Version?)" pops up regularly. The reason for runtime errors inside the code of a library is a bug(!) in applying the setting for Branch Optimizations.

For a lg32 file, GFA-BASIC wants to apply the Full Optimization for Exe setting on the compiling process. However, it is never applied at all, because the code applies this setting in the wrong place, after the code is compiled ;). Consequently, the compiler switches to the trackbar/slider setting from Branch Optimizations.
This is a bug from a long time ago and it is simply never tested properly.

In general objectcode generated for a lg32 file is position independent, it differs from code generated for  EXE (and GLL files). Therefor, the lg32-generated code for the jump-tables for Switch/Case statements and On n GoSub/Call statements are wrong (this is also true for a GLL, for which I always use the default settings). 

The only setting that work flawlessly is the None setting of the slider in 'Branch Optimizations' and uncheck the 'Full Optimization' check box. 


A lg32-file has to be compiled using the default settings for Branch Optimizations.
The slider must be set to the first position (None) and the checkbox Full Optimization for Exe must be unchecked.

Note - The slider setting is applied to compiling code in memory, independent of the required output file type (EXE, LG32, or GLL). The most right position (Full) is exactly the same as checking the Full Optimization for Exe - box. This way you can test fully optimized code inside the IDE.

Note - The branch optimizations of the compiler do not lead to remarkable performance results. These days with fast CPUs and large caches performance increase is hard to provide, the only real performance increase is accomplished by using Naked procedures. Remember however, Naked procedures do not include termination code and do not allow exception handlers.

The $Library statement
The $Library statement loads a lg32 file into memory. But sometimes it cannot locate the lg32 file. The IDE code to find a lg32 file is a bit complicated. In some conditions you may omit the extension and in others you cannot. It depends on the inclusion of a path in $Library statement. For instance, you may include a relative path (relative to the current directory, mostly the g32-file directory, but not necessarily), but than the extension must be provided. It's all a bit incoherent. But there is a solution that always works correctly. That is - the library is always located properly.

Solution for load errors
This solution adds more functionality to the $Library statement and so it complements the current functionality. You must add a (new) register entry to the GFA/BASIC key in the HKEY_CURRENT_USER/Software setting. The key must be named "lg32path" and the value can contain multiple full paths separated by commas. (The value uses the same syntax a the PATH environment variable).

New key: "lg32path", REG_SZ
Value: "C:\GFA\Include, D:\GFA\MyLibs"

Have fun with lg32 file.

ANSI and UNICODE

Updated 21-05-2017: Sample code at the end of the post.

GFA-BASIC 32 only supports ANSI strings, not UNICODE… What exactly does that mean?

ANSI-strings consist of a sequence of bytes – the characters of a string – where each byte represents a character. This allows for 256 different characters because a byte can contain a value between 0 and 255. Restricting strings to bytes limits the number of possible – mostly for not western languages – characters. To allow for more characters each character in a string must somehow occupy more than one byte. In Windows, each Unicode character is encoded using UTF-16 (where UTF is an acronym for Unicode Transformation Format). UTF-16 encodes each character as 2 bytes (or 16 bits). UTF-16 is a compromise between saving space and providing ease of coding. It is used throughout Windows, including .NET and COM.

In UNICODE the lower 256 values represent the same characters as in ANSI, but they are stored as a sequence of 16-bits integers. Additional characters are represented with higher values above 256. In UNICODE the first 256 characters have the same value as in ANSI, but each character requires 2-bytes of storage. When you convert an ANSI string to UNICODE it becomes twice the size of the ANSI string.
Let’s see what this means from a GFA-BASIC perspective.

ANSI in a GB String
When you store a literal string like “GFABASIC” in a String (ANSI, 1-byte representation), the string is filled with 8 bytes of (hexadecimal) values 47 46 41 42 41 53 49 43.

a_t$ = "GFABASIC"   ' 47 46 41 42 41 53 49 43

The same string can be created by using Chr$() and populate these byte values. (A more general approach would be to use the Mk1$() function):

a_t$ = Chr($47, $46, $41, $42, $41, $53, $49, $43)
a_t$ = Mk1($47, $46, $41, $42, $41, $53, $49, $43)

GFA-BASIC’s string functions expect ANSI strings only, and by default GB only communicates with the ANSI version of the Windows API functions. With a little knowledge you can do more.

Windows APIs are UNICODE
Windows is an UNICODE system. When a Windows API takes a string as an parameter, Windows always provides two versions of the same API. It provides an API for ANSI stings and an API for UNICODE strings. To differentiate between ANSI and UNICODE respectively, the names of the API function either ends with uppercase A - for ANSI parameters - and uppercase W for the version that accepts or expects UNICODE. A typical example would be the  SetWindowText() API which comes in two flavors SetWindowTextA() and SetWindowTextW().
The GFA-BASIC’s built-in APIs are the ones that map to the functions that end with A. So the GB function SetWindowText() maps to the SetWindowTextA() function.

UNICODE in a GB String
By default, when you declare a literal string in your source code, the compiler turns the string's characters into an array of 8-bit data types, the String. You can not – in the same way - declare a literal UNICODE string. To assign a sequence of 2-byte characters you’ll need to use different methods. For instance by populating a String by hand. In the example above it only takes one change to create a UNICODE array of characters. Simply change the Mk1() function to Mk2():

u_t$ = Mk2($47, $46, $41, $42, $41, $53, $49, $43) + #0

Now each character occupies 2 bytes and has become UNICODE formatted, because it encodes each character using UTF16, interspersing zero bytes between every ASCII character, like so

u_t$ = Chr($47,0, $46,0, $41,0, $42,0, $41,0, $53,0, $49,0, $43,0) + #0

A GB String data type always adds a null-byte (only one) to zero-terminate the sequence of characters. Since the above assignments are GB controlled, the strings end with only one null-byte. UNICODE should end with two null-bytes. You should explicitly add an additional null at the end of the string to properly create a UNICODE string.

UNICODE is not BSTR
Note that we simply created a piece of memory to store characters in 2-bytes rather than in 1-byte. The String memory is allocated from the program’s global heap and this memory is only guarded by GB. Although the string contains UNICODE it is not a BSTR. A BSTR is a COM defined string type and is allocated from COM-memory. Both the client (a GB-program) and the provider/server have access to the same COM-memory.
When a string is assigned to a Variant, which supports BSTRs only, GB allocates COM string memory and converts the ANSI string to UNICODE.

Using pure UNICODE
The GFA-BASIC string-functions use a 1-byte character indexing system. However, you can overcome this limitation for 2-byte formatted strings and apply GB String-functions when you multiply the index and length parameters by 2. For instance:

u_t$ = Left(u_t$, ipos * 2) + #0
u_t$ = Mid(u_t$, ipos * 2, nBytes * 2) + #0

You can pass these UNICODE formatted strings to APIs that end with uppercase W. To introduce the wide character APIs to your code you must Declare them explicitly. For instance, this code displays u_t$ in the client area of a window.

Declare Function TextOutW Lib "gdi32.dll" Alias "TextOutW" ( _
  ByVal hdc As Handle,        // handle to DC _
  ByVal nXStart As Int,       // x-coordinate of starting position _
  ByVal nYStart As Int,       // y-coordinate of starting position _
  ByVal lpwString As Long,    // character string _
  ByVal cbString As Long      // number of characters _
  ) As Long

Form frm1
TextOutW(frm1.hDC, 1, 1, V:u_t$, Len(u_t$) / 2)

Remember one thing. Windows uses UNICODE only, including fonts. Whether you use TextOutW or TextOutA (as Text does), all output is performed using UNICODE fonts. The TextOutA first converts the text to UNICODE and than invokes TextOutW. By providing a UNICODE formatted to a W-version API only skips the conversion from ANSI. See below for an example.

Obtaining UNICODE text from Windows APIs
Since XP, all Windows APIs taking or returning a string parameter are implemented in UNICODE only. The ANSI version of these functions translate (or convert) the ANSI strings to and from UNICODE format. Well, GB only handles ANSI strings; it passes and retrieves ANSI strings to and from Windows APIs. What is the consequence of this restriction?

When an ANSI string is passed to an A – version of an API, the Windows API will convert the string to UNICODE and than invoke the W-version of that API. There is no loss of information in this conversion. All ANSI characters are converted to UNICODE by expanding the string with zero’s as explained above. The string-size is doubled, but that’s all.

The other way around is more problematic. A Windows API may return or provide a UNICODE formatted string containing non-ANSI characters, characters with a 2-byte value above 256 … When the A-version of the API is used to retrieve text, Windows will do the UNICODE-to-ANSI conversion on behalf of the A-version of that API and the characters with a higher value of 256 will be lost.
This won’t be a problem if the ANSI-based GFA-BASIC program is used in languages no other than Latin (English) alphabets. In other languages the Windows system accepts more characters and the text won’t be properly returned to the GFA-BASIC String data type.
When your program needs UNICODE input or use UNICODE strings you should explicitly declare all the required wide APIs. In addition, you might also need W replacements for the GDI text-out functions. To use the GB string functions, you should remember to multiply or divide all integer arguments with 2.

Displaying UNICODE glyph characters (updated 21-05-2017)
Windows 10 includes and uses a new graphical font: Segoe MDL2 Assets. This sample shows how to obtain the glyphs form the font icons for use in GB.
In the accessory Special Characters select Segoe MDL2 Assets and than select a graphical character. Write down the 16-bit value from the box at the bottom and assign it to a String. Here the value for the picture for saving is 0xE105.

image

Form frm1
ScaleMode = basPixels   ' by default
SetFont "Segoe MDL2 Assets"
' Display UNICODE string "GFABASIC"
Dim u_t$ = Mk2($47, $46, $41, $42, $41, $53, $49, $43) + #0
TextW 1, 1, u_t$

' Get a Picture Object from a glyph.
' Char-value from 'Special Characters' Accesorry
Dim hBmp As Handle, p As Picture
Dim size As SIZE
u_t$ = Mk2(0xE105)          ' the Save-glyph
TextW 1, 31, u_t$           ' show it
TextSizeW(Me.hDC, V:u_t$, Len(u_t$) / 2, size)
Get 1, 31, 1 + size.cx, 31 + size.cy, hBmp   ' a GDI-handle
Put 50, 1, hBmp             ' and test it
Set p = CreatePicture(hBmp, True)  ' into a Picture
PaintPicture p, 70, 1       ' and test it
Do
  Sleep
Until Me Is Nothing

Proc TextW(x As Int, y As Int, wstr As String)
  ' Assume Scalemode = basPixels, ScaleLeft=0, and ScaleTop=0
  TextOutW(Me.hDC, x, y, V:wstr, Len(wstr) / 2)
  ' If AutoRedraw == True draw on bitmap.
  If Me.hDC2 Then TextOutW(Me.hDC2, x, y, V:wstr, Len(wstr) / 2)
EndProc

Declare Function TextOutW Lib "gdi32.dll" Alias "TextOutW" ( _
  ByVal hdc As Handle,         // handle to DC _
  ByVal nXStart As Int,        // x-coordinate of starting position _
  ByVal nYStart As Int,        // y-coordinate of starting position _
  ByVal lpwString As Long,     // character string _
  ByVal cbString As Long       // number of characters _
  ) As Long
Declare Function TextSizeW Lib "gdi32.dll" Alias "GetTextExtentPoint32W" ( _
  ByVal hdc As Handle,        // handle to DC _
  ByVal lpString As Long,     // text string _
  ByVal cbString As Int,      // characters in string _
  ByRef lpSize As SIZE        // string size _
  ) As Long
Type SIZE
  - Long cx, cy
EndType

A few notes about this sample (compared to the previous version).

  1. The Segoe MDL2 Assets font is not a fixed-sized font (the LOGFONT member lfpitchAndFamily is not FIXED_PITCH). However, the glyphs in the font all have the same format. To obtain the size of a glyph-character we cannot use the ANSI GB functions TextWidth() and TextHeight(), since they cannot return the size of a 2-byte character. Therefor the inclusion of the TextSizeW() function.
  2. To conform to GB’s scaling the TextOutW function should take coordinates in the current ScaleMode and the text-output should obey the ScaleLeft and ScaleTop settings. In this sample TextW simply draws on a pixel resolution scale and relative to (0,0), located at the top-left of the client area. Note however that Get and Put actually use the current scaling. Be sure to use the same ScaleMode for both GB commands as API functions. (As long as B= basPixels (default scaling in GFA-BASIC, VB uses twips, do not confuse the both).

Finally, the return values of ScaleLeft and ScaleTop are wrong (al versions below Build 1200). Hope to update the GfaWin23.ocx as soon as possible).

02 March 2017

A Dynamic Array Library

James Gaite created an impressive extension to the standard GFA-BASIC 32 array functionality. In response to comments made in a thread on the GB32 forum, he has spent some time trying to compile some basic dynamic objects that will work within a UDT, as well as making GFA-BASIC's handling of arrays in variants a bit more flexible.

Download
The results are contained in the zipped library file containing a compiled library file (lg32) and a with a brief help file. For easy access you should copy the lg32-file into the source directory of the project you are working on. Including a library file in your project forces the IDE to search for that file in the current working directory first.
Please note that this is a work in progress, as can be seen by the version number.
Dynamic Array Library (version 0.23 Build 10) : DynamicArrays.zip

Main features (but not all):
  1. A Hash table (THash) that:
     - can be used in User-defined Types and Arrays, and can be passed ByRef to functions and subroutines;
       - can 'remember' sort preferences and add all new keys accordingly.
  2. . A Dynamic Variable-length String Array (DSArray or DSA) which:
       - can be used in User-defined Types and Arrays and be passed to all routines;
       - can 'remember' sort preferences and add all new additions accordingly;
       - has basic find, find/replace and find/remove functions.
  3. A basic Variable-length String Variable that can be used in User-defined Types.
  4. New commands and functions for Redim'ing and setting values in a dynamic array in a variant.
Please post any questions (or bugs you might discover) to this thread on the GB32 forum http://gb32.proboards.com/thread/117/dynamic-variables-arrays or as comments attached to this blog article.

20 February 2017

Tip: Using mt.exe to include a manifest

A tip from Peter Harder on how to use the mt.exe tool included with the Windows SDK.


The GFA-Basic editor can not include a *.manifest file into a created *.exe file. However, the Microsoft Windows Software Development Kit (SDK) contains a file named "mt.exe" that can
include the *.manifest file after creating the *.exe file.

I uploaded an examples that includes also the mt.exe and a batch file that shows how to call the mt.exe:

Just follow the instructions in the readme.txt.
For a complete description of mt.exe tool use Google and search for "mt.exe".