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.