My friend used to tell this story at developers' conferences. He had this application installed throughout Florida. Working for himself, he was the developer, support technician, and telephone operator all rolled into one. He had this one user (he called her Marge, but I'm sure the name was changed to protect the innocent) who was a support technician's nightmare. Now this is back in DOS days, when the best developers could hope for when a run-time exception occurred was to report the source code line number to the user. Marge, of course, was continually running into difficulties, but could never report the correct line number. The last time my friend heard from Marge was when he told her "the next time an error occurs, call me immediately. I'm not sure where I'll be, but I know the area code is 904 and the telephone number has a 0 in it".
What's the point of this story? Well our ability to track and report errors has increased tremendously under Windows, and Delphi's VCL (and I'm sure CLX as well, though I hasten to add I've never used it) provides all the hooks we need to trap exceptions, and to log certain information about the application state when the error occurred.
Most of us have probably written Try / Except blocks, as in:
// Code Section A
// Code Section B
If any exception occurs in the code fragment labeled "Code Section A", Delphi will transfer control to the code fragment labeled "Code Section B". Note that this only happens if there is an exception. One way to trap all exceptions in your code, then, would be to use a Try / Except block in every event handler you write, but this rapidly becomes tedious and there is a better way.
The TApplication class is our savior. Take a look in Help at the TApplication class, and you'll see as well as declaring application-wide properties (such as ExeName, HelpFile, MainForm, etc.) and application-wide methods (such as Run, CreateForm, Terminate, ProcessMessages, etc.), it defines a number of application-wide events. Table 1 lists some of these events.
OnHint Occurs when the mouse pointer moves over a control or menu item that can display a Help Hint
OnShowHint Occurs when the application is about to display the hint window for a Help Hint
OnMinimize Occurs when an application is minimized
OnRestore Occurs when the previously minimized application is restored to its normal size
OnActivate Occurs when an application becomes active
OnDeactivate Occurs when an application becomes inactive
OnException Occurs when an unhandled exception occurs in the application
Table 1 - Application-Wide events
You can write event handlers for these events in the manner you write events when the user clicks on a push button or a menu item. How do you write these events? The easiest way, since Delphi 5 I believe, is to use a TApplicationEvents non visual component on your main form. You'll find it on the Additional tabsheet of the components toolbar. Surprised to see it? So was I a few months ago when one of my students pointed it out to me (previously I'd been setting the event handler pointer in code in the main form's onCreate event, but I'm a great believer in simplicity (laziness my wife calls it), and TApplicationEvents is certainly easier to use). So drop a TApplication events component onto your main form and take a look at the events tab of the object inspector. TApplicationEvents is simply a component which makes it easy for you to write these application-wide events. And now you have no excuse not to display hints for menu items in your status bar - use the onHint event.
We're interested in the onException event, of course. Note from table 1 that Delphi will fire this event only when an unhandled exception occurs. This means that if you have your own exception handling in the form of Try / Except blocks, Delphi will not fire the TApplication onException event. This event, then, is used to replace Delphi's default exception handler (the thing that displays a modal window in your application with the error icon and the error class).
So what can you do in this event? You can display the error to the user (although that's what Delphi's VCL does by default so there's little point in using the event if that's all you are going to do). What a lot of developers like to do is log the exception to a file. You could use the TIniFile class to log the error to an INI file - saving information such as the data and time, the user name, and other pertinent information. What's often useful is a screen shot of the current form that was executing when the error occurred. This is surprisingly easy to obtain. The TForm class (from which all forms descend) has a method called GetFormImage which returns a TBitMap object. The TBitMap class has a method called SaveToFile, so it's easy to capture an image of the current form and save it to a file. To see this in action, drop a push button onto the form and in the onClick event handler write the following code:
procedure TForm1.Button2Click(Sender: TObject);
formImage : TBitMap;
formImage := self.GetFormImage;
Now of course, this captures the image of the form which contains the button. If you want to capture the currently selected form in the application onException handler, you need to use the Screen object. Another global variable, Screen is an instance of the TScreen class (take a look in help). TScreen.ActiveForm is a pointer to the active form, so the following code, the onException event of the Application object, will capture the image of the form which was active when the exception occurred.
procedure TForm1.ApplicationEvents1Exception(Sender: TObject;
bitMap : TBitMap;
bitMap := Screen.ActiveForm.GetFormImage;