22 April 2009

The Command.Picture property

Years ago I asked a question on the GFA MailingList about the .Picture property of the Command Ocx. At design-time the button displays the bitmaps of an ImageList properly, but when the program was run the pictures were showed with a black background. I forgot about it for years, but recently I created a program with buttons (Command Ocx) with images and the problem reappeared. The image assigned to the button was definitely a masked-bitmap, without the transparent color applied. The difference with all those years back ant the present time, is the amount of documentation and interpretation of the dissassembly of the GfaWin23.Ocx. I immediately started a debugsession to see what happened when a iml.ListImages(idx).Picture is assigned to a Command Ocx. The session turned out surprisingly informative, neither the Command nor the ImageList Ocx contained a bug. I was thinking wrongly, I used the wrong ListImage method, I should have used iml.ListImages(idx).ExtractIcon. To begin with, the ImageList control was assigned a bitmap. However the control stores the single images in two separate bitmaps, a mask bitmap and a masked bitmap (because I had set the ImageList.UseMaskColor property). The background color of the bitmap image was blacked out (which I specified using the ImageList.MaskColor property.) I then invoked the following command to give the button a picture, assuming this would provide the Command Ocx with the original image I put into the ImageList control:
cmd.Picture = ImageListToolbar.ListImages(61).Picture
But I assumed wrongly. The .Picture property of the ImageList returned the handle of the masked bitmap only. So, to give a Command Ocx a transparent image from a ImageList Ocx use the .ExtractIcon method.
cmd.Picture = ImageListToolbar.ListImages(61).ExtractIcon
The Command.Picture = pict pseudo-implementation is (without error and IsNothing checks) as follows: type = pict.get_Type handle = pict.get_Handle SendMessage(Command.hWnd, BM_SETIMAGE, (type=1? IMAGE_BITMAP: IMAGE_ICON), handle) Giving the Command.Picture property a Picture object containing an icon will result in a transparent image in a Button control.

08 April 2009

ANSI API functions and RichEdit

Recently someone patched the GfaWin23.Ocx DLL by replacing the "RichEd20.dll" text entry by "MsftEdit.dll" and "RichEdit20A" by "RICHEDIT50W". The patch isn't very difficult since both text entries are equally sized. The patch was made to obtain the functionallity of the RichEdit Control version 4.0 (which for some reason is called 50) by the GFA-BASIC 32 Ocx interface. By pathing the Ocx file this way, he succeeded in using the "msftEdit.dll. So far so good, but the patch raised a question for me. Why would this control only register a "RICHEDIT50W" class and not a "RICHEDIT50A" ANSI variant? What effect would that have on GFA-BASIC 32 using ANSI functions only?

Previous RichEdit control versions (3.0, 2.0) support two window classes "RichEdit20A" for ANSI systems and "RichEdit20W" for UNICODE systems. When you were compiling a program (in C/C++) for a UNICODE program you would use the "RichEdit20W" class in the CreateWindowExW() API function. GFA-BASIC 32 uses the "RichEdit20A" window class to create a RichEdit Ocx using CreateWindowExA. The properties and methods of the RichEdit Ocx are implemented by sending control messages using SendMessageA().

After patching the rich edit control is created by invoking CreateWindowExA and passing a pointer to an ANSI string containing "RICHEDIT50W". How can that work? The new control only supports wide character coding, how can it accept an ANSI string? I think I found the answer. ANSI API functions are redirected to the wide (UNICODE) variant.

When a program is specifically created as a UNICODE application (which is possible only for NT 4.0 and higher) it uses the UNICODE versions of the system API functions. All API functions have an ANSI and a UNICODE variant with different(!) entry points in the system DLLs. For instance, the SendMessage() API comes in two variants: SendMessageA() and SendMessageW(), two different functions. The xW() system functions expect strings formatted in UNICODE format. On UNICODE Windows systems (thus everything above Windows 95/98/Me) the ANSI versions of the API functions first translate a string parameter to UNICODE and then call the xW() version passing the newly created UNICODE string. This makes ANSI programs quite slow when API functions use strings (upto 3 times, see "Under the Hood 1997" by Matt Pietrek).

GFA-BASIC 32 is an ANSI program and invokes only the xA() versions of the Windows API. Therefor, on Windows NT 4 and above any string passed is converted to UNICODE before handled in the xW() version of the function. (GFA-BASIC 32 is not prepared for pure UNICODE systems, it had to run on Winddows 95 as well.) Back to the question, why does the MsftEdit.Dll accept an ANSI string in CreateWindowExA()?Because in turn the CreateWindowExA() calls CreateWindowExW() on any 'not Win95/98/Me' system passing the classname "RICHEDIT50W" as a UNICODE string. This means, according to my findings that the MsftEdit.dll won't run under Windows 95 when used from the patched GFA-BASIC 32 Ocx-dll. These Windows systems don't redirect to a wide version of the API function.

BTW It might be useful to know how GFA-BASIC loads the appropriate richedit dll. Just before a RichEdit Ocx control is created, GFA-BASIC 32 loads the appropriate library, as follows: If hLibRichEd == 0 hLibRichEd = LoadLibraryA("RichEd20.dll") If hLibRichEd == 0 hLibRichEd = LoadLibraryA("RichEd32.dll") If hLibRichEd == 0 Then RaiseErr "OutOfMemory" Endif Endif Then "RichEdit20A" is used in a CreateWindowExA() API call to create a control window of the this class.

04 April 2009

GetFirstVisible method not implemented

The ListView Ocx method .GetFirstVisible isn't implemented and simply returns Null. It is described as "to return a reference to the first ListItem object visible in the internal area of a ListView control". I think FO simply forgot to implement it properly, because many properties and methods return a reference to a Listitem object and a copy/paste action of the relevant GfaOcx23 source code would have sufficed to make the method work. Pitty. However, by using two (not VB-compatible) ListView methods the problem is easily solved. (Actually, it is one property and one method) ListView.TopIndex ListView.ListItem(index%) The ListView.TopIndex get-property returns the index (long integer) of the first visible item. It doesn't return a reference to a ListItem, but returns a long starting with 0. The ListItem(index%) returns a reference to a ListItem object from an index starting with 1. Yes, that is COM combined with Windows API. Anyway to replace the .GetFirstVisible method you should use:
Local ListItem li
// Set li = ListView.GetFirstVisible replacement:
Set li = ListView.ListItem(ListView.TopIndex + 1)

03 April 2009

Pointer and Gfa_Var object

I wasn't finished discussing the Pointer [To] data type. Maybe I should summarize the things I figured out until now:
  • GFA-BASIC 32 is capable of generating two types of code to access data; directly and indirectly using C-like pointer code.
  • A Pointer [To] type variable is 32-bits variable without a VarPtr address. The data memory address is set using Pointer=. Other than this, GFA-BASIC 32 treats the pointer as a normal data type.
  • All ByRef subroutine arguments are Pointer To variables, whose VarPtr-address is set by the caller (instruction that executes the subroutine).

Because of the duality, GFA-BASIC 32 needs to handle a Pointer To variable differently from normal variables. A normal variable has a name and memory location, in fact the variable-name is an alias for a data location. As such a variable name is not known after compiling, all references to the variable name are removed and replaced by assembler instruction theat memory loaction. Therefor a normal variable has only one 'address', either returned by VarPtr (V:) or ArrPtr. A Pointer (32-bits wide) on the other hand is split in two separate units. The address the pointer is stored and the VarPtr address it is pointing to. A Pointer variable has two addresses. This is refelected in the Gfa_Var object. A Gfa_Var object is an item of Gfa_Vars collection, a collection of variables of a certain subroutine. The Gfa_Var has two properties that illustrate this concept:

Gfa_Var.VarPtr Gfa_Var.Addr

In case of a normal variable both properties return the VarPtr address, In case of a pointer the .Addr property returns the address of the Pointer variable and the .VarPtr property the address set using Pointer()=. To help investigate I wrote a small procedure to show the Gfa_Var properties in the Debug Output window. A call to ShowVar is done by including a copy of a Gfa_Vars collection item. In case of the example below, I inspected a global user-defined-type declared as Foo. To obtain a collection of global variables you either use Gfa_Globals or Gfa_Vars(""). To inspect a local variable, execute ShowVar from inside a procedure and use Gfa_Vars0!varname.

ShowVar Gfa_Vars("")!Foo, V:foo, ArrPtr(foo), _
 "- Global Foo As Tfoo:"

Proc ShowVar(vGfaVar As Gfa_Var, vp%, ap%, comment$)
  Debug comment$

  Debug "Variable: "#39 & vGfaVar.Name & #39 & _
    ", TypeName: "#39 & vGfaVar.TypeName & #39 & _
    ", Sub: "#39 & vGfaVar.PName & #39
  Trace Hex(vGfaVar.Type)
  Trace vGfaVar.Addr
  Debug "ArrPtr(" & vGfaVar.Name & ") = " ap
  Debug "VarPtr(" & vGfaVar.Name & ") = " vp
  Trace vGfaVar.VarPtr
  Trace vGfaVar.Size
  Trace vGfaVar.Len
  Trace vGfaVar.IsTyped
  Debug
EndProc