25 June 2013

What to do with an OLE_COLOR?

An OCX/ActiveX control color property can be assigned any valid Win32 color reference. However, the OCX/COM libraries provide a special data type for color properties, the OLE_COLOR data type. This new type was invented to give VB-like programs an opportunity to differentiate between a normal Win32 color reference and system colors. These programs could then present a special color-selection dialog box that allowed the selection of system colors. The snapshot of the GB32 Ole-color selection is shown below.

image

RGB colors are of type COLORREF
Like an RGB-color value, an OLE_COLOR data type is internally represented as a 32-bit long integer. An RGB-color is referred to as a COLORREF value and is used with GDI device context functions. A COLORREF is simply defined as a 32-bit value used to specify an red, green, and blue components of a color requiring only 3 bytes. When specifying an explicit RGB color, the COLORREF value has the following hexadecimal form.

0x00bbggrr

The low-order byte contains a value for the relative intensity of red, the second byte contains a value for green, and the third byte contains a value for blue. The high-order byte must be zero. The maximum value for a single byte is 0xFF.
To create a COLORREF color value, you can use the GB32 RGB() function. To extract the individual values for the red, green, and blue components of a color value, use the GetRValue, GetGValue, and GetBValue functions, respectively. For performance reasons these GFA-BASIC 32 functions are directly translated to assembly code; they are not implemented as function calls.

The OLE_COLOR type
When a COM object property (most often .ForeColor and .BackColor only) requires an OLE_COLOR color, you can either pass an RGB  value (COLORREF) or a COM specific formatted system color index value. A COM specific format has it’s high-byte set 0x80. An OLE_COLOR can have two formats only:

  • 0x00bbggrr (COLORREF value)
  • 0x800000xx, where xx is the index used by GetSysColor().

The object doesn’t do much with the OLE_COLOR value, it is more a kind of a storage type. For instance, you cannot pass an OLE_COLOR to a GDI device context handle. GDI doesn’t know what to do with a COM formatted system index color (0x800000xx). The OLE_COLOR value is simply saved in the private data of the COM object and remains unchanged until the application explicitly changes the value again. This way, the Get-color-property variant of the Put-color-property will return exactly the same value as was first put in.

How the OLE_COLOR is used
Since the format of the OLE_COLOR data type isn’t known to Win32 drawing functions, they must be converted to RGB colors first. Usually, the OLE_COLOR is converted on a just-in-time basis. Just before a painting operation is performed the OLE_COLOR (32-bit long integer) is tested for a high-byte value of 0x80. When the high-byte is indeed 0x80, the low-byte value is used as a parameter to the GetSysColor() API function, which in turn returns the RGB color value of the specified display element. See the code below.

Aside: Identifying display elements
Display elements are the parts of a window and the display that appear on the system display screen. These elements are identified using the API COLOR_* constants, like COLOR_BTNFACE, COLOR_WINDOW, etc. These API constants may also be used in the GFA-BASIC 32 function SysCol(idx), which is slightly easier to use and a bit faster due to caching.

Dim rgb% = SysCol(COLOR_WINDOW)

The GFA-BASIC32 OLE_COLOR Constants
GFA-BASIC 32 also defines a set of OLE_COLOR constants to identify the system display elements. These constants are very much like the Windows API COLOR_* constants. However, instead starting their names with ‘COLOR_’ they start with ‘col’, like colBtnFace and colWindow. These GB32 OLE_COLOR constants have the format of 0x800000xx, where xx is the index for the display element. See the code below.

Convert an OLE_COLOR
When you want to use an OLE_COLOR value, make sure to convert the OLE_COLOR to a COLORREF (RGB) value first. This is the only color format Win32 understands. In GB32 you get hold of an OLE_COLOR when you use one the col* constants or retrieve a color from one of the Ocx-color properties. For instance, when you want to use the same color as the form’s background, you might very well end up with an OLE_COLOR with the high-byte set to 0x80.

Now, there are two possible ways to convert an OLE_COLOR to a COLORREF value.

1. Manually, in your code. For example this converts to an RGB-value:

Trace Hex(SysCol(colBtnFace))   ' =0 (SysCol: what is a colBtnFace?)
Trace Hex(colBtnFace)           ' =8000000F (an OLE_COLOR)
Trace Hex(OleToRGB(colBtnFace)) ' =F0F0F0 (the RGB value)
Trace Hex(OleToRGB(0x667788))   ' =667788 (no conversion)

Function OleToRGB(olecolor As Long) As Long Naked
  If (olecolor And 0xFF000000) == 0x80000000
    Return SysCol(olecolor And 0xFF)  ' LoByte
  Else
    Return olecolor                   ' RGB!
  EndIf
EndFunc

2. Use the system OLE function OleTranslateColor()

Declare Function OleTranslateColor Lib "oleaut32.dll" ( _
  ByVal OleColor As Long, _
  ByVal hPal As Handle, _
  ByRef ColorRef As Long) As Long

Dim rgb As Long Long

If OleTranslateColor(colBtnFace, Null, rgb) == S_OK Then _ Trace Hex(rgb)

The second option using OleTranlateColor() might be preferable because it can also return other GDI color formats, like a palette-index value. In addition in case of an error, it let you investigate the type of the error. See the SDK for more info.

More flexibility
You can use OLE_COLOR types with the GB32 graphic commands, though. All drawing statements, font statements, and color statements accept an OLE_COLOR type. GB32 converts the OLE_COLOR to a COLORREF before it invokes the corresponding GDI function.

Another nice page on GB32

Sometimes I scan the internet for GFA-BASIC 32 references. Recently I stumbled upon this site http://basic.mindteq.com/index.php?i=81 with a comment from Bruce Bell. He wrote a very nice summary of the GFA-BASIC 32 essentials. Chapeau!

23 June 2013

The Align Property of Command, Checkbox, and Option

The Align property of the Ocx controls Command, Checkbox and Option differ from other Ocx controls. The documentation for Align says that it indicates how an Ocx control is placed inside its parent or owner Ocx control, also called docking. Several predefined constants (basNoAlign (0), basTop (1), basBottom (2), basLeft (3), and basRight (4))  are provided to help using the correct docking value with Align.

The buttons are different
In GFA-BASIC32 the Windows BUTTON class controls (push)button, checkbox, and radio-button are implemented as Ocx Command, Ocx Checkbox, and Ocx Option, respectively. The position of the contents of the BUTTON control can be changed by applying a predefined BUTTON-style alignment value. These are defined in the winuser.h SDK include file:

#define BS_LEFT             0x00000100
#define BS_RIGHT            0x00000200
#define BS_CENTER           0x00000300
#define BS_TOP              0x00000400
#define BS_BOTTOM           0x00000800
#define BS_VCENTER          0x00000C00


These values can be applied using API functions or using the WinStyle property of the Ocx controls. In addition, these style bits can be set using the Align property of the BUTTON class Ocx controls (Command, Checkbox, and Option). However, the values that you can assign to Align are different than the values used with the docking purpose of Align. The documentation lacks proper explanation of using the Align property with buttons. (At least in the English help file.)So let me fill the gap.
The position of the contents of the BUTTON controls can be set using a value between 0 and 10, but not 3 and 7. There meaning - as described by the official GB32 documentation -and the corresponding button styles are listed below.

Value Position BUTTON Style Value
0 Center Normal H/V centered 0x00000000
1 Left BS_LEFT (vertical centered) 0x00000100
2 Right BS_RIGHT (vertical centered) 0x00000200
3 no meaning* BS_CENTER 0x00000300
4 Top BS_TOP 0x00000400
5 TopLeft BS_TOP | BS_LEFT 0x00000400 | 0x00000100
6 TopRight BS_TOP | BS_RIGHT 0x00000400 | 0x00000200
7 no meaning* BS_TOP | BS_CENTER 0x00000400 | 0x00000300
8 Bottom BS_BOTTOM 0x00000800
9 BottomLeft BS_BOTTOM | BS_LEFT 0x00000800 | 0x00000100
10 BottomRight BS_BOTTOM | BS_RIGHT 0x00000800 | 0x00000200


When one of the position values from the first column is passed to the Align property the value is shifted left 8 bits and then sent to the BUTTON control using

SendMessage(ocx.hWnd, BM_SETSTYLE, position, 1)
* The no meaning values 3 and 7 can be used in code, not in the Form Editor. When used in code, the internal GB32 function that implements Align will accept any value between 0 and 15! It simply shifts the value 8 bits left and sends it to the BUTTON control.

So, the value of the Align property is used to position the contents of a BUTTON control Ocx. The value is converted to a BS_* constant value as defined in the SDK and passed to the BUTTON control. In this particular case the Align property has a different meaning. Perhaps, it should be named ‘Alignment’ as with the Label Ocx controls.

19 May 2013

Mailinglist Is Back!

From Juergen, the man who provides the GFA-BASIC mailing list on his private server for the past 10-15(?) years:

Hi folks, my domain @liebenstein.de was for a time not available, because the domain was put mistakenly on a black list and out of name server listings. With that also the mailing list didn't work of course. Please excuse this, the list is now
available again.

Thanks, Juergen!

30 April 2013

GFABASIC Mailing-list gone?

Many of us now receive this notification since the 4th of April.


This page can't be displayed
  • Make sure the web address http://gfa-basic.liebenstein.de is correct.
  • Look for the page with your search engine.
  • Refresh the page in a few minutes.
 

The moderator Juergen, cannot be reached by his GB mailing list's e-mail address. Either he doesn't know the list in reachable or he unplugged it.
 
Questions can be send to this blog's e-mail address or at my personal address. I will make them public and when the mailing list remains off line I will start my own list.

07 April 2013

Favorite: On GoSub/GoTo/Call

One of my favorite programming constructs in GFA-BASIC is, and always has always been, the On value GoXxx statements. Although, I must admit, there was a time I doubt the usefulness and legality of this command, because C/C++ doesn’t offer such a branch instruction. At those times I was under the impression that it was a typical BASIC overkill command. However, it is definitely not, on the contrary! These statements let GFA-BASIC produce optimized branching code through the use of jump tables. C/C++ compilers can do this only when their ‘optimize to fast code’ - switch is set. Since C/C++ doesn’t support jump tables as a language construct, these compilers crunch together a switch/case block and try to turn it in a jump table just like GFA-BASIC does by default. These are the statements that produce highly optimized branching code without setting a compiler option.

On n GoSub label1, label2, …
On n GoTo label1, label2, …
On n Call Proc1, Proc2, …

My most favorite version is On GoSub. This allows for executing local subroutines labeled by a Label: and ended with a Return statement. This version is not compatible to C/C++, these compilers produce code compatible to GFA-BASIC’s On GoTo.

Not in an Editor Extension
As I mostly program editor extensions, I was surprised to got in problems with these branch statements. GFA-BASIC, like Visual C++, creates a jump table at the end of a function/procedure. However GFA-BASIC hard-wires the address of the jump table, with the presumption that the code starts at $400000 (hInstance). With dynamically loaded GLLs, that place was reserved for the IDE executable image (that is where the code is loaded). When the GLL reaches the execution of the On GoSub code it reads the execution address of the labels from the IDE’s code text section. To the GLL this code contains rubbish. When the GLL is loaded not all the addresses in the GLL code aren’t updated correctly.

As my goal is to create code as efficient as possible, I am a bit disappointed and maybe I will try to fix the GLL-loader.

07 February 2013

SetFont 0 or Null?

In 2002 Peter Harder wrote:  "SetFont 0" oder "SetFont ungueltiges_handle%" sorgen im Gegensatz zu GFA16 bei GB32 für einen Programmabbruch. Tückisch vor allem  dann, wenn dieses Problem beim Umschreiben eines GFA16-Programms ' erst beim Kunden auffällt; ein Hinweis in der Onlinehilfe auf  diesen Unterschied fehlt.

What is this about SetFont?
In previous versions of GB the statement SetFont 0  was often used to select a default stock object font in the current active device context. Peter’s remark suggests that this functionality is missing from GFA-BASIC 32. However, it isn’t actually missing, but the data type of the argument is a Variant, rather then an integer. The 0 value is automatically converted to a Numeric Variant (data subtype = basInt) with value 0. This means: 0 is a valid numeric value and is handled as if the font-handle with value 0 is passed to SetFont.

It throws an error
The SetFont library routine checks for a numeric value in the Variant and uses that value to select a stock object. However, the valid value 0 references the WHITE_BRUSH stock object and not a font. SetFont stops and throws an error. ( The error “Illegal Function call”, which, of course, should have been “Invalid Parameter”.)

Use Null instead
To have SetFont select a stock object, to be precise, the DEFAULT_GUI_FONT, the Variant argument of SetFont must be set to the VT_NULL variant subtype. This is done by using the Null constant.

SetFont Null

The Null keyword is used with a Variant to indicate that it intentionally contains no valid data.

But what if you have a variable that is holding a font handle? Well, in case the font handle is 0, you would want to pass a VT_NULL variant subtype, so you must convert it to Null. You could use the explicit Null constant as above, but you can also use an undocumented function Null(value), which is replaced by a simple Null.

Global font%
If font%==
0
 
SetFont Null(font%
)
Else
  SetFont
font%
EndIf

14 January 2013

Using Fonts the old-way

The old-style font commands from GFA-BASIC for Windows 16 can still be used in GB32. However before using them you should be aware of an essential difference between GB32 and GB16. Beside this you should also know what the consequences are when applying the GB16 font commands.

The GB LOGFONT structure
In GB16 the font commands applies to one, and only one, internally maintained, global LOGFONT structure (user defined type). In GB32 there are multiple LOGFONT structures and none of them is a global variable. The LOGFONT structure is now part of a Form object and the font commands now work on the LOGFONT of the current active Form (Me)

The LOGFONT type is defined in wingdi.inc.g32 located in the Include directory of your GB32 installation.

' Logical Font (wingdi.h)
Global Const LF_FACESIZE = 32
Global Const LF_FULLFACESIZE = 64

Type LOGFONT
  lfHeight As Long
  lfWidth As Long
  lfEscapement As Long
  lfOrientation As Long
  lfWeight As Long
  lfItalic As Byte
  lfUnderline As Byte
  lfStrikeOut As Byte
  lfCharSet As Byte
  lfOutPrecision As Byte
  lfClipPrecision As Byte
  lfQuality As Byte
  lfPitchAndFamily As Byte
  lfFaceName(LF_FACESIZE) As Byte
End Type

Regardless of the GB version, the members of the current LOGFONT variable are set and read using the same set of commands. In GB16 they are applied to the one global variable, but in GB32 they still set and read the LOGFONT members from the current active Form (Me).

GB-Command Description
FONT member value sets a value to a member
RFONT member variable reads a member value into a variable
variable$ = _Font$ copies all logfont-members into a string
_Font$ = variable$ sets all logfont-members from a string
Dlg Font sets all logfont members from a users font selection
Font To hfnt Creates a logical font from logfont
FreeFont hfnt Deletes a logical font
GetFont hfnt Obtains loglont values from a logical font
SetFont hfnt Selects the logical font into the window's DC

After filling the LOGFONT members (using Font name, _Font$, or Dlg Font) the Font To hfnt invokes the GDI function CreateFontIndirect() which returns a Windows handle to the font created. This functions allocates memory whose memory handle is returned in the hfnt variable (32-bits). This font memory must be released by the application explicitly using FreeFont hfnt.
The GDI fontmapper creates the font that best matches the values in the LOGFONT structure. However, they might be different from what was requested. To obtain the attributes of the GDI created font, the application would use GetFont hfnt. This reloads the LOGFONT values from the actual font handle.

What about SetFont?
In GB32 this commands invokes a series of complex procedures. In GB16 it was as simple as selecting the logical GDI font handle into the current device context. In addition, in GB16 the current device context could be manipulated using SetDC, which is obsolete in the 32-bits version. Of course, eventually, GB32 will select a font handle into a device context like in GB16, but GB32 goes the COM way. As such the GB command SetFont hfnt creates a hidden Font object and performs a Set Me.Font = hiddenFont (from hfnt).

Finally, any font the application created the old way using Font To must(!) be released explicitly when it is no longer in use. Meaning after that some other font has been set. (Either using SetFont or using the Form's property Font.)

11 January 2013

Use the Explorer Preview Pane

This time I want to share a tip with you. I'm a big fan of the Explorer Preview Pane a facility that can show the contents if files next to the files in a separate pane. It allows and helps me to quickly scan C/C++ (header) files, readme-text files, and other plain text files. Most often it helps to scan through MSWord en PDF files as well. I use it for GFA-BASIC 32 files as well.
Preview GFA-BASIC 32 files
GB32 files are plain text files as well. So it would only be logical (as Spock would say) to view them in the preview pane as well. It is not only logical but possible as well. You can assign the '.g32' extension to the default Text Viewer of Windows Explorer by using a registry hack. However, this is not only cumbersome, but a bit complex as well see http://blogs.msdn.com/b/toub/archive/2006/12/14/preview-handler-association-editor.aspx. In the article Stephen Taub, author of the program, describes the Preview Handler Association Editor tool.
PreviewPane
This utility helps in assigning all registered file-extensions to a preview handler. Note that since Windows Vista the GFA-BASIC 32 file extension .g32 can only registered when GB32 is started in Administrator Mode. Then go to Properties and click the button to register the g32 and lg32 file extensions.
Changing the font
To change the standard font of the preview pane isn't difficult, because the pane always uses the current Notepad font. By changing the default font of Notepad you change the font of preview pane when using the Windows TXT Previewer.
Procedure: Launch Notepad and select Format/Font... from the menu bar. Change the font and close Notepad. Now text files should be rendered in the font you just selected.

04 January 2013

Memory Leak on Image Ocx

At the end of 2012 Peter Heinzig posted a sample showing GFA-BASIC 32 memory leaks with the Form and Image Ocx. In Memory leak with Form.Picture I focused on the Form object and a remedy against the memory leak. In the meantime I investigated the Image Ocx and can confirm a memory leak when the Ocx control is destroyed. Just like the Form Object the Image Ocx doesn't release it's Picture object. The remedy is actually the same. In the parent Form Ocx _Destroy sub event add a command to release the Picture property of the image control.

The solutions presented here are applicable on all versions of GFA-BASIC 32. However I hope to build an update of the runtime as soon as possible. I hope these are the only memory leaks, but it takes time to verify these are the only Picture objects that aren't released after a program has been closed.

31 December 2012

Printing a listing on another printer

If you are running a Windows Vista or Windows 7 operating system, then the procedure to select different printers has changed from earlier versions of Windows. Starting with Windows Vista the "Printer ..." button is missing from the common dialog Page Setup dialog box. This is a consequence of the newer Windows operating systems. Now if you want to change printers or print drivers, you must change the default printer from the “Devices and Printers” window

How does this affect printing from GFA-BASIC 32?
To print a listing you can select Print in the File submenu. Depending on the presence of a text-selection, Gfa_Print will either print the entire listing or the current selection only. The text is printed immediately, without an option to select a different printer or to cancel the print job.

Initially, when you haven't printed before, the listing is sent to the default printer using default settings. At very first time, on my default printer, this produces lines in a vey hard to read small font size printed in the upper left corner of the paper.
Now I'm aware of this behavior, I make sure to set the printer page options before I  start printing for the first time.
The printer settings are available in the Printer Tab in the GFA-BASIC 32 Properties dialog box. The top two buttons are used to customize GB32's printing options.

GB32 Properties Printer

Selecting the top button labeled 'Page Setup' will bring up the Page Setup common dialog box. The Page Setup dialog box allows the user to set the paper size, source, orientation, and margins for printing. Starting with Windows Vista the common dialog box that pops up in GFA-BASIC 32 looks like this. The margin settings are initialized to zero, so it is logical that the listing is printed in the top-left corner of the paper.

GB32 PageSetup

After changing the page settings and selecting OK, GFA-BASIC 32 updates the top button in the Printer Tab with the name of the Printer (here Lexmark).

  • Due to a bug in the GFA-BASICs English string data the button text isn't shown properly. Rather than providing the printer name it only shows the word 'Printer'. (The first image shows a fixed version of the IDE.)

Applying new settings
The Page Setup values aren't applied before selecting the OK button in the GFA-BASIC Properties dialog box. The same is true for values from the other tabs (Editor and Compiler). 
The printer settings are applied to some global application variables and stored in 4 registry values in 'HCKU\Software\GFA\Basic' register sub keys:

Printer String containing DEVNAMES values
PrinterDMF Data containing relevant DEVMODE values
PrinterFont Compressed LOGFONT structure
PrinterPage Margins, MarginMetrics, Options. Used to initialize the Properties Print Page

At the next start the GFA-BASIC IDE reads back the "PrinterPage" values from the registry and assigns them to the global Printer Page variables. These values are used to initialize the controls of the Printer Properties Page (hence the name PrinterPage).

The "Printer" and "PrinterDMF" registry values contain device specific information. When the actual process of printing is started these values are used to create a printer device context. In case the values are missing or corrupted GFA-BASIC will obtain a device context for the default printer and starts printing without any user intervention.

How changes affect GB32
If you paid attention, you will have noticed that GFA-BASIC 32 supports the logic for using different printer than the default printer, but lacks the possibility to select one. How come? Well in all their wisdom Microsoft decided to change the Page Setup common dialog box starting with Windows Vista. Previously, until Windows XP, the Page Setup common dialog box contained a third button labeled 'Printer ...". It was this button that allowed the user to select a different printer from the Page Setup dialog box.

Workaround? Hardly!
When GFA-BASIC 32 was released it could not be known that MS would remove the "Printer ..." button. GB32's printer selection logic was build entirely around this button in the Page Setup dialog. That was - and still is - the only way to select a different printer. The only remaining option is to set another printer as the default printer from the Control Panel, although that wouldn't suffice. You should also remove the registry settings "Printer" and "PrinterDMF".

By providing this background information I'm hoping for some input as of how to get an actually working hotfix.

29 December 2012

Memory Leak with Form.Picture Object

When you assign an image to the Picture property of a Form Ocx, and don't release the Picture explicitly, a memory leak occurs when the Form is destroyed.

An image can be assigned using the Form-Editor Properties window in the sidebar. For a Form object the Properties window shows a Picture property that can be assigned an image (bitmap, icon, cursor, GIF, JPEG, and metafiles) by double clicking in the value column. Behind the scenes the LoadForm command - that is used to load a Form created with the Form-Editor - will create a COM Picture object on behalf of the Form. The dynamically allocated Picture COM object is then assigned to the Form.Picture property. 
However, a program may also set a Picture object to the Form.Picture property explicitly in code.

The memory leak occurs when the Form is closed. In the process of destroying all resources the Picture object is simply forgotten. The memory for the image is never released and the reference count on the COM object never reaches zero. Not only the memory necessary to store the image, but the memory allocated for the COM object will remain occupied. The memory used to store a bitmap can be of a considerable size and after some time you may even run out of memory. This most likely will present itself after RUNning a program multiple times within the IDE.

The best place to set the Picture Object to Nothing is within the Form_Destroy() event sub. A program cannot release the object when the Form has gone (==Nothing).

The leaked memory can never be reclaimed, because after the Form is destroyed the pointer to the Picture's COM Object is zeroed out. Selecting 'Cleanup Resources' from the Project menu will not work.

16 November 2012

Windows 8: Download WinHlp32.exe

GFA-BASIC 32 for Windows works perfectly well under Windows 8 (Win32/COM). GB32 is the ultimate professional BASIC to write native Windows 32 desktop programs to be compatible with older OSes and the latest Windows OS Win 8.

However to be useful with Windows 8 you will need to display the older-formatted help file using WinHlp32.exe. This program is required to display 32-bit Help files that have the ".hlp" file name extension. To view .hlp files on Windows 8, you need to install this application.

Download it from Microsoft Download Center.

11 October 2012

An owned Form

Until of the older set of chm MSDN Library files (<= October 2001) were replaced by the newer MSHELP, the MSDN contained an article called "Win32 Window Hierarchy and Styles" by Kyle Marsh, September 29, 1993. This article explains how top-level windows (aka Forms) relate to each other.

Form is a top-level window
A Form window, whether is is created using OpenW, Dialog, or Form, is a top-level window by default. A top-level window is any window that is not a child window. Top-level windows do not have the WS_CHILD style. All top-level windows are also connected to the desktop window through the parent window handle, GetParent(). Top-level windows are connected to the desktop window as if they were child windows of the desktop, and can be considered children of the desktop in that parent/child navigation techniques can be used to move between a top-level window and the desktop window. All top-level windows are displayed in the client area of the desktop window and thus behave as if they were children of the desktop for display purposes.

The parent window
Each window, being a top-level or a child window, has a parent window. The desktop window occupies the first level of the windows hierarchy and top-level windows occupy the second level. Child windows, which are windows created with the WS_CHILD style, occupy all other levels. Child windows connect to their parent window in the same way top-level windows connect to the desktop window.

The owned window
Top-level windows can own or be owned by other top-level windows. One Form can own another Form. An owned window is always displayed above its owner in the Z order and is hidden when its owner is minimized. Try this and see what happens, you cannot display Form Win_1 on top of Form Win_2:

OpenW 1
Me.Caption = Me.Name
OpenW Owner Win_1 , 2
Me.Caption = Me.Name
Do
  Sleep
Until Me Is Nothing

Win_2 is owned by Win_1. Owned windows are not hidden when their owner is hidden. Thus, if window #1 is minimized, window #2 is hidden as well.
BTW you can set the owner using the designer in the Properties window. A Form has a an Owned property that can be set to True. After loading the Form using LoadForm the current active Form as referenced by Me will be set as the owner.

On the API level an owned window relationship is created by passing the window handle to the owner window in the hwndParent parameter of the CreateWindow() function. GB32 passes the window handle of the Form object specified in the Owner clause in the OpenW statement. To top-level window #2, window #1 is the owner, but not its parent window. The parent window for both Forms is the desktop window.
The GetParent() API function is not used to obtain the owner of a top-level window, that would return the desktop window handle. Insert the following lines before the start of the message loop. Both will return Null (0).

Print GetParent(Win_1.hWnd)
Print GetParent(Win_2.hWnd)

To obtain the owner GetWindow(hWnd, GW_OWNER) API function should be used. Insert these lines as well, and see how their results differ.

Print GetWindow(Win_1.hWnd, GW_OWNER)
Print GetWindow(Win_2.hWnd, GW_OWNER)

Finally, closing the owned top-level Win_2 won't automatically close Win_1. After Win_2 has been destroyed the current Form Me is set to Win_1. The global message loop will not end until Me == Nothing, which will not happen until Win_1 has been closed.

25 July 2012

Desktop Applications Again

PCWorld featured an article on changing habits of users of desktop, web, and smartphone software. Some quotes.

Cloud computing
"We had a thesis that people did not want to install software; that the cloud meant that people could use a browser to interact with software and would never have to install anything. We were completely wrong,” Jones says. “People love installing software"

The Web is out
No more Web-based applications: "But given what developers have discovered firsthand, we may see users clamor for native desktop apps, where they previously deemed Web apps to be sufficient."

Also read the blogs referenced in this article. GFA-BASIC 32 survived its time.

18 July 2012

How does GB32 define a word?

Did you ever realize how GB32 defines a word? Is it consistent across the different editor operations? Does a Ctrl-Left keyboard shortcut point to same word start as Ctrl-F? The Ctrl-F accelerator shows the Find Dialog Box where the Find What edit text control defaults to the current word. Is this the same word that is used in Ctrl-Left and Ctrl-Right actions? And what about the mouse double-click? What word does the editor use for the FindNextWord, to locate the next occurrence of that word? An example.

Global iVar1% = $4DC41C
Dim dd As Double = 2.45!

The next table shows the result of the 'current word' used to start the operation.

Word Ctrl Right Find Dlg Double Click
Global Global Global Global
iVar1% iVar1% iVar1 iVar1%
$4DC41C 4DC41C 4DC41C $4DC41C
2.45! 2.45! 2 2 or 45, depending on the position of the caret

Disturbing? At least confusing, because it isn't consistent. To me the whole module containing the editor's find and replace code seems a bit clumsy. (Maybe Frank Ostrowski was somewhat distracted during its development.) I can only guess what leaded to this inconsistency. The $AutoPostFix directive may have caused the decision to use the variable name without a postfix with the Find Dialog. Anyway, I don't like it. In my opinion all operations that determine a word should select the same word and the green marked words seem to be best candidate.

If there would be separate functions that return the current word, it would give me a starting point. Unfortunately, all operations mentioned above have their own getcurrentword algorithm. Another set back, by now I hoped to have had implemented the COM support I promised.

16 July 2012

Efficiently building a String

If you need to build a string by adding lots of pieces together, GFA-BASIC's string handling is relative slow because a new string is created in memory every time a new piece is added.

Often, alternatives are used like string builder functions. String builders allocate a string chunk and then uses memory copy methods to manipulate the data within the same string's memory. You'll find them in all frameworks and libraries of popular programming languages.

GB32 offers an efficient string builder as well, although it is bit hidden. The sprintf() function reserves a chunk of stack memory and uses memory copy functions to copy characters form the source string to a destination string. When it hits on a data placeholder in the source string it copies the data into destination string. Only when all characters and data are copied to destination buffer, the stack buffer in converted to a an actual String.

The difference between the two methods is illustrated below:

Dim sEdPos As String
' Assemble a caret position in a long
Dim EdPos As Long = MakeLong(12, 1)
' Put the string together
sEdPos = "Line: " + Dec(HiWord(EdPos)) + _
  " Column: " + Dec(LoWord(EdPos))
' Or use sprintf()
sEdPos = sprintf("Line: %d Column: %d", _
  HiWord(EdPos), LoWord(EdPos))

12 July 2012

(GB/VB)BASIC isn't dead

You are invited to visit "TIOBE Programming Community Index" at : http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

See the trends in computer language. Right now plain C enjoys the first position, with Java and C++ slowly going down and Objective-C going up. (Visual) Basic remains steady at position 6. Don't confuse it with VB.NET, which is located at position 15. But VB.NET has an increasing audience also.

07 July 2012

Write # or Print #

Why does the GB32 Hash type use Write to save the data from a hash table (= dictionary)?

The correct answer is: In contrast with Print #, Write # uses a non-locale format. In Basic we often choose Print # over Write #, but both have their own goal. It has even been suggested that Write is a relic from prehistoric BASICs. However, this isn't true. To be able to correctly read data from a file into variables using Input #, use the Write # statement instead of the Print # statement to write the data to the files. Using Write # ensures each separate data field is properly delimited.

Both, the Print # and Write # commands, can be used to write data to a sequential text file, however the commands cause the information to be written to the text file differently.

The Write # statement is used to write raw data to the text file as comma- delimited and has the following syntax:

Write #filenumber[,outputlist]

All data written to the file using Print # is internationally aware; that is, the data is properly formatted/converted using the appropriate decimal separator. The syntax is similar, but the result quite different.

Print #filenumber[,outputlist]

Difference 1 – Quotation marks
The Write# command places quotation marks around all text values (strings), but not numeric data. The Date fields are surrounded with #s (pound signs). 
The Input# command is used to retrieve values from a text-only file created with the Write# statement (removes quotation marks from strings).

The Print# command does not enclose the data in quotation marks.

Difference 2 – Delimiter
The Write # command separates two or more values on the same command line with commas.The Print # command separates two or more values on the same command line (separated by a comma) with a tab character.

Print # writes an image of the data to the file, you must delimit the data yourself so it prints correctly. If you use Tab (or a comma) with no arguments to move the print position to the next print zone, Print # also writes the spaces between print fields to the file.

Advantages of Write
When using Write # to write data to a file, several universal assumptions are followed so the data can always be read and correctly interpreted using Input #, regardless of locale:

  • Numeric data is always written using the period as the decimal separator.
  • For Boolean data, either #True# or #False# is printed. The True and False keywords are not translated, regardless of locale.
  • Date data is written to the file using the universal date format. When either the date or the time component is missing or zero, only the part provided gets written to the file.
  • Nothing is written to the file if outputlist data is Empty. However, for data, #Null# is written.
  • If outputlist data is Null data, #Null# is written to the file.
  • For Error data, the output appears as #Error errorcode#. The Error keyword is not translated, regardless of locale.

To get your memory refreshed, look at the following code:

OpenW 1
Write App.Name          // "NoName"
Write CFloat(3.2)       // 3.2
Write CDate(Now)        // #2012-07-11 12:42:33
Write CBool(0)          // #False#
Write CHandle(0)        // #Null#
Write CFloat(3.2), Date // 3.2,#2012-07-11 12:42:33

' Print inserts a space at the front of a number
Print App.Name          // NoName
Print CFloat(3.2)       //  3.2
Print CDate(Now)        // 11-7-2012 12:42:33
Print CBool(0)          // False
Print CHandle(0)        //  0
Print CFloat(3.2), Date // 3.2      #2012-07-11 12:42:33

Unlike the Print # statement, the Write # statement inserts commas between items and quotation marks around strings as they are written to the file. You don't have to put explicit delimiters in the list. Write # inserts a newline character, that is, a carriage return–linefeed (Chr(13) + Chr(10), after it has written the final character in outputlist to the file.

If, at some future time, you want to read the data from a file using the Input # statement, use the Write # statement instead of the Print # statement to write the data to the file. Using Write # ensures the integrity of each separate data field by properly delimiting it, so it can be read back in using Input #. Using Write # also ensures it can be correctly read in any locale.

12 April 2012

Ocx background color

Not all OCX controls support a .BackColor property to change the background color, although the control itself allows changing it. Here GB32 conforms to VB too much. Rather than adding a property to the implementation – which would require another patch in executable code – we can use a different approach.

All OCX controls share a user-defined structure to store the current settings of the wrapped control. Setting and getting property values is sometimes nothing more than changing and setting a member of the structure. Setting and obtaining the background value is one of them. The BackColor property stores its value in the member at offset $B8 of the start of object’s data block. The following code changes the background of a StatusBar Ocx.

Form frm1
Ocx StatusBar sb1
Trace SetBkColorControl(sb1, RGB(23, 56, 229))
Do
  Sleep
Until Me Is Nothing

Function SetBkColorControl(Ctrl As Control, Color As Long) As Long
  Const OffBackColor = $B8
  Local Long ObjPtr  = Long{*Ctrl}
  SetBkColorControl  = Long{ObjPtr + OffBackColor}
  {ObjPtr + OffBackColor} = Color
  ~InvalidateRect(Ctrl.hwnd, 0, 1)

  ' Notes
  ' 1 Using the IDispatch interface does not work
  '   when the GetTypeInfo doesn't support the
  '   the .BackColor property.
  ' BackColor is not a property of StatusBar
  If 0 Then Ctrl.BackColor = Color

  ' 2 When using XP Visual Themes the Visual Style
  '   theme takes over and gets precedence over
  '   GB OCX propery settings.
EndFunc

Note – Not all controls let their background set this way. Just experiment. 

03 April 2012

ImageList error signals missing manifest

When an application adds an Ocx ImageList to a Form using the Form-Editor an error might occur after compilation to EXE.

When the EXE is started the application stops executing displaying an ImageList loading error. This error occurs when the application is developed using a GfaWin32.Exe.manifest file. The manifest file forces the executable (named in its file name) to use the newer comctl32.dll version 6.x to apply more appealing visual styles. This DLL contains an updated load and save function for Image List common controls. When your application is compiled to a stand-alone the ImageList data is saved within the EXE using the ImageList_Write() API from the comctl32.dll version 6. Now when you launch the stand-alone EXE without a reference to the newer common control DLL, the ImageList_Read() from the older DLL is used. And, well… these functions are not compatible.

After compiling an application to a stand-alone EXE, a manifest file for the stand-alone EXE must be included, otherwise the executable will start without using the newer comctl32.dll. So, if you name your application “myapp.exe” you must include a “myapp.exe.manifest” file along with the EXE. You can easily copy the GfaWin32.exe.manifest file to the application’s directory and rename it by replacing the first part to the name of your application. A GB32 project would then include the following files:

myapp.g32
myapp.exe
myapp.exe.manifest

Note - It doesn’t hurt to include a manifest file for systems that don’t support comctl32 version 6.

05 February 2012

How a GLL Dialog # is created

The Dialog command used in a GLL editor extension is quite different than the Dialog in a normal GB32 application. How is that possible?

When one or more GLLs is loaded in memory, the IDE assigns the compiled GLL an array with pointers to the GB32 library functions in the runtime DLL (or Ocx). This is the same array with pointers each application gets when it is loaded. This way a call to a library function like Left$(), Dialog, or OpenW, etc, will reference the appropriate function in the runtime. When the GLL is loaded some of these pointers are replaced by addresses of functions inside the IDE executable. This way when the GLL invokes a dialog related function it will call the function inside the IDE, rather than in the runtime. All the dialog box related functions and commands are replaced. Examples are the Control command and the names of standard and common controls. You'll find an overview of the valid GB32 dialog box related statements and functions in the help file.

The GB32 dialog box related statements and functions are GfaBasic for Windows 16-bit-compatible. However, the window styles specified in the Dialog # command may come as a surprise. When you omit the style GB32 uses a default window style. GB32 processes as follows:

' Create a dialog box
Local Long dwStyle = 0
Dialog # 1, 200, 200, 536, 400, "" , dwStyle
EndDialog

'
' How GB32 sets the Dialog window styles.
'
If (dwStyle %& DS_MODALFRAME) Then
  Ex_Style = WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE

Else If dwStyle == 0                  ' Default Style?
  dwStyle = WS_POPUP | WS_DLGFRAME | WS_BORDER
  If Len(szTitle) == 0                ' No Title?
    dwStyle  = WS_POPUP | DS_MODALFRAME
    Ex_Style = WS_EX_DLGMODALFRAME
  EndIf
EndIf
'
dwStyle  |= DS_SYSMODAL       ' Top Most

The system creates the window (dialog box) with the WS_EX_TOPMOST style using CreateWindowEx(). The dialog has no owner or parent. It is modeless because it exist parallel to the Gfa_hWnd IDE window.
The WS_EX_TOPMOST style does not prevent the user from accessing other windows on the desktop.
This gives you the tools puzzle the appearance of GLL dialog box and might explain its unexpected visual style.

29 January 2012

Gfa_Setting() returns extra #0

Gfa_Setting is available as a statement and as a function. They are used as follows:

' Set/Create a subkey
Gfa_Setting("MySubKey") = mysetting$
' Get subkey value ($)
mysetting$ = Gfa_Setting("MySubKey")
' Delete a subkey
Gfa_Setting("MySubKey") = ""

The Gfa_Setting(subkey$) reads a value from the subkey under HKEY_CURRENT_USER\Software\GFA\Basic using the RegQueryValueEx() API function that retrieves the data for a specified value name associated with an open registry key. The advantage of using this API is that it doesn't require a data type. Even better, the function returns a code indicating the type of data stored in the specified value. The possible type codes are:

Const REG_NONE  = 0
Const REG_SZ  = 1
Const REG_EXPAND_SZ  = 2
Const REG_BINARY  = 3
Const REG_DWORD  = 4
Const REG_DWORD_LITTLE_ENDIAN  = 4
Const REG_DWORD_BIG_ENDIAN  = 5
Const REG_LINK  = 6
Const REG_MULTI_SZ  = 7
etc..

After invoking RegQueryValueEx GB32 checks the value in the type parameter and than decides how to copy the data to the GB32 variable used in the var$ = Gfa_Setting(subkey$) code. When the type value returned is REG_SZ or REG_EXPAND_SZ or REG_BINARY then GB32 uses the StrPeek(addr, size) function to create and copy the data to the string. The size of the data is also returned by RegQueryValueEx. Simple enough. But there is a catch that is overlooked:

"If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, then the data will also include the size of the terminating null character or characters."

GB32 uses the size value to invoke StrPeek and stores the terminating #0 character inside the string and sets the length of the string to the size of data + 1 for the #0 character. GB32 uses the same function for Gfa_BinSetting() and does not care for the value of the size.

Now, when you apply a string concatenation (str & str2) the second string doesn't seem to be appended to the first string. The including #0 character in the last position of the string makes the use of string functions awkward. When you pass that string to a function that expects a C-string the string data is read to the first #0 character. Internally, and externally on the API level, many functions expect a C-string and stop processing the string at first occurrence of a #0 character.  

For now, you best use ZTrim() on the return value of Gfa_Setting(). It seems not to predict how many #0 characters will be included. See underlined words in the quotation.

' Get subkey string value
mysetting$ = ZTrim(Gfa_Setting("MySubKey"))

20 January 2012

Gfa_Line doesn't always redraw the caret

The GFA-BASIC implementation of the editor-extension contains a large set of Gfa_* functions and commands. However, in contrast with the term 'editor extension', the Gfa_* statements are not restricted to the (text) editor only. The GFA-BASIC IDE application itself is divided into several entities such as an application part, a form-editor, a compiler, and a text-editor. Only a part of the Gfa_ statements apply to the text-editor only, and the most important terms in this respect are 'text-selection' and 'insertion point'.

The active point
The text-selection represents a selection in the code text in the editor window. Note that a selection can be a single point (the insertion point) or a contiguous range of text. Using Gfa_ commands you can move the insertion point, control which text is selected, and modify the contents of the selection. When the text-selection is restricted to a single point, Gfa_Line and Gfa_Col return its position. Also, since there is no selection, Gfa_Line equals Gfa_SelLine and Gfa_Col equals Gfa_SelCol.

Whether or not the text is selected, the Gfa_Line and Gfa_Col always indicate the active point. This is the point where all other text actions are performed. When a range of text is selected the insertion point can be located at the beginning or at the end of the selection.

Command Description
Gfa_Line Gets/sets the line where the insertion point is.
Gfa_Col Gets/sets the column where the insertion point is.
Gfa_SelLine Gets/sets the line at the bottom of a selection. The bottom line is not necessarily at the active end of the selection.
Gfa_SelCol Gets/sets the column at the bottom of a selection. The bottom line is not necessarily at the active end of the selection.

Moving the caret
The insertion point is represented by a caret, is a blinking line, block, or bitmap in the client area of a window. The caret typically indicates the place at which text (or graphics) will be inserted. The caret is a system resource and controlled by invoking Windows system API calls such as ShowCaret() and SetCaretPosition(). Each time the caret is moved SetCaretPosition() is invoked.
Programmatically, you move the caret using one of the following Gfa_ commands:

Command Key Press
Gfa_Left [count ] LEFT ARROW
Gfa_Right [count] RIGHT ARROW
Gfa_Down [count] DOWN ARROW
Gfa_Up [count] UP ARROW
Gfa_PageDown PAGE DOWN
Gfa_PageUp PAGE UP

count is optional. A Long that specifies the number of times you want to repeat this action. The default is one.

  • When executing one of these commands (or pressing the key(s)) the current selection is canceled before the action is performed.
  • These are relative movements. They move the caret relative to the current active insertion point represented by Gfa_Col and Gfa_Line.
  • Many key press actions are not represented by a Gfa_ command. For instance: word-left, word-right; end-of-line, start-of-line; etc.

So, how do you move the caret to an absolute position, rather than to a relative one? Within a text line the Gfa_Col = newcolumn command will do the trick. It cancels any selection and updates the caret position. By assigning 0 to Gfa_Col the caret moves to the start-of-the-line and by setting it to some large number (_maxInt) is moves to the end-of-the-line. Consequently, you expect Gfa_Line to do the same for lines. Well it does modify the current line, but it sometimes 'forget" to update the caret visually. Meaning, it forgets to put the caret into its new position.

Gfa_Line = doesn't redraw caret
The Gfa_Line = command differs in two aspects from all other commands. First it doesn't cancel a selection, and, secondly, it doesn't always redraw the caret in it's new position. It does however change the current line used for text operations, which is important because some text operations are line oriented. So, without displaying the caret at the new position (combined with a possible scrolling) the current active line can be changed to perform actions on the text line (Gfa_Update, Gfa_Text = , delete and insert lines, setting a bookmark, clipboard actions, etc). The problem is that the editor extension implementation lacks a command to update the caret explicitly.

Workaround to redraw caret
To explicitly make the caret visible after invoking Gfa_Line = we must take into account that as portion of the text maybe selected. So, first we must cancel any selection and after invoking Gfa_Line =  we must redraw the caret ourselves.

Global Gfa_SetCaret As Pointer Byte
Pointer(Gfa_SetCaret)       = $4DC41C
Global Const Ed_SetCaretPos = $409B99


' Cancel selection (if any)
Gfa_Col = 0 ' Set/Goto new line Gfa_Line = iGotoLine ' Set global flag (when in OnWMCommand) Gfa_SetCaret = 1 ' Or call directly ~StdCall(Ed_SetCaretPos)()

Most (if not all) IDE commands are processed in the IDE's OnWMCommand() function. This is a central routine to execute all accelerator key commands, menu commands, and the GLL's key and menu events. Besides this, it adds the commands to the UNDO-buffer and stores them in a keyboard macro when Gfa_IsRecording = True. At the end of IDE's OnWMCommand() function the global variable Gfa_IsCaret is checked to see if any of the executed commands did set this flag (to have the caret redrawn). If so, before returning from OnWMCommand() the IDE's Ed_SetCaretPos() function is invoked to redraw the caret and Gfa_SetCaret is cleared.

Now you have some background knowledge of Gfa_Line you have the tools to properly redraw the caret.

14 November 2011

GFA-BASIC 16 Bit Revival

The Frenchman Michel Goux has assembled all tools for a proper installation of GFA-BASIC for Windows 16 Bit (also known as Windows 3.1). All tools like IDE, Compiler, Linker, ExtTool, etc are to be downloaded into one new package. The package also includes tips for running the 16-bits version on more recent OS's like Vista-32 and Windows 7-32.

Unfortunately, it will not run on 64-bit systems? Maybe some have a solution for that? 

Go to the Michel's GfaWin16 download page.

You can also check-out my GFA-BASIC 16 page for downloading additional stuff.

08 November 2011

Apply XP Visual Styles

 

Running GFA-BASIC 32 under XP or better (Vista/7) it applies the Windows XP Visual Styles. The controls are drawn using a new look using a newer common control library. The IDE uses the newer look because of the presence of manifest file in the same directory as the GfaWin32.exe. When you create an EXE for your own GFA-BASIC application you must not forget to include a manifest file using the same name as your application.

Go to the CodeProject site for a brief introduction on Applying Windows XP Visual Styles.

For an implementation of a GFA-BASIC 32 project go to Peter Heinzig site for an example http://www.peterheinzig.de

02 November 2011

Dim ... As New Interface?

What does New mean when used in a declaration statement? Why is New limited to only 8 so called OCX types? What are OCX types anyway? Confused? Let us continue the story on COM.

OCX Objects
By now you know that in GFA-BASIC 32 OCX windows are, in fact, normal  windows and controls wrapped up in a COM Object. For instance, the very familiar OpenW # command still creates an overlapped application window, but it now also creates a COM wrapper of data type Form. Not only creates OpenW a window using the CreateWindowEx() API, but also allocates an additional block of memory  (using malloc) to store all COM related information for the Form object type.

A GFA-BASIC OCX object is a window wrapped in a COM object. An OCX object requires a window handle.

Information about the OCX 
An OCX object is also called an ActiveX control (hence the window handle). GFA-BASIC OCX objects must behave as proper ActiveX objects as described in the OLE/COM documentation. (Actually, OLE is an old name. On the way it changed to COM.)
To use/control a COM object it must somewhere describe (publish) its functions, called properties and methods. This is done by including a type library with a COM object. The type library also specifies whether the COM object supports the creation of an instance of that object. This literally means that the COM object provides a class (function) doing a malloc() to allocate the memory to store the object.

A type library specifies the properties and methods of a COM object (interface), and whether it can be created on behalf of the client (coclass).

Declaring a COM Object
If you have ever used OleView.exe to examine GFA-BASIC's COM objects, you may have noticed the term interface. The interface specifies the name of the COM type and the functions used to access it. The interface name always starts with a capital I - like IForm, ICollection, etc. When used in code the capital I is lost (VB convention, duplicated by GB32).

All the interfaces defined in the type library from the GFA-BASIC Ocx runtime DLL  are added to the GFA-BASIC 32 internal list of data types. (This list is accessible through the Gfa_Types collection.) The list of data types contains all primary types like Bool, Byte, Card, Int32, Long, Currency, String, etc. All names stored in the type-list can be used in a declaration command like Dim, Global, and Local.

There is small problem though. GFA-BASIC 32 doesn't add the COM types by loading them from the type library. The addition of COM types like Collection, DisAsm, TextBox, etc. is hard-coded into the IDE code. Unfortunately! Otherwise we could have easily added third-party COM references to our applications.

To understand how GFA-BASIC handles a declared COM data type we can compare a COM object type to a String data type. For instance

Dim MyName As String
Dim frm As Form

These declarations don't do much. MyName and frm are variable-names and both are an alias for a 4-byte memory location. Initially, the value stored at the addresses is zero (pointer is Null), meaning no additional memory has been allocated for these variables. The pointers remain Null until something is assigned to the variables. Because MyName is of type String, GFA-BASIC knows how to do that and how to interpret the pointer at the location MyName.

The same is true for COM data types. Declaring a COM data type does not create the memory for it. The frm variable references some memory-address, but it does not actually create a window on the screen. First the window must be created and then the variable can be assigned an object of type Form. For COM objects a special command is invented to assign one COM object to another Set:

OpenW 1
Set frm = Win_1

Most GFA-BASIC 32 COM objects are created implicitly. OpenW creates a Form object, OCX TextBox creates a TextBox object, and so on. In addition, OCX controls that require multiple subitems create collections. A ToolBar OCX creates a Buttons collection of Button items.
For implicitly created objects GFA-BASIC 32 is responsible for memory allocation, initialization, and releasing memory.

Some COM types are created explicitly when some data is loaded. The LoadCursor() function creates a MouseCursor object and LoadPicture() creates a Picture object. These functions can be seen as 'a kind of New'.

The New keyword
The New keyword is used with some COM types that are available at your request. These types are independent of any OCX type. The New keyword allows you to create an instance of the object while declaring it. In GFA-BASIC 32 there are only 8 of these COM types allowed.

Dim f   As New Font       ' IFont2
Dim sf  As New StdFont    ' IStdFont
Dim p   As New Picture    ' IPicture2
Dim sp  As New StdPicture ' IStdPicture
Dim cd  As New CommDlg    
Dim col As New Collection
Dim dis As New DisAsm
Dim iml As New ImageList
  • Font (IFont2) and Picture (IPicture2) are GFA-BASIC implementations of the standard COM types.
  • StdFont and StdPicture are the types from the standard OLE libraries. Font and StdFont are 'compatible'. A variable of type Font can be set to an instance of StdFont and vice versa. The same is true for Picture and StdPicture.
  • The CommDlg and ImageList objects can be created using the OCX command as well.
  • Once the object created with New goes out of scope the object is automatically released.
  • Objects can be released explicitly by setting the variable to Nothing.

Other objects are single-instance objects created at runtime. GFA-BASIC 32 prohibits the creation of additional, new objects of these types. There is only one instance of App, Screen, Printer, ClipBoard, Code4, Debug, and Err. The single-instance objects are released when the program is terminated.

23 October 2011

Cleanup after an exception

In the previous post Don't use END to terminate your application I discussed why End messes up the memory available to an application. The same is true for Assert and Stop (when you use to terminate the application).

Main exception handler
These commands raise an exception caught by the nearest exception handler. This might be a custom Try/Catch handler in your program, but most often it passed on to the applications main exception handler. The main part of a GB32 application is surrounded with a GB32 generated application-exception-handler that releases file handles and memory associated with GB32 data types. The OCX forms/controls are not part of the cleaning process. To properly release all COM object memory all forms have to be closed by an internal function Destroy_Form(). The forms in turn are responsible for releasing the OCX objects of the child windows (controls).

Custom handler
When you use a custom structured exception handling (Try/Catch) in your application the exceptions might be caught by your application's defined custom handler. Now the exception is not passed on to the main application-handler and no memory and file handles are released. When you are put in this situation, you can select the clean-up command from the IDE's menu to close all forms and file handles.

Cleanup opened Forms and files
GB32 includes a library function that can cleanup non-closed forms and files after an application terminates abruptly. The DLL-function is located in the runtime and is exported by ordinal #49. The function is automatically invoked when you RUN a program inside the IDE. But you can also invoke it by selecting 'Clean up' (shortcut Ctrl-L) from the IDE's menubar. The menuitem  executes the Gfa_CleanUp command that invokes the DLL cleanup function.

Why would you use Gfa_CleanUp when the same function is executed at the next RUN of your code? Well, programs that are terminated improperly (END, ASSERT, or any exception the program causes) leave with invalid memory-pointers and Gfa_CleanUp might fail (mostly likely it will).

Gfa_CleanUp executes within a exception handler itself and it first calls DLL function #49 to close the forms. If it returns properly it calls exported function #119 which cleans the file handlers. In short, this is what Gfa_CleanUp looks like:

Try
  G49_CleanUpForms()
  G119_CleanUpFiles()
Catch
EndCatch

When the G49_CleanUpForms() DLL function causes an exception the G119_CleanUpFiles() is never executed!

The G49_CleanUpForms() simply enumerates over the Forms collection to call each Destroy_Form() internal function. This function is responsible for deleting form-specific COM objects for Fonts, MouseCursors, Pictures, Icons, AutoRedraw bitmaps, Menus, controls, graphic settings, end other related COM objects. This is a very large function responsible for properly clearing (in the right order) all created COM objects. When something didn't work correctly in your application and it was terminated in an unstable state, it is most likely this function might fail. This might leave behind all kinds of allocated COM memory. In addition, the function might have generated an exception, most likely of an invalid memory pointer, and from the pseudo code above you then see that the file handles aren't released at all.

In conclusion,

  1. When you set up a new application use a proper message loop and try to created one main-form that owns other forms (set the Form.Owned property for the second and later forms in the Properties window). Closing the main-form will close all other forms.
  2. Don't use END and be careful with ASSERT. When an application terminates improperly try using invoke Gfa_CleanUp.
  3. Fix an exception error and make sure it won't happen again. Then restart the IDE to get rid of memory left-overs.

16 October 2011

Don't use END to terminate your application

Using the End statement is like waking up from a nightmare. After multiple 'runs' in the IDE the GFA-BASIC 32 IDE might stop working at all and crashes eventually. End does not behave as it did in previous versions of GFA-BASIC where it was safe to use it as the application termination command. In GB32 you should never use End to terminate a program. Here is why.

End is defined as to terminate the execution of an application immediately. End is never required by itself but may be placed anywhere in a procedure to end code execution, close files opened with the Open statement and to clear variables.

The End statement stops code execution abruptly, without invoking the any window events, or any other code. Code you have placed in the form_Destroy, form_Close, etc, is not executed. GB32 data types referencing allocated memory at runtime (strings, arrays, hash tables) are properly destroyed, and files opened using the Open statement are closed. Object references held by other programs are invalidated.

The End statement provides a way to force your program to halt. For normal termination of a GB32 program, you should close all forms. Then when you use the 'Do : Sleep : Until Me Is Nothing' loop your program closes as soon as there are no more forms open. This is the only proper way to run a program!

Note The same is true for Quit, Assert, and Stop. These commands do not terminate your application properly.

It is an exception dude
For the curious. When GB32 encounters the End, Assert, and Stop commands GB32 it inserts code that raises an exception event: GB32 inserts a call to the RaiseException() API. An “exception” is an event that is unexpected or disrupts the ability of the process to proceed normally. However, GB32 supports structured exception handling (Try/Catch) the exception is caught by the nearest exception handler. In most cases, and certainly when End is used to terminate an application, the exception is caught by GB32's main-program exception handler. This handler is implicitly inserted around the code in the main program part of a GB32 program. Each unhandled exception is eventually handled by this main exception handler. The handler does nothing more than clearing variables and closing file handles.

So, when GB32 suddenly stops working, make sure your code let the OCX forms and controls properly close their windows in a natural order.

Also Known As bugs
NOTE – Don't blame GB32 too soon! Their are little or no bugs in the memory handling of GB32. Without pointing any fingers..., it turns out that most GB32 blamers are frustrated programmers that can't get their program(s) to work. Often the program structure is wrong or the programmer is under-qualified. This then leads to mysterious mistakes, AKA GB32 bugs.

05 September 2011

CreateObject peculiarities

Why CreateObject isn't fully compatible to VB(Script).

Introduction
The GFA-BASIC 32 function CreateObject() creates and then references a COM automation object. Usually the function is used to control a target application (or documents or other objects supported by the target application). Controlling a target application through COM (or OLE) is called OLE Automation.

Understanding OLE Automation
OLE Automation is an OLE service for integrating applications. It enables an application to expose its functionality (MS Word), or to control the functionality of other applications on the same computer or across networks. As a result, applications can be automated and integrated with programming code, creating virtually endless possibilities. OLE Automation is the umbrella term for the process by which an OLE Automation controller sends instructions to an OLE Automation server (using the functionality exposed by the OLE Automation server), where they are run.

CreateObject returns a reference to an object determined by the class argument.

Syntax: Set object = CreateObject(class)

Depending on class, the reference may be to an existing object or to a newly created object. The object reference returned by CreateObject is (usually) assigned to an object variable of type Object. Assigning a COM object to a variable differs from assigning a value or variable to another variable. It always requires the Set statement (as opposed to the Let statement that is implicitly used by other assignments).

Note - Using CreateObject (or GetObject) to set the Object-variable to the target application, or some object that the target application supports, establishes a reference to the target application's Application object. Then the object variable in the sourcecode can use the methods and properties of the corresponding object in the target application.

The ProgID of an automation object
To create an automation object the class argument of CreateObject(class) can be a string of the form "appName.objectType". A string using this format is called a ProgID.
A ProgID is a string that gets converted to a CLSID (a 128-bits GUID value) by looking up the string in the register. Note that the automation object can only be found when it has registered itself using the same string format "appName.objectType". This happen,s for instance, with MS Excel which registers itself as "Excel.Application". And Internet Explorer as "InternetExplorer.Application". These applications can be referenced through automation as follows:  

Dim obj As Object
Set obj = CreateObject("Excel.Application")

The class argument
In GFA-BASIC 32 the class argument may also specify CLSID either as a string (1) or as GUID constant (2). For instance:

Dim obj As Object
Set obj = CreateObject("7b55b57b-cd72-449d-82eb-a02e0964e3eb")

GUID gd = 7b55b57b-cd72-449d-82eb-a02e0964e3eb Set obj = CreateObject(gd)

Remarkable features of GFA-BASIC's CreateObject() are:

  • The class$ argument can specify a GUID value as a string.
  • The parameter can specify the address of GUID user-defined type. (The GFA-BASIC statement GUID name = xxx..-..-.._..-...x defines a constant (Long) as the address to a GUID value.
  • The syntax does not support the second optional parameter server as is described in Help documentation.
  • The language locale of the target application can be set prior to invoking CreateObject() using Mode(Lang) = .
  • The CreateObject() function is not 100% VB(Script) compatible.

CreateObject 's compatibility
In COM terms CreateObject() connects to a coclass at run time using the IDispatch interface (late binding). The Object variable type performs property and method operations using the IDispatch interface functions. Once you’ve created a new object and returned a reference to it, you can work with the object in GFA-BASIC in the same way you would work with any other object. That is, you can set and retrieve the object’s properties and apply its methods. To be an automation object the object must support IUnknown and IDispatch. All COM objects support IUnknown, because that defines them as a COM object. See this post on COM interfaces.
The IDispatch interface defines late binding and allows a client to call the methods and properties on the server. Consequently, without supporting an IDispatch interface a server cannot be called an automation controller. GFA-BASIC 32 consequently asks the automation object for an IDispatch interface and only connects to the coclass when it acknowledges that it supports an IDispatch interface. Otherwise it generates a runtime error.

VB(Script) are wrong
However VB and VBScript implement CreateObject differently, more flexible maybe, but essentially wrong. At the heart of creating an object is the OLE function CoCreateInstance(). It takes a pointer to the interface the program wants to use to communicate with the object. Since 'OLE automation' is required, it seems logical to pass the GUID for the IDispatch interface (as GFA-BASIC 32 does). But VB(Script) doesn't. The VB-CreateObject passes the GUID for IUnknown which every COM object supports, because IUnknown is what an object makes a COM object in the first place! So in VB CreateObject always works, even when it shouldn't. But the object reference returned by VB's CreateObject is not guaranteed to support IDispatch.

Porting VB to GB
This causes trouble when porting a VB program to GB. Implicitly CreateObject calls the object's QueryInterface(), asking it if it supports IDispatch. A correct behavior for QueryInterface(0 would be to reply for each interface it supports. Badly written OLE automation objects sometimes forget to reply for all interfaces it supports (especially IDispatch). One of them is Microsoft's type-library-reader-tool  "TLI.TypeLibInfo". CreateObject fails with GB where VB does return a reference:

Set TLInfo = CreateObject("TLI.TypeLibInfo")

When GB's CreateObject doesn't work where VB(Script) works, don't blame GFA-BASIC 32, but the COM programmer instead. BTW in these cases you must use the CoCreateInstance() API.