I always thought Delphi’s global exception handler was a great feature. It allows your program to continue after an otherwise unhandled exception would have caused it to terminate. Typically in a serious application you would assign your own global exception handler, or used one of the great 3rd part exception handlers like madExcept or Exceptional Magic (I love that name!) They both provide a nice dialog, stack trace, logging and reporting.
Well it turns out that if you want to be Microsoft Windows Vista Logo certified, then you need to crash your application on certain exceptions.
Applications must handle only exceptions that are known and expected, and Windows Error Reporting must not be disabled. If a fault (such as an Access Violation) is injected into an application, the application must allow Windows Error Reporting to report this crash. (from requirement 3.2 Resilient Software: Eliminate Application Failures)
Microsoft’s rational for this requirement is the ISV will receive the error report Microsoft collects for them. I guess most software developers don’t have access to tools like we do in Delphi to catch exceptions and log them for us.
So short of tossing out the Forms unit and writing everything from scratch, how can you get around the usefulness of the global exception handler.
My first thought was to create a custome application exception handler by placing a TApplicationEvents on your main form and assigning the OnException event. In the event include the line
This will pass an exception up to the operating system and terminate your application, at least when I tested it in Delphi 2007. When I tried it in Delphi 7, it didn’t work right.
A more elegant and involved solution:
First you need a couple global variables:
var GlobalExcept: Exception; GlobalExceptAddr: Pointer;
And a global exception handler:
procedure TForm1.ApplicationException(Sender: TObject; E: Exception); begin if E is EAccessViolation then begin // Keep the exception object from being destroyed! AcquireExceptionObject; GlobalExcept := e; GlobalExceptAddr := ExceptAddr; Application.Terminate; end; end;
The rest of the magic happens in the project file (DPR)
begin GlobalExcept := nil; GlobalExceptAddr := nil; try Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; finally if Assigned( GlobalExcept ) then begin raise GlobalExcept at GlobalExceptAddr; end; end; end.
And you can [download the code].
I didn’t cover this in my CodeRage II session on Exceptional Exceptions, but there is a lot of other cool stuff, like