15 July 2011

COM (OOP) in GB32 - IUnknown (1)

In this part I'll show you the layout of a COM class as it could be implemented in GFA-BASIC 32. Note that we restrict ourselves to a minimalist COM object that supports the IUnknown interface only. We will not yet create properties and methods.
The GFA-BASIC 32 approach is loosely based on the article series COM in plain C by Jeff Glatt, specifically Part 1 and Part 2. However, in the first step we are not going to use type libraries, we are not going to store the COM object in a DLL, and we are not going to define another COM object that creates the one we define. COM is merely a binary standard; it dictates how to layout an array of function pointers and where to put the address of this array (of function pointers). It also dictates how to add reference counting. To comply to this standard a COM object must at least contain three pointers to pre-defined functions, also known as the IUnknown interface. In GFA-BASIC 32 this could look like this:
// IUnknownBlog.g32 17.07.2011
Debug.Show

// Our COM object: an implementation of IUnknown
GUID IID_IUnknownImpl = 9578fdab-97cb-4322-99e4-699abd26be1d
Type IUnknownImpl
  lpVtbl As Pointer IUnknownImplVtbl
  RefCount As Int
EndType

// VTABLE (an array of function pointers)
Type IUnknownImplVtbl
  QueryInterface As Long
  AddRef As Long
  Release As Long
EndType
Static IUnknownImplVtbl As IUnknownImplVtbl
With IUnknownImplVtbl
  .QueryInterface = ProcAddr(IUnknownImplVtbl_QueryInterface)
  .AddRef = ProcAddr(IUnknownImplVtbl_AddRef)
  .Release = ProcAddr(IUnknownImplVtbl_Release)
EndWith

// First create a heap allocated instance
// of our implementation of IUnknownImpl
Dim pIUnk As Pointer IUnknownImpl
Pointer pIUnk        = mAlloc(SizeOf(IUnknownImpl))
Pointer pIUnk.lpVtbl = *IUnknownImplVtbl
pIUnk.RefCount = 1

Dim obIUnk As Object
{V:obIUnk} = V:pIUnk
Trace obIUnk


Dim o As Object // assign to other
Set o = obIUnk  // AddRef() call
// two calls to Release
/*** END ***/

GUID IID_IUnknown      = 00000000-0000-0000-c000-000000000046
Global Const E_NOTIMPL = 0x80004001
Global Const E_NOINTERFACE = 0x80004002
Declare Function IsEqualGUID Lib "ole32" (ByVal prguid1 As Long, ByVal prguid2 As Long) As Bool

Function IUnknownImplVtbl_QueryInterface( _
  ByRef this As IUnknownImpl, riid%, ppv%) As Long Naked
  Trace Hex(*this)
  {ppv} = Null
  If IsEqualGUID(riid, IID_IUnknownImpl) || _
    IsEqualGUID(riid, IID_IUnknown)
    {ppv} = *this
    IUnknownImplVtbl_AddRef(this)
    Return S_OK
  EndIf
  Return E_NOINTERFACE
EndFunc

Function IUnknownImplVtbl_AddRef(ByRef this As IUnknownImpl) As Long Naked
  Trace Hex(*this)
  this.RefCount++
  Return this.RefCount
EndFunc

Function IUnknownImplVtbl_Release(ByRef this As IUnknownImpl) As Long Naked
  Trace Hex(*this)
  this.RefCount--
  If this.RefCount == 0 Then ~mFree(*this)
  Return this.RefCount
EndFunc
Copy it to a new GFA-BASIC 32 application and save as IUnknownBlog.g32. Try to run it; it should compile and run flawlessly. Our first minimalist COM object/class has been defined and is up and running. Of course it doesn't do anything, but the we have an object that integrates with GFA-BASIC 32 and fully complies to the COM binary standard.
Assign to Object
Let us take a brief look at the code. Since this project isn't meant for the beginner, I'll walk you through it in big steps.
The Object data type holds a pointer to a piece of memory of at least 4 (lpVtbl) bytes. Mostly it contains another integer for a reference count. In GFA-BASIC 32 the Ocx variables always define their count in the second slot, we will use that as well.
To put a memory address in an Object variable we usually use Set obj2 = obj2. GFA-BASIC 32 checks for two proper COM object types at compile-time. Since we have mAlloc-ed address only this syntax wouldn't work. We must write it to Object directly. For the same reason we set the RefCount to 1 by hand.
The array of functions (VTABLE) is stored in a Type and shared with all instances of our custom COM object. Therefor a static (global) variable is used and initialized once. Each new COM object should hold the vtable-address in its lpVtbl member.
_QueryInterface, _AddRef, and _Release
The application COM functions _QueryInterface, _AddRef, and _Release are never called directly, but always by GFA or another COM object. They clutter up our application code and are in fact just boiler plate code. We must do something about that, for instance put them in a $Library. Also note the Naked attribute. These functions are never executed in the context of the application and need no TRACE code and Try/Catch-exception handler. They should be as naked as possible to gain the best performance.
The Trace *this commands in the code are to verify the address passed. Also note the type of the this pointer. COM passes the address of the COM object by reference and by adding the correct type into the function declaration we can access its members directly. All other parameters are simple placeholders for addresses we don't use or pass on.
In the next part we will implement an IDispatch object and try to integrate the COM code more into GFA-BASIC 32.

24 May 2011

Passing a Hash to a procedure (III)–Workaround

This part three in a series about the GFA-BASIC 32 Hash data type. In this part I discuss a workaround to use Hash procedure parameters and some peculiarities of the hash data type. However, you might like to read the previous posts first.
The workaround
A compiler bug blocks the the Hash Xxx commands for a Hash data type passed by-reference. On the other hand, the hash [ ] - operator syntax works well. The Hash Xxx commands work correctly for a local Hash data type, the one that is not passed as an argument to the procedure. The logical conclusion is to declare and use a temporary local Hash of the same data type and then somehow 'assign the hash parameter to the local hash'.
GFA-BASIC 32 has a two constructions to do just that:
  • Declaring a variable p As Pointer To data-type and than use Pointer(p) = address command to assign the pointer (<=> variable) an address.
  • Swapping the descriptors of the Hash variables: Swap hs1, hs2.
We can skip the first option. It should work but it doesn't, the compiler refuses the statement:
Dim hs As Pointer Hash Int
The swap method should work as well, but again it doesn't. The compiler doesn't know what to do with Swapping two Hash variables. However there is light at the end of the tunnel, Swap works for all other GFA-BASIC 32 data types including the UDT (Type). And that's the one we are going to use to Swap the hash variables, hence the procedure SwapHash().
Local hsInt As Hash Int
hashtest(HB[])

Proc hashtest(ByRef HS As Hash Int)
  Dim ths As Hash Int   ' temporary Hash
  SwapHash( *HS, *ths ) ' swap descriptors

  ' Now ONLY use ths[]
  Hash Add ths["dd"], 7
  ths ["aa"] = 8
  ' ...

  SwapHash( *HS, *ths ) ' swap back
EndProc

Proc SwapHash(ptrHash1%, ptrHash2%) Naked
  Local hsdesc1 As Pointer HashDesc
  Pointer(hsdesc1) = ptrHash1
  Local hsdesc2 As Pointer HashDesc
  Pointer(hsdesc2) = ptrHash2
  ' Make sure the types are the same:
  Assert hsdesc1.pType == hsdesc2.pType _
    ' SwapHash() - Hash type mismatch

  Swap hsdesc1, hsdesc2

  Type HashDesc         ' The Hash Descriptor
    pHashData As Long   ' pointer to memory
    pType As Long       ' Hash data type
  EndType
EndProc

19 May 2011

Passing a Hash to a subroutine (II) - The Descriptor

In the previous post we discussed the GFA-BASIC 32 compiler bug when a Hash variable is passed to a subroutine. The compiler produces incorrect code for the Hash Xxx commands, but it works well in case hash-operators are used.
The situation is like this:
Sub q(hs As Hash String)
  ' Errorneous:
  Hash Add hs["mykey"], "Entry1"
  Hash Add hs[2], "Entry2"
  ' OK:
  hs[3] = "Entry3"
  Print hs[%]
EndSub
Note that the Hash variable hs is passed by-reference (implicitly due to the use of the keyword Sub).

Hash descriptor
A Hash variable is actually a 8-byte structure (user-defined type) containing two LONG integers. The Hash variable references the address of this hash-type, called the descriptor. The Hash-descriptor is defined as follows:
Type HashDesc
  pHashData As Long
  pType As Long
EndType
After declaring a hash variable (Dim hs As Hash type) the first long integer of the descriptor is Null indicating that the hash didn't allocate any memory. The second long contains a value indicating the GFA-BASIC 32 primary data type used to store values in the hash table. The data type could be Int, Byte, String, Variant, etc. Every GFA-BASIC 32 data type can be used, with the exception of UDT types. As an example the next code declares a Hash to store (long) Integer values and displays the descriptor:
Local HB As Hash Int
Local hs_desc As Pointer To HashDesc
Pointer(hs_desc) = *HB
Debug "HASH DESCRIPTOR"
Trace Hex(*hs_desc)
Trace Hex(hs_desc.pHashData)    ' = 0
Trace Hex(hs_desc.pType)        ' = $4018
The variable HB references the starting address of the 8-byte descriptor. As with any GFA-BASIC 32 variable that uses a descriptor, the address can be obtained using ArrPtr(HB) or *HB. Using the preceding code you can inspect the descriptor.

Hash MUST be declared as ByRef 
When a hash variable is passed to a subroutine, its descriptor address is always passed by reference (whether or not ByRef or ByVal is used in the procedure header). BUT, the Hash parameter MUST be ByRef, otherwise the code generated for the procedure will use a wrong address. A ByVal Hash parameter sets the compiler in error mode without returning to the editor. As a consequence all code following might (will actually) be compiled wrongly.
When the Hash parameter is passed correctly by-reference the compiler still generates incorrect code for the Hash Xxx commands, like Hash Add, Hash Remove,...
The advise remains the same, don't pass Hash tables around, but use them as global variables only.

18 May 2011

Hash passing to a subroutine (I) - The bug

As mentioned in the (English) help file the handling of a Hash type argument is erroneous. The compiler produces incorrect code when the local Hash parameter is used in Hash commands like Hash Add, Hash Remove, etc. As far as I know all Hash Xxx commands suffer from the compiler bug. In contradiction with this behavior is the handling of the Hash–operator code. When a Hash data type is passed to a subroutine the compiler produces correct code for all [ ] hash-operator code. The following code illustrates the situation:
Sub q(hs As Hash String)
  ' Errorneous:
  Hash Add hs["mykey"], "Entry1"
  Hash Add hs[2], "Entry2"
  ' OK:
  hs[3] = "Entry3"
  Print hs[%]
EndSub

What is happening?
Due to the implicit Sub by-reference passing the compiler puts the address of the Hash argument on the stack when invoking q( Hs[] ). This is the first step performed by the compiler when a call to q() must be produced and it turns out to be ok. The second step performed by the compiler is when the subroutine q() is compiled. Due to the by-ref passing the compiler must set the local hs variable to the address put on the stack by the code that invoked the call. That way the local variable hs points to the same data as the hash passed to q().
However, somehow the compiler messes up with the Hash Xxx commands. When inspecting the assembly code for q() it can be seen that for the hash-operator instructions the correct Hash address is passed to the runtime library functions. Also, it is clear that the compiler passes the incorrect address in case the Hash Xxx commands are used.

What is to be done?
At this moment this bug cannot be fixed, so we either need a workaround or we avoid the use of Hash variables as a subroutine argument at all. The second option isn't the worst of both, because due to their nature Hash tables are mostly used globally and don't need to be passed to subroutines at all. Of course, a hash table could be used to store temporary results (regular expression commands), but in that case the Hash is usually declared locally and not passed to any subroutine.
Advise: Don't pass Hash variables to subroutines.
In the next post I will discuss a workaround together with the layout of the GFA-BASIC 32 Hash data type.

14 May 2011

Visual Styles, Ocx Button, and ForeColor

The BUTTON window class is the base windows class for the Command, Frame, Option, and CheckBox Ocx controls. These different OCX controls are created by passing a special BUTTON window style (BS_) in the style parameter of the CreateWindowExA() Windows API.

GFABASIC 32 Ocx BUTTON window Style
Command BS_PUSHBUTTON, BS_DEFPUSHBUTTON
CheckBox BS_CHECKBOX, BS_AUTOCHECKBOX, BS_3STATE, and BS_AUTO3STATE
Option BS_RADIOBUTTON and BS_AUTORADIOBUTTON
Frame BS_GROUPBOX

As you can see the names of the Ocx controls conform to VB control names, which was one of the main changes in the GFA-BASIC 32 development. Another thing adapted from VB is the ability to change the controls behavior and appearance through properties. These are either changed in the Properties sidebar in the form-editor or in source code using the COM properties interfaces. In this case it doesn't matter how the appearance is changed, but how GFA-BASIC 32 implements the VB compatible behavior.

First of all, the implementation is VB compatible, but totally different. In most case (if not all) the GB32 implementation of properties and methods is much faster and cleaner. VB and VB.NET are snakes compared to the fast performance of GB32 applications. Currently, performance doesn't count as strong as in previous decades, but still. Maybe with Windows 7 for Tablets performance and size counts again. Who knows?

BUTTON drawing
The BUTTON control sends the WM_CTLCOLORBTN message to the parent window of a button when the button is about to be drawn. By responding to this message, the parent window can set a button's text and background colors. In GFA-BASIC 32 applications without the Visual Styles enabled this works as documented. However, when an application is accompanied with a manifest the new Visual Styles are applied and take precedence over the GFA-BASIC implementation.

When the Windows OS encounters a manifest file, the OS uses a different version of the common control library and globally subclasses all Windows controls, including the 'old standard' controls like the BUTTON class. GFA-BASIC 32 wasn't developed when the Visual Styles were around and uses a speedy drawing algorithm based on the classic behavior of the BUTTON class drawing. As a result, the Visual Styles improvement is not applied to the text color of the BUTTON class.

ForeColor cannot be set
Thus, the text color of Command, CheckBox, Option, and Frame Ocx cannot be changed by changing their ForeColor property when using a manifest file.
When you really need to change the text color I'm not sure how to proceed, so you are your own there...

10 May 2011

ShowFolders Sample

The ShowFolders method of the CommDlg Ocx object keeps raising questions. Therefor an example of an extended browse for folders dialog box.

The API behind the dialog box of the ShowFolders method is the SHBrowseForFolder() Shell function. Actually, there are two styles of dialog box available. The older style is displayed by default and is displayed using the BIF_ constants specified in the previous blog CommDlg.ShowFolders. However, the list of constants is a subset of the total number of flags. To specify a dialog box using the newer style, you should pass the BIF_USENEWUI flag in the ShowFolders method.

image

The newer style provides a number of additional features, including drag-and-drop capability within the dialog box, reordering, deletion, shortcut menus, the ability to create new folders, and other shortcut menu commands. Initially, it is larger than the older dialog box, but can be resized by the user.
The dialog box can be displayed using the following code.

Public Const BIF_RETURNONLYFSDIRS   = 0x0001
Public Const BIF_DONTGOBELOWDOMAIN  = 0x0002
Public Const BIF_STATUSTEXT         = 0x0004
Public Const BIF_RETURNFSANCESTORS  = 0x0008
Public Const BIF_EDITBOX            = 0x0010
Public Const BIF_VALIDATE           = 0x0020
Public Const BIF_NEWDIALOGSTYLE     = 0x0040
Public Const BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX)
Public Const BIF_NONEWFOLDERBUTTON  = 0x0200
Public Const BIF_BROWSEFORCOMPUTER  = 0x1000
Public Const BIF_BROWSEFORPRINTER   = 0x2000
Public Const BIF_BROWSEINCLUDEFILES = 0x4000

Global cd As New CommDlg
cd.Title = "GFA-BASIC32 ShowFolders Demo"
cd.ShowFolders BIF_USENEWUI
If Len(cd.FileName) Then _
  MsgBox "Folder Selected: " & cd.FileName

21 April 2011

CommDlg.ShowFolders

The ShowFolders method of the CommDlg Ocx isn't documented properly. The method is included to provide an easy way to display the Shell's SHBrowseForFolders dialog box allowing the user to select a folder rather than a file. It is - of course - not part of the the Common Dialog Box Library and should  have been implemented as a separately function. Or shouldn't it?

The FileName and Title properties
The CommDlg.ShowFolders(Optional flag As Variant) method shares two properties with other CommDlg methods that show dialog boxes: FileName and Title. In case these are used with ShowFolders their meaning is as follows:

- CommDlg.Title  (R/W)
Sets the string that is displayed above the tree view control in the dialog box. This string can be used to specify instructions to the user. Returns the same value as was put into it.

- CommDlg.FileName  (R/W)
Sets the browse dialog box's initial selection folder (has nothing to do with filenames!). Note, by default, the SHBrowseForFolder API lets the user start at the desktop to browse the shell's namespace and pick a folder. GFA-BASIC 32 overrides this behavior by starting the browse dialog box at the folder specified in the .FileName property. To let the dialog box start at the desktop set the FileName property to an empty string. To bring up the browse dialog box with the current directory selected, set FileName = CurDir.

After closing the ShowFolders dialog box the FileName property contains the name of the selected folder. That is if you select the Ok button; after Cancel the FileName is undefined.

CommDlg code was/is buggy
The GFA-BASIC 32 library code that implements CommDlg Ocx and specifically the Show Folder function, has always been buggy and I've been fixing it many times in the past. I was confident I fixed all bugs in Build 1169. At the time of writing this blog I tried it again and I was shocked to see that it still has a bug. When you press the browse dialog box's Cancel button the FileName property is undefined and present an Access Violation error. I will fix it as soon as possible and release Build 1170, which also contains fixes for the Dlg Open/Save commands.

The optional parameter
The ShowFolders(flag) method accepts one optional argument that specifies the options for the dialog box. When you omit the argument, the default behavior is BIF_RETURNONLYFSDIRS. The flag can include a combination of the following values:

BIF_BROWSEFORCOMPUTER (0x1000) Only return computers. If the user selects anything other than a computer, the OK button is grayed.
BIF_BROWSEFORPRINTER (0x2000) Only return printers. If the user selects anything other than a printer, the OK button is grayed.
BIF_BROWSEINCLUDEFILES (0x4000) The browse dialog will display files as well as folders.
BIF_DONTGOBELOWDOMAIN (0x2) Do not include network folders below the domain level in the tree view control.
BIF_EDITBOX (0x10) Version 4.71. The browse dialog includes an edit control in which the user can type the name of an item.
BIF_RETURNFSANCESTORS (0x08) Only return file system ancestors. If the user selects anything other than a file system ancestor, the OK button is grayed.
BIF_RETURNONLYFSDIRS (0x01) Only return file system directories. If the user selects folders that are not part of the file system, the OK button is grayed.
BIF_STATUSTEXT (0x04) Include a status area in the dialog box. The callback function can set the status text by sending messages to the dialog box.
BIF_VALIDATE (0x20) Version 4.71. If the user types an invalid name into the edit box, the browse dialog will call the application's BrowseCallBackproc with the BFFM_VALIDATEFAILED message. This flag is ignored if BIF_EDITBOX is not specified.

The BrowseCallbackProc
Although the ShowFolders method will pass most of the flags without questions asked, it doesn't honor them all. Some of the flags require an application-defined callback function to handle application defined behavior. The underlying SHBrowseForFolder API function calls BrowseCallbackProc to notify it about events. The address of the callback function is passed in the BROWSEINFO structure that contains information used to display the dialog box. The GFA-BASIC 32 ShowFolders method uses the callback proc to select the folder set with CommDlg.FileName in response of the BFFM_INITIALIZED message. GFA-BASIC 32 does not provide a way to interact with its BrowseCallbackProc, making ShowFolders a simple folder selection mechanism.

10 April 2011

An Array of Pointer To

The "Pointer To" data type isn't a key feature of (GFA-)BASIC. A BASIC programmer should not be bothered with pointers, at least that is the consensus. If you like to know more about pointers in GFA-BASIC check out ByRef arguments are Pointer To variables.

Introduction
One of the many variants of "Pointer To" I never considered; a declaration of an array of type "As Pointer To type" . For example, what does this do?

Dim a(1) As Pointer Large

It turns out that (with Option Base 0) the array contains two elements (0,1) of int32 data type. Two Longs are allocated and not two Large variables (int64). The same is true of all other types. Due to the Pointer keyword the elements are only 32-bit wide and are initialized to Null.

Code
This time I don't present a smoking explanation but sample code only. It contains 3 examples of 3 different array types, but they are all of Pointer To. See what happens.

' Essentially, it is an array of 32-bit integers
' to hold addresses of memory locations.
' The array's data type determines how to use
' the address stored in the elements.

' Example 1 - Array as a Pointer To Large

Dim a(1) As Pointer Large
Trace ArraySize(a())    ' 8 bytes, thus 2 * 4 bytes

' Assign memory locations to the elements:
Dim il As Large = 9, jl As Large = 10
Pointer(a(0)) = V:il     ' set array-elem to address of i
Pointer(a(1)) = V:jl

' Due to "As Pointer To Large" GFA performs
' an indirect access using the addresses stored in
' array elements. It does NOT directly return the
' values stored in a(0) or a(1) (which are memory locations).
Trace a(0)              ' = 9
Trace a(1)              ' = 10

' How does GFA do this?
' Well, let us see what is actually stored in a(0) and a(1).
' We use ArrayAddr() to obtain the addresses  of the array elements and then Lpeek the address stored there and then use Large{adr} to obtain the value.
Dim adr As Int
adr = Int{ArrayAddr(a()) + 0}   ' a(0)
Trace Large{adr}                ' = 9
adr = Int{ArrayAddr(a()) + 4}   ' a(1)
Trace Large{adr}                ' = 10

' Example 2 - Array as a Pointer To String
' Again an array of 32-bit integers, but now they must
' be set to point to the string's descriptor.
Dim s(1) As Pointer String, st As String = "Hello"
Trace ArraySize(s())    ' still 8 bytes
Pointer(s(0)) = *st     ' assign string descriptors
Pointer(s(1)) = *st     ' assign string descriptors

' See how GFA correctly uses the addresses to
' obtain the strings:
Trace s(0)              ' = Hello
Trace s(1)              ' = Hello

' Internally handled as:
Trace Char{{{ArrayAddr(s()) + 0 }}}
Trace Char{{{ArrayAddr(s()) + 4 }}}


' Example 3 - Array as a Pointer To UDT
' Again an array of 32-bit integers, but now they must
' be set to point to instances of ArrayDesc type.
Dim ad(1) As Pointer ArrayDesc
Trace ArraySize(ad())    ' still 8 bytes

' Use ArrayDesc of previously declared arrays
Pointer(ad(0)) = *a()    ' assign addr of ArrayDesc a()
Pointer(ad(1)) = *s()    ' assign addr of ArrayDesc s()

' See how GFA correctly uses the addresses
Trace ad(0).ptype              ' 37 ($24 + $1)
Trace ad(1).ptype              ' 73 ($48 + $1)

' Internally handled as:
Trace Int{{ArrayAddr(ad()) + 0 } + 4}
Trace Int{{ArrayAddr(ad()) + 4 } + 4}

Type ArrayDesc
  -Int    Magic         ' 4 Byte ASCII
  -Int    ptype         ' vtType
  -Int    size          ' size of the datatype
  -Int    dimCnt        ' number of dimensions
  -Int    dimCnt2       ' Erase and $ArrayChk
  -Int    paddr         ' ArrayAddr()
  -Int    corr          ' correction value
  -Int    paddrCorr     ' void*addrCorr;
  -Int    anzElem       ' number of elements
  -Int    sizeArr       ' size in bytes ArraySize()
  -Int    Idx(3)  '[3][1];      //Open Array
EndType

Note - A declaration of an array creates an array descriptor which the array variable references. Operations on the variable are performed using the content of the descriptor. This is not about the array descriptor, but it illustrates what is allocated with Dim. The UDT ArrayDesc is used as an example for an array of Pointer To UDT.

Conclusion
GFA-BASIC 32 returns the content of array elements correctly, but this only works when the array elements are assigned the addresses of memory locations that need to be read using the arrays data type.

09 April 2011

Status of GFA-BASIC 32

It has been a while, but I certainly didn't give up on GFA-BASIC 32. In fact, I've done some fundamental research this winter to figure out a (new) direction for GFA-BASIC 32.

COM is a must
Each new Windows OS provides more COM based interfaces, rather than new DLL function APIs. A modern language must support the use of COM interfaces, and I've done a lot of research in that area. The first step in the process should be the use of non-ActiveX COM libraries. This requires adding COM references to a GFA-BASIC 32 project. Once this is completed, the language should have the ability to provide COM classes as well. So, in the next stage, GFA-BASIC 32 must support object-oriented structures like VB's Class/End Class. Finally, research must be done to evaluate the opportunity to add third party ActiveX controls.

Maintaining GFA-BASIC
This is not the only thing that is on my mind. I often get questions or receive bug reports that require a lot of time to investigate. I only have a heavily commented disassembly, I still don't understand all of it. Once I found a bug or improvement it needs to be implemented. Well, patching is an art in itself and often can be done in multiple ways. Sometimes I need to acquaint myself with entirely new ways of hacking. This costs too much time, so I need to figure out how to proceed with that. My first priority is to implement COM support.

Assemble in GFA-BASIC?
But, only recently (!) I realized I could maybe copy the disassembly to GFA-BASIC 32 and use GFA-BASIC's assembler to re-create the executable (GfaWin32,exe). I made some trials and it might actually work! That would mean I could edit the (assembly) code of the IDE directly inside GFA and then recompile (F5). Wow, maybe I give this my number one priority ...

Next blog entry
One of the questions I received contained a statement I wasn't familiar with. The code contained a declaration of an array of type "Pointer To Int". I had no idea what that meant. Do you? If not check out the next blog.

28 November 2010

Download new update

General note on updates. Go to the Download Page (button on the top of the blog).

Update of Build 1169 (2)

This is the second update of Build 1169. The IDE (GfaWin32.exe) isn't changed and still has build number 1169; however the runtime in the previous build-release 1169 (1) contained a new bug.

The CommDlg property .FileName didn't return the selected folder when CommDlg.ShowFolders was used. This is now fixed and the .FileName property should now return the correct filename(s) or folder. Note that the .FileName property was associated with three bugs. I hope they are all fixed now.
The runtime has build number 1169 now as well.

GOTO DOWNLOAD

22 November 2010

ImageList Ocx Part 2

In the introduction to the GFA-BASIC 32 ImageList Ocx (1) the emphasis was placed on the creation of the image list common control, which – as we saw - is delayed to the moment the first picture is added to the control. All images have the same size. The size is determined by the first picture's height and width, or the size values set using the .ImageWidth and .ImageHeight properties of the ImageList Ocx.  The .ColorFormat property specifies how the pictures are added and whether or not a mask is generated. The color for the mask is  specified using the .MaskColor property.

After the creation
Once the underlying image list common control is created, the .ColorFormat, .ImageWidth and .ImageHeight properties fail to accept a new value. These properties raise the E_FAIL COM error. This is also demonstrated when you use the 'ImageList Data' dialog box to add images to an ImageList Ocx. The upper left controls contain the options to create the image list common control. You may specify the required size and color format. The default settings are zero for both the size properties, and the 0 (device dependent/mask included) ColorFormat property.
After adding an image these control are disabled, because ImageList_Create() has been invoked using the settings from these controls.  
[image17.png]
Using masks in GFA-BASIC 32 is a bit confusing because its setting is the opposite of Windows API.

Masks in GFA-BASIC 32
By default GFA-BASIC 32 creates a mask for each and every picture you add to the ImageList control. Whether or not the mask is added to the underlying image list common control is determined by the .ColorFormat property value.
In contrast with the API specifications, GFA-BASIC 32 uses a reversed setting for the mask option. The flags argument in the ImageList_Create() function specifies the mask using the ILC_MASK bit value. In GFA-BASIC 32 you don't explicitly specify  a mask by setting bit 1 to 1; in GFA-BASIC the .ColorFormat value must hold a zero value in bit 1.

.ColorFormat = 0    '(default) create mask
.ColorFormat |= 1   ' no mask


Just before creating the image list common control GFA-BASIC inverts the first bit and includes it in the flags parameter passed to the ImageList_Create() function.
However, when you explicitly exclude a mask (by setting .ColorFormat |= 1) GFA-BASIC 32 will continue to create masks. However, when picture and the mask are added they are simply ignored by the image list common control.
Note Excluding a mask will result in loss of data when you add an icon to the image list. Therefore GFA-BASIC 32 uses a mask at all times.

Purpose of UseMaskColor and MaskColor Properties
When you add an icon into the list, GFA-BASIC uses its internal mask. If a bitmap or a metafile is added, a mask is created in memory. When .UseMaskColor == 0 the mask is simply a black monochrome bitmap of the same size, all pixel bits are set to zero. However, when you set .UseMaskColor to a nonzero value, the pixels in the image that have the same color value as the .MaskColor setting have a corresponding white color in the monochrome mask bitmap. This opens up the possibility to draw the image transparently. How the image is drawn later depends on more factors than the availability of a mask bitmap only, however.
You can change both UseMaskColor and the MaskColor properties before adding the next image to the list. That way you can have GFA-BASIC generate the correct mask for the image to add. (Each image might have a different transparency color.) 
UseMaskColor and MaskColor are a per image setting when adding a Picture to the list.

The BackColor Property
The only property we didn't discuss so far. The .BackColor property isn't used in the creation of the image list common control and it isn't used in the addition of images to the list. The .BackColor property setting is used when we want to get something out of the image list control. It is used in drawing operations and when a Picture object is retrieved.
The .BackColor is used in the ListImage.Draw method. The .Draw method is simply a wrapper around the ImageList_Draw(himl, i, hdc, x, y, fStyle) API, which draws an imagelist item in the specified device context. The fStyle parameter is used to set the drawing style and only when fStyle = ILD_NORMAL (0) the image is drawn using the background color. You can experiment with this, it speaks for itself.
Note The .BackColor value is passed to the underlying common control using ImageList_SetBkColor() API. 

The .BackColor setting is also used when the ImageList control is used with other controls (that can't be bound to the ImageList control). You can assign the ListImage.Picture object of the ListImage item to the .Picture property of another control. For example, the following code assigns the Picture object of the first ListImage object in a ListImages collection to the Picture property of a newly created StatusBar panel:

Dim pn As Panel
Set pn = sb.Panels.Add() ' Add a new Panel object.
Set pn.Picture = im1.ListImages(1).Picture 

However, how is this Picture created? That is , how are single Pictures  extracted from the image list control? Can this be done by invoking some ImageList_Xxx() API function? The answer is no, the Picture property is a GFA-BASIC 32 specific implementation which uses the .BackColor setting.

The Picture property With .Picture (Get-property) GFA-BASIC allows to create a new bitmap from the image list (and its mask). It creates always a 24 bits DIB memory bitmap to draw the image using ImageList_Draw(). The size of the DIB is ImageWidth x ImageHeight and the background is cleared using the color from .BackColor setting. Then, GFA-BASIC uses the ImageList_Draw() API to draw the specified image using the API background color for the image list (which is also set with .BackColor property!). The net result is a bitmap with a background color .BackColor.
Since the .Picture property returns a 24 bit DIB only, possibly transparent, the new obtained image form the ImageList Ocx might be quite different from what first was added. The .Picture property only returns device independent bitmaps or icons. Any jpg and metafile images are converted to a DIB.  

ExtractIcon
When you require a transparent image for some other control that accepts an icon use the .ExtractIcon property instead. This will return a real Windows icon resource which is transparent by default.

ImageList Ocx (1)

An ImageList control is a collection of images of the same size and type. An image list control helps you manage a collection of images that are the same size, such as bitmaps or icons. Image lists, which are designed for use with list view and tree view controls, manage images but do not display them directly. In BASIC its use is extended to be repository for other controls as well.
Note Part 2 was published before part 1, so might have read ImageList Ocx (2) before.
The API behind the creation
The windowless ImageList Ocx is a bit different from the image list common control. Although the GFA-BASIC 32 Ocx is created using the API function ImageList_Create(), the underlying image list common control is managed using a second layer of ListImages, another Ocx type. The ListImages object is created when the ImageList Ocx is created and both are initially 'empty'; there is not even a real image list common control. GFA-BASIC 32 doesn't create the underlying image list control before the first picture is added to the list. The reason behind this behavior will be clear when you look at the definition of ImageList_Create(). The image list needs the initial size of the images and most often the size isn't known until the first image is added. 
hIml = ImageList_Create(cx, cy, flags, cInitial, cGrow )

This function creates a new image list common control and returns the handle to the image list if successful. Note that the handle isn't a handle to a window (hIml isn't HWND type).
To be able to create a proper ImageList Ocx, the following parameters and Ocx properties need to be set.
API Meaning ImageList property
cx Define the width and height, in pixels, of each image. .ImageWidth
cy Defines the height of each image .ImageHeight
flags Set of bit flags that specify the type of image list to create.
- use a mask (ILC_MASK) 
- color depth  (ILC_COLORnn)
.ColorFormat (sets color depth and sets ILC_MASK)
cInitial Specify the number of images that the image list initially contains. 1 - Determined by GB32
cGrow Number of images by which the image list can grow when the system needs to make room for new images. 1, 2, or 4 - Determined by GB32
Only three properties determine the type of image list common control is created.
  1. .ImageWidth and .ImageHeight are used to set the cx and cy arguments of the API function. When not specified the first image added to the list determines the width and height of all images (cx and cy).
  2. .ColorFormat is used to set flags API parameter, which specifies the required image type (16 colors, true colors, etc.). Default is device dependent with a mask.
Masks
The .UseMaskColor and .MaskColor properties are useful only when the control has a masked bitmap attached. The masked-bitmap is created when the API argument flags includes ILC_MASK (GFA-BASIC default). If the ImageList Ocx doesn't contain a masked bitmap these two properties have no effect!
  • A non-masked image list includes a color bitmap that contains one or more images. This is a wide bitmap containing small bitmaps. When a non-masked image is drawn, it is simply copied into the target device context (DC); no special processing occurs.
  • A masked image list includes two wide bitmaps of equal size. The first is a color bitmap that contains the images; the second is a monochrome bitmap that contains a series of masks (one for each image in the first bitmap). The mask is created by GFA-BASIC 32 and not by Windows, except for icons. Icons contain both a color bitmap and a mask and GFA-BASIC adds directly to the underlying common control.
On output, when an image is retrieved from the ImageList Ocx, the existence of a mask determines how the image is composed. An image can be drawn using ImageList.Draw or extracted using ImageList.Picture or ImageList.ExtractIcon
With a masked image present, the mask is combined with the image itself. This combination produces transparent areas in the bitmap in which the background color of the target DC shows through. More about this is ImageList Ocx (2).
The Picture input Object
The ImageList Ocx only accepts images contained in a Picture or StdPicture object. A Picture Ocx object is a container for a bitmap, icon, jpg, or metafile image. In both VB and GB32 it is not possible to assign any other type of image than a Picture COM type.
There are three ways to obtain a Picture object.    
1. Use LoadPicture() to load an external image file.
2. Use CreatePicture() to create a picture object initialized with a bitmap or icon.
3. Obtain a Picture object from another COM object.

All Picture objects (jpg, bmp, dib, emf) that are added to the ImageList Ocx are provided with custom created mask. The masked image is created by GFA-BASIC using the color from the .MaskColor property, but only when .UseMaskColor is True. .UseMaskColor and .MaskColor determine the transparent color per image.
Adding Pictures – API details
Once you have an image in a Picture object, you can start adding that image to ImageList Ocx. Internally, GB32 has a choice from three possible API functions to add a Picture image: ImageList_Add(), ImageList_AddIcon(), and ImageList_AddMasked() [not used by GB32]. Obviously, GFA-BASIC uses ImageList_AddIcon() when the Picture object holds an icon resource. In all other circumstances it uses the ImageList_Add() API which takes three arguments:
index = ImageList_Add(himl, hbmImage, hbmMask)

When you add an image into the list, GFA-BASIC gives it a mask. If the image is an icon, its internal mask is reused. If it is a bitmap or a metafile, a mask is created. The end result is a collection of icons of the same size. However, all images in an image list are contained in a single, wide bitmap in screen device format. When an image list includes a mask, it also has a monochrome bitmap that contains masks used to draw images transparently (icon style).
Before invoking this function, GFA creates the hbmImage and the hbmMask. The hbmMask is a black bitmap in case .UseMaskColor = False (0).
Finally
Usually, the ImageList Ocx is used as an image repository for other Ocx controls. When you attach an ImageList to another control, you don’t need to worry about what gets drawn and how. An image list is a collection of images of the same size, each of which can be referred to by its index or key.
A second blog ImageList Ocx (2) discusses more implementation details.

05 August 2010

Initializing an Arrray

An array is a named collection of values of the same type. In BASIC an array is always dynamic, the memory necessary to store the array elements is allocated at runtime. You cannot set the array elements to a constant value before hand. Initializing array elements is a runtime operation performed after the array is created, unfortunately. Let me illustrate.
In C/C++ you can assign initial values to an array by enclosing values within braces {}. For instance

int scores[5] = {1, 2, 3, 4, 5};

In contrast with BASIC, the array is static and not created dynamically at runtime. The C/C++ compiler reserves 5 consecutive integers (each 4-bytes, thus 20 bytes) in the .rdata section of executable and associates the address with the variable name scores. When the executable is loaded the 5 integer values are loaded as well and scores points to the first integer (value=1). Because the array is of type int (32-bits integer) the C/C++ compiler converts the ASCII representation "1" found in the source code to the 32-bits number 1. The executable contains the following array data, assuming the scores is stored at 0x0498760:

variable address value
scores[0] 0498760 1
scores[1] 0498764 2
scores[2] 0498768 3
scores[3] 049876C 4
scores[4] 0498770 5

In C/C++ the initialization is performed at compile time. So, when the C/C++ program is started no time is spent on assigning values to the array elements.
This in contrast with (classic) BASIC. The only method of initializing an array is by explicitly assigning each value to the array elements. Often, the values are stored in a DATA statement and then assigned using READ in a For-Next loop. To accomplish the same in BASIC you will often see the following code.

Dim scores(4) As Int, i As Int
For i = 0 To 4
  Read scores(i)
Next
Data 1,2,3,4,5

At compile time, the compiler doesn't know the data type of the Data-items. This is determined by the datatype of the variable used in the READ command and known at runtime only. After compiling the values in the Data line are stored as ASCII strings. The Read command converts the ASCII "1" to a 32-bit integer (because scores() is declared as an Int). After READ completes the conversion of the first value, it increments the data-pointer _Data. The next Read statement converts the next string ("2") using an ASCII-to-Int function. (The runtime function used is READINT() ). A rather clumsy, inelegant, and (run)time consuming process. Fortunately, GFA-BASIC 32 provides a faster way to initialize an array, thereby skipping the ASCII-TO-INT process for each element. BASIC doesn't allow array access through pointers, so the dynamic allocation remains.

The Array command
To initialize an array with literal constants GFA-BASIC 32 offers the Array command. The Array command takes a string with data in binary format to initialize the elements.

Array scores() =  Mki$(1, 2, 3, 4, 5)

The string is nothing more than a container for the data; a piece of memory containing the values in their binary form. And, most importantly, the string is created at compile-time. The string data is stored in their binary 32-bit format (Mki()) and takes 5 * 4 bytes = 20 bytes in your program's code. The storage is the same as with an initialized C/C++ array. The Array command uses the length of the string to calculate the number of elements to create a one-dimension array. It then copies the data to the array. This process is about 6-8 times faster than the For-Next loop method. 

20 July 2010

Unsigned to signed Byte data type

GFA-BASIC 32 provides two unsigned integer data types, the Card and Byte data type. The Card is a 16-bits integer and allows you to store positive integral values in the range from 0 to 65535. The Byte is 8-bits data type allowing you store a positive value from 0 to 255.

It is not important how many positive numbers the data type can hold. More importantly is the way how these values are handled in case of mathematic operations. The default operation GFA-BASIC 32 allows on integers is signed arithmetic, because all other integer data types are signed types. However, when you include a value stored in a Card or Byte GFA-BASIC expands the value to an unsigned 32-bits integer before using it in a calculation. So, when you store 255 in a Byte, the value 255 is used in calculations.

Now, suppose you need to use a Byte to store values like: –1, 0, 1. (The GFA-BASIC 32 editor uses these values to store line indenting information and I used it to implement block folding.) You can simply store these values into a Byte and in case of –1 all bits of the byte are set resulting in $FF (=255).

Dim b As Byte = -1  ' b = 255 ($FF)

When the Byte is used in a calculation, the value 255 is applied, which is not what we want. We need the Byte to behave as a signed value; $FF must be interpreted as –1. For this to happen we must GFA-BASIC 32 tell to interpret the Byte as a signed byte explicitly using the Sbyte() function. The Sbyte(b) function forces a conversion to a signed expansion to 32-bits.

Dim b As Byte = -1  ' b = 255 ($FF)
i% = b           ' i% = 255
i% = Sbyte(b)    ' i% = -1

In fact, the compiler uses a different assembler instruction to move the value from memory to the eax register (movzx eax, mem).

02 July 2010

Accessing a C array

A C-compiler treats arrays as pointers. An array-variable points to the first element. C computes the address of an array  element by multiplying the element index with the size of the data type. This is
automatic behavior of C. A pointer is an 32-bits integer holding an address pointing to a specific type. For instance, the next declaration defines a pointer to an array of 16-bit integers. To be effective it needs to be assigned an address first.

short* parr;
parr = 0x0180980;

The variable parr references the Short (int16) at the given address. To read the value at the address:

short value;
value = *parr;

Incrementing the pointer with one will let the pointer reference the next element, not the next memory location! E.g. parr++ will result in 0x0180982 (+2). Pointer arithmetic includes the size of data type.

parr++; 
value = *parr;
  
Accessing the fifth element in the array:

parr += 5; value = *parr;
value = *(parr + 5) ;      // even shorter
  
Almost the same can be simulated in GFA-BASIC 32 using the Pointer-type. Except GFA-BASIC does not use the size of the data type into pointer arithmetic. A Pointer variable defines the type of the variable without an address, like C.

Local parr As Register Pointer Short
Pointer(parr) = 0x0180980
Local Short value 
value = parr

To obtain the value from the address GFA-BASIC 32 doesn't provide an indirection operator as C does, as in value = *parr. In GFA-BASIC 32 you must first assign the pointer variable an address using Pointer(parr)= addr. Once the pointer variable is assigned an address, it can be used as a normal variable. Note that Pointer()= performs an kind of implicit VarPtr(parr) = addr, because a pointer variables VarPtr == Null.
Incrementing the pointer to point to the next element requires to set the variable's VarPtr to the next address, in this case plus 2 (SizeOf(Short)==2). So, first give the variable a new address and than access the address using normal variable syntax.

Pointer(parr) = Pointer(parr) + SizeOf(parr)
value = parr

The next sample shows how to iterate over a C- array and how to access an element by index.

Dim C_str As String = "Hello"

' Iterating a C-array
Dim parr As Pointer Byte
Pointer(parr) = V:C_str
Trace Hex(Pointer(parr))
While parr <> 0
  Debug Chr(parr);
  Pointer(parr) = Pointer(parr) + SizeOf(parr)
EndWhile
Debug
Trace Hex(Pointer(parr))

' C-array using index
Pointer(parr) = V:C_str + 4 * SizeOf(parr)
Trace Chr(parr)

The code uses the Debug Window and shows the begin and ending address of the variable parr. The Debug commands show the byte (in character format) the parr Byte variable is addressing.

27 June 2010

A GFA-BASIC32 Forum

I'm joining a forum now.

http://gb32.proboards.com/

The administrator Detlef Peters AKA 'Joshy' convinced me of the relevance of this forum. He has created plug-ins, DLLs, libraries to be used with Basic4GL, but he thinks this language is dead and no longer supported. He also thinks that quite some Basic4GL users might switch to GFA-BASIC 32. They are welcome and might need technical support. Joshy seems knowledgeable and serious, and therefore I decided to join, although I don't like forums, I prefer mailing lists, but what the heck I'll give it a try.

I will check in as much as possible, but will limit myself to answering GFA-BASIC 32 related questions and problems. General Windows programming issues should be solved by studying (VB) books and other samples. For me, it is no longer opportune to answer questions like "what is the difference between ByVal and ByRef?" The internet is full with good explanations. I will go into questions of why you should use ByRef explicitly in non-event Sub subroutines

13 June 2010

Exe without runtime?

One of the questions I got is:

"There is a possibility that my program e.g. "name.g32" run as "name.exe" after compiling without using "GfaWin32.Ocx" ?
If yes, can you give me instructions for this please ?

At the first beta introduction of GFA-BASIC 32 back in 1998 we asked the same question. Why wouldn't it be possible to statically link the necessary code into the EXE? That would give us a slightly larger EXE, but the result would be one file. There are two reasons why this isn't possible.

  1. The entire interface of a GFA-BASIC 32 program is encapsulated in to COM objects. All COM objects are related to each other and thus the stand-alone EXE would increase with about 500 KBytes. GFA simply choose to put the code in a single (in contrast with VB!) OCX runtime. Putting OLE/COM stuff in a DLL is common to all programming languages.
  2. The Windows OS is capable of sharing one instance of a DLL with multiple EXEs. Not only your EXE depends on the runtime, but the IDE does as well. All instances of your programs and all instances of GFA-BASIC 32 IDEs share this one DLL. 

Mailinglist off-line

It seems the gfa@liebenstein.de mailinglist is currently offline. I hope this will be fixed soon, otherwise we will have to look for an alternative. For the moment, if you have urgent questions you can ask me at gfabasic32@gmail.com.

10 June 2010

New 'old-GFABASIC 32- samples'

Harald Bonsel posted me the following:

"I found an old directory of GFA ftp://ftp.gfa.net/pub/gfa/

. May be you out the link on your  Google page?"

It turned out that the old site contains some never published samples. so check it out.