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.