06 October 2013

Debug is a runtime object

Closer examination of the Debug object reveals some interesting facts. Although the GFA-BASIC IDE offers some alternative actions to open the debug window, it is not an object owned by the IDE.

The Debug object is completely embedded (data and methods) in the GfaWin.Ocx, which is GFA-BASIC’s runtime. The Debug object is not dynamically created like other COM objects. You don’t use the Ocx command or a New clause in a Dim statement. The Debug object is a global (static) object located in the data section of the DLL. Other examples of static runtime objects are App, Screen, Printer, Forms, and Err.

Note – Most COM objects are – by nature – dynamically created at runtime. In GFA-BASIC they are either allocated using the Ocx (or OcxOcx) command, or with the New clause in a Dim statement. These commands create and allocate memory on runtime. Like the String datatype, a COM object is always stored and accessed by a pointer. A COM object variable occupies only 32 bits and points to a block of memory allocated from the free store. When the variable contains the value 0 (not pointing to a memory address) the object is said to be Nothing. There are some more functions that implicitly create dynamic COM objects, like LoadForm and LoadPicture. Each COM object created by a GFA-BASIC command is destroyed by GFA-BASIC, whether it is a static object or dynamic object.

The Debug object manages the window
Thee Debug object isn’t allocated at runtime, it is available all times. You can immediately start invoking properties and methods on the Debug object. Although the purpose of the debug output window is to display state information at runtime, the Debug object is mainly used to control or manage the debug output window. The debug output window is a modeless, top level, non-owned window with one child control; a standard EDIT control. Many properties and methods control the appearance and behavior of the debug output window and/or its edit control. For more information of the properties and methods look at the descriptions in the help-file or press a dot after the Debug keyword in the editor. Most of them speak for them selves.

Like your application can use the Debug object, the IDE can use the output window as well. The IDE uses the same static Debug object as your application. Any changes the IDE makes are reflected in your application, and vice versa. For instance, when the IDE hides the debug output window, your application must re-show it explicitly.
The IDE offers you two alternatives to show the output window.

  1. By selecting the IDE menu bar View | Toggle Output Window [Alt+3] at design time.
  2. By selecting the tray icon popup menu item at run-time.

Your application can provide its own means of operation to open the debug output window. The debug window could be used for logging, without actually showing the window.

So, whether your application or the IDE controls the debug output window, they both use the same static Debug object in the runtime. Therefor it is important to know what happens in the background.

The Debug thread
First of all, at the first opportunity – when one of the Debug object’s properties or methods is invoked – a new thread is created. As with all good threads it is not destroyed before the application ends; that is when the DLL runtime unloads from memory. When the GFA-BASIC IDE starts executing the thread isn’t started immediately. Only when your GFA-BASIC application, a GLL editor extension, or one of the IDE commands to show the debug window, are applied the thread is actually started.

The debug thread registers the DebugClass window class and creates an instance of the window class (top-level). Note that the window is displayed in the taskbar. Once the window is created the thread enters an infinite message retrieval loop (inside the thread) and handles the dispatching of messages to the DebugClass window procedure, located in the runtime. The window procedure itself is responsible for responding to the messages of the debug output window. One of the messages the window procedure must handle is the WM_SYSCOMMAND, because the debug window thread adds a system menu item to the window (Always on Top). The window procedure either responds by setting the top-level window to the top of the top-level-windows Z-order (HWND_TOPMOST), or by setting it to the bottom of the Z-order (HWND_BOTTOM), which by the way doesn’t mean that it will disappear immediately.

The position is saved automatically
Another message the thread’s window procedure handles is WM_EXITSIZEMOVE. This saves the (new) position of the ‘DebugClass’ window in the registry. It saves the position as a binary value from a RECT structure. It is saved under the a sub key name of ‘DebPos’ in ‘HKEY_CURRENT_USER/Software/GFA/Basic’. When this key doesn’t exist, it is created. When your customers application uses the debug window, the position will be saved in this registry key of your customers PC. Also note that your customer has the ‘Always on Top’ option available. This setting isn’t stored in the registry. (BTW the window position isn’t saved when the output window is in a minimized or maximized state.)

Give it an owner
I find the debug window - unowned toplevel - completely living on its own quite annoying. I think it should be owned by the main window (and thread) of the application that is using it. In case of GFABASIC this should be the Gfa_hWnd window, in case of a customer application, your main windows handle.

~SetWindowLong(Debug.hWnd, GWL_HWNDPARENT, Gfa_hWnd)
Assert GetWindow(Debug.hWnd, GW_OWNER) == Gfa_hWnd

As an example I added the Assert line. I was a little unsure what exactly setting GWL_HWNDPARENT would do and tested the result for what I expected it to do. Anyway, giving it an owner removes the Debug window from the taskbar and makes it behave like its owner. When the owner (Gfa_hWnd) is minimized, the debug output window is minimized. When the owner is (de-)activated the output window is de-activated.

Give it a different font
The edit control is the one and only child window of the debug output window. It has ID number 1 and its window handle is obtained using:

hEditCtrl = DlgItem(Debug.hWnd, 1)

Since it uses a non mono space font you might want to create your own font using API functions and then assign it to the edit control.

SendMessage hEditCtrl, WM_SETFONT, hFont, MakeWParam(1,0)