29 October 2009

Modify the Windows style

For a project I needed to change many window style bits in one session. For this I developed two procedures, one to change the GWL_STYLE and one to change the GWL_EXSTYLE window styles.
$Export Proc Modify*

Procedure ModifyStyle(hWnd As Handle, lRemove As Long, lAdd As Long)
Local Long Style = GetWindowLong(hWnd, GWL_STYLE)
Style &= ~lRemove
Style |= lAdd
~SetWindowLong(hWnd, GWL_STYLE, Style)
~SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_DRAWFRAME)

Procedure ModifyExStyle(hWnd As Handle, lRemove As Long, lAdd As Long)
Local Long ExStyle = GetWindowLong(hWnd, GWL_EXSTYLE)
ExStyle &= ~lRemove
ExStyle |= lAdd
~SetWindowLong(hWnd, GWL_EXSTYLE, ExStyle)
~SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_DRAWFRAME)

As you can see, I collected these in a $Library (.lg32). I'm a big fan of libraries and whenever possible I put general subroutines in a compiled module.

I'm aware of some problems with loading of LG32 files, I had them in the past as well. However, and I don't know why, I don't have them anymore ...

26 October 2009

LabelAddr in a GLL

In two recent posts (here and here) I discussed the problems with subclassing the Gfa_hWnd window in a GLL. In fact, it turned out to be impossible using 'normal' Windows API functions. Another solution was required.
The final solution I developed is based on the MS Detours technique from MS Research Labs. However, not even this highly tricky technique worked for me. The GLL still couldn't be unloaded using the Extension Manager Dialog box. Then, I adapted the Detour technique and configured it in such a way that unloading the GLL isn't a problem anymore. Almost two years ago I first started to study the Detours technique, and finally I came up with a solution to full fill my needs.
The adapted method is based on a GFA-BASIC 32 assembly routine, which is injected into the main window procedure of Gfa_hWnd inside the code of the IDE. To complete my mission I had to figure out the address of a label inside a GFA-BASIC subroutine. The injected code has to jump (jmp) to the label's address. However, the addresses of labels in GLLs (GFA Editor Extensions) are not known in advance.
Both the ProcAddr() and LabelAddr() functions are filled in by the compiler at compile time. In addition, a relocation-table is stored with the compiled program. In contrast with loading an EXE, which is performed by the OS, the addresses in a GLL are relocated by the GFA-BASIC 32 IDE when the GLL is loaded. The IDE is capable of adjusting the ProcAddr addresses inside the GLL, but it doesn't adjust the hardcoded LabelAddr values.
It takes a trick to obtain the relocated address of a label. The GLL is loaded into memory and addresses are calculated using an offset. When we know the offset value the hardcoded label addresses can be calculated. The function Gfa_LabelOffset in the next code, does just this. Since the value is constant for the entire GLL, it is stored in a static variable.
Function Gfa_LabelOffset() As Long
Static LabelOffset% = 0
If LabelOffset% == 0
GetRegs : 1
LabelOffset% = _EIP - LabelAddr(1)
Return LabelOffset%

The function can be used to obtain the real location of a label as follows:

Local stubAddr% = LabelAddr(stub) + Gfa_LabelOffset

The LabelAddr(stub) returns the hardcoded value set by the compiler. The real location is then adjusted by the offset value used to relocate all addresses in the GLL.

20 October 2009

EXE behaves different

"My program runs fine in the IDE, but it freezes when the compiled EXE is started." What is happening?
In the past ten years this message pops-up once and a while. At the same time this message is never repeated. The developer reviews his or her program and recognizes a programming bug. Then after the bug is solved, the program runs without any problem. Until now I never (!) encountered a compiler bug that resulted in an erroneous EXE.
Then, how is it possible that the EXE behaves different than the IDE-program? Some things to consider.
  1. The most logical error is a programming error (90% chance).
    I noticed a problem in user-defined types, for instance a member array isn't properly dimensioned, or a Boolean member is used (see the Help File for notes on using Booleans in a Type). Check your Types, but even better review the initialization code of your program.
  2. In Mysterious Errors I described a theory about errors that cannot be reproduced. But again, the programmer is to blame.
  3. The compiler inserts Trace-code between each code line in the IDE. You can disable this with $StepOff and get the same compiler result as an EXE.
  4. An EXE might behave different because the Compiler setting  Full Branch Optimization for EXE is enabled. This is the only option that produces a different result. (1% chance).
  5. Make sure you are aware of the ByRef Bug for Sub procedures. Insert ByVal and ByRef in the procedure headings. Better: use Sub only for event subs.
All in all, look for the problem in your code, not in the compiler. If an EXE works with one OS, but doesn't with another, than we have a possible bug!

14 October 2009

Difference between _Message and _MessageProc

There are two somewhat strange, certainly badly documented, event subs for forms: the Form_Message and Form_MessageProc event. Although they sound much alike, they are quite different. Both events are invoked from inside the window procedure of the form. However _Message() is called only for a handful of messages, but _MessageProc() is called for all messages, regardless of their origin. These subs are defined as:

Sub frm1_Message(hWnd%, Mess%, wParam%, lParam%)
Sub frm1_MessageProc(hWnd%, Mess%, wParam%, 
  lParam%, retval%, ValidRet?)
The _MessageProc event sub With the _MessageProc Sub you can actually filter or influence the behavior of the GB32 window procedure for the Form window class. The _MessageProc is called before GB32 handles the message itself (more or less, maybe later more on this). The sub obtains six message parameters. The first four are the same as defined for a Windows API window procedure. Every message, whether it is obtained from the message queue (the posted messages) or by a direct call from any other source (for instance through SendMessage), is handled in the window procedure. The hWnd parameter is the window to which the message is sent (we already know the objects name: frm and we could obtain the window handle from the object, that would have lowered the number of parameters by one). The Msg parameter is the message number—which is usually a constant such as WM_ACTIVATEAPP or WM_PAINT. The wParam and lParam parameters differ for each message, as does the return value; you must look up the specific message to see what they mean. Often, wParam or the return value is ignored, but not always. The _MessageProc() event sub has two additional parameters (ByRef) that allow you to return a value. For instance, when you want GB32 not to handle a certain message you can set the ValidRet? Boolean variable to True and provide a return value by setting RetVal%. What value RetVal must have is defined in the Windows API SDK. It often says something like: "If you handle this message return zero (or..)". Now let us look at an example. Suppose you want to store the window coordinates of OpenW #1 in the register so the application can use these values to open at the same place. In GB32 you must then handle the sub events Form_ReSize and Form_Moved to store the coordinates. As an alternative you could use Form_MessageProc and handle the WM_EXITSIZEMOVE message as follows:
Sub Win_1_MessageProc(hWnd%, Mess%, wParam%, _

      lParam%, Retval%, ValidRet?)

 Local Int x, y, w, h

 Switch Mess


   GetWinRect hwnd, x, y, w, h

   SaveSetting "MyComp", "ThisApp", "Position",

        Mkl$(x, y, w, h)

   ValiRet? = True : RetVal = 0


Note the Form_MessageProc() actually _is_ the subclass window procedure for the GB32 windows (Form, Dialog, OpenW, ChildW, ParentW). Subclassing is a built-in feature of GFA-BASIC 32. As such the the OCX control Form is perfectly suited to write custom controls.
The _Message event sub The Form_Message is different: you cannot filter messages and return values, you can only respond to a handful of messages, to WM_SIZE, or WM_PAINT for instance. Why these? Because these are posted messages, messages that are retrieved from the message queue. Note that most posted messages have a accompanying sub event. For instance, a WM_SIZE message results in calling the Form_ReSize event sub. You can use the _Message sub to handle many messages that are otherwise handled in these event subs. All you need to know is which messages are posted. These are all input messages like key and mouse messages, window management messages like moved, sized and wm_paint. You must then, just as in _MessageProc, create a Switch/Case structure to respond to the message. The main disadvantage is that you must interpret the wParam and lParam parameters yourself... The order in which the sub events are called You can easily test in which order the sub events are called. For posted messages, those that are retrieved from the message queue using Sleep, the _Message() event is called before any other sub. Then the message is 'dispatched' to the window procedure and the _MessageProc is called. And at last, the event sub is invoked. For a WM_SIZE message the sequence is: Win_1_Message() Win_1_MessageProc()
Win_1_ReSize (This article was previously posted on the GFA-BASIC Google Pages. It has been edited to remove errors.)

The Sub-ByRef flaw

A call to a Sub procedure using by reference arguments might not update the correct variable. To understand this behavior be sure to understand the differences between ByVal and ByRef arguments. This is explained in the next tip 'Passing values ByRef or ByVal'. Here you have learned that a Sub by default takes arguments as ByRef, making ByRef optional. Unfortunately this is not always true.

When an argument is passed by reference, the procedure is passed the address of the argument variable. When the argument is a global variable the address of the variable is passed as expected. In the following situation the ByRef clause is omitted and the global variable is updated to 100 correctly.

Global a% = 50
Print a%   ' 100
Sub MySub(b%)
  b% = 100

When a second Sub is called from within MySub passing b% by reference, b% is  not updated as expected. The variable b% is passed by value, rather than by reference!

Sub MySub(b%)
  b% = 100 : NextSub(b%) : Print b%  // still 100
Sub NextSub(c%)
  c% = 200    // unexpected: c% is ByVal argument

Why this is happening is unclear, but it is easily repaired by using ByRef explicitly in Sub headings.

Sub NextSub(ByRef c%)

Loading a library: fail or success?

When the $Library directive is parsed (triggered when the line is changed) the specified library is loaded and the exported items are added to the Imports tab in the sidebar. When the file is indeed located and opened a message of success is written to Debug Output Window saying "Loading lg32filename".
Any errors caused by the $Library are then due to a corrupt lg32 file.

The "Load lg32 error" message
Programs that contain references to libraries using the $Library directive may occasionally show a "Load lg32 error: filename.lg32" in the status bar. This message is generated when the specified library can't be located at the specified path or, when a path is omitted, in the current active directory, 'My Documents\lg32', or in directories that are searched using the SearchPath API function.
This message should therefore be called: "Lg32 file not found".

Reload project to reset current directory
When a lg32 can't be found the most obvious reason is an unexpected active directory. Sometimes when working at a project the active directory is changed and the project's directory isn't the current any longer. To check for this, simply reload the project, because this sets the current active directory back to the project's folder.

Set default path for Libraries
An undocumented feature is the possibility to set a default path for libraries in the registry. The registry key HKEY_CURRENT_USER\Software\GFA\Basic must contain the key "lg32Path" specifying the default path for the libraries. The key is not present at default, so you must add the key yourself.

07 October 2009

Hooking to handle Gfa_hWnd messages?

In the Why you cannot subclass Gfa_hWnd I explained why the Gfa_hWnd cannot be subclassed to extend the editor's functionality.

Still, I need to intercept and modify messages for the main IDE window Gfa_hWnd. Some kind of subclassing is required. The other feature Windows offers is the 'hooking', known as Windows Hooks. "Hooking is a sort of subclassing, only it isn't associated with a single window, but with a thread or even the whole system. It's a kind of filter of Windows' messages that allow you to override, or add functionalities when a particular message is received." Sounds promising, doesn't it? 

Hooking Example

In fact, I did create a test program. First, two hooks are installed, the WH_GETMESSAGE and the WH_CALLWNDPROC hooks. There are other hooks, but these seem to be the most logical ones. Just to show how hooking might be done in GFA-BASIC 32, I present some code.
After declaring two global Handle variables, the hooks are initialized as follows:

Global Handle hGetMsgHook, hCallWndHook
' Process or modify all messages (of any type) for the system whenever a
' GetMessage or a PeekMessage function is called (WH_GETMESSAGE).
hGetMsgHook = SetWindowsHookEx(WH_GETMESSAGE , ProcAddr( GetMsgProc), App.hInstance, GetCurrentThreadId())

' Process or modify all messages (of any type) whenever a SendMessage
' function is called (WH_CALLWNDPROC).
hCallWndHook = SetWindowsHookEx(WH_CALLWNDPROC , ProcAddr( CallWndProc), App.hInstance, GetCurrentThreadId())

From this code you can see that both hooks are thread wide, meaning all messages for the thread are passed on to the hook filter functions GetMsgProc() and CallWndProc(). This is one of the reasons I didn't use the hook technique to 'subclass' the Gfa_hWnd window. Later more.
The application defined hook filter functions are defined as:

Function GetMsgProc(ByVal idHook As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  Dim msg As Pointer MSG
  Pointer(msg) = lParam
  If msg.hwnd == GfahWnd && msg.mess > 0 && msg.mess <> 275
    Trace msg.mess
  Return CallNextHookEx(hGetMsgHook, idHook, wParam, lParam)
End Function
Function CallWndProc(ByVal idHook As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  Dim msg As Pointer MSG
  Pointer(msg) = lParam
  If msg.hwnd == GfahWnd && msg.mess > 0 && msg.mess <> 275
    Trace msg.mess
  Return CallNextHookEx(hGetMsgHook, idHook, wParam, lParam)
End Function

The GfahWnd variable contains the Gfa_hWnd value and MSG is an API user-defined type to store the message parameters. All well known I presume.

Is hooking a solution?
Does Windows Hooking offer a solution to my problem? The answer is no. Subclassing goes further than hooking. Subclassing functions can return values in response to messages, hooking filter functions cannot. Another issue is the use of thread wide filter functions, that are called for every message that is sent through SendMessage or retrieved with GetMessage. Not only do these hook functions significantly drain on system performance, they might also interfere with the application at hand, that is, the program currently being developed and run in the IDE.

I need another solution.

05 October 2009

Why you cannot subclass Gfa_hWnd

In the course of interpreting the disassembly of both the GfaWin32.exe and the run-time I created quite some editor extension procedures. Most of them are rudimentary and should be developed further, but some of them are very useful (at least to me). I always hoped to put them together in one single GLL file and make that GLL available. However, I never came to do that. I had a problem I didn't understand, and thus didn't know how to solve. After unloading that GLL the IDE crashed.

To add new functionality a Windows developer will most likely start by subclassing the window. In fact, subclassing is the most elementary of all modification techniques. I started by subclassing both IDE windows, the main IDE window Gfa_hWnd and the editor window Gfa_hWndEd. The subclassing was performed in the Gfa_Init() procedure and undone in Gfa_Exit(), which seemed logical at the time. However, as said, this caused a (fatal) problem that at that time I couldn't resolve and I reserved it for a later moment to handle. I was convinced it was something I simply overlooked and at that stage it wasn't really an obstacle. The GLL was automatically loaded when the IDE was started and unloaded when it was closed. I never unloaded the GLL by hand using the Editor Extension Manager dialog box.

Sometimes I was annoyed by the problem and tried to find out the cause of it. It turned out that when the GLL was unloaded in the course of closing the IDE, the un-subclassing was performed after the main window was closed. So, the Gfa_hWnd window didn't exist anymore and re-installing the old window procedure didn't cause any problem. But, when I unloaded the GLL when the IDE was active, using the Extension dialog box, it always crashed the IDE. The undoing of the subclassing of Gfa_hWnd was the problem. In fact, you cannot subclass the Gfa_hWnd in a GLL.

My problem was that I didn't understand exactly what I was doing when I was subclassing a window. Let me illustrate by describing the path a WM_COMMAND (a posted) message follows to where it gets actually handled. Here is what happens when you select the GFA-BASIC 32's Editor Extension Manager dialog box, either from the menu-bar or through a shortcut First the OS puts a WM_COMMAND message in the application queue. The program obtains the message in an endless GetMessage loop and dispatches the message to the window procedure (note: the one I tried to subclass). The window procedure invokes the modal dialog box and a new message loop is started to handle the dialog box. Now, after subclassing, the OS invokes the new window procedure rather than the old one, and requires the new procedure to execute the old window procedure using the CallWindowProc() API. So, the old window procedure, which handled the WM_COMMAND, is called from the new window procedure located in the GLL. In fact, when the dialog box is put on the screen and a modal message loop is started, the program is still executing the new window procedure inside the GLL! After closing the dialog box the program will want to return to the location it was called from, the place where CallWindowProc() API was invoked. You see the problem now?

The old window procedure was invoked from inside the new window procedure (located in the GLL), which was just unloaded using the Editor Extension Manager dialog box. There is no place to return to! Ergo, the most elementary technique, subclassing a window to add new functionality, is not possible for Gfa_hWnd in an editor extension.