Crashing Like VB

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

  raise e;

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].

Thanks to Jeremi Reda for asking this question in borland.public.delphi.non-technical.

I didn’t cover this in my CodeRage II session on Exceptional Exceptions, but there is a lot of other cool stuff, like AcquireExceptionObject!

Tags: , , , , , ,

2 Responses to “Crashing Like VB”

  1. Allen Bauer says:

    Jim,

    I have a funny feeling that I’m going to get yelled at here :-). There is a one-line built-in solution to the above problem. Many releases ago, I had taken into account that folks may want to invoke the built-in Windows error reporting or to allow a Just-in-Time debugger to be invoked when an exception is raised.

    Down inside the system unit, there is a global byte variable called JITEnable. Simply set it to 1 to tell the exception system to pass all non-Delphi exceptions on through to the UnhandledExceptionFilter API, which is what will invoke either the registered JIT debugger or Windows error reporting. You can also set this value to >1 which will pass *all* exceptions raised to that API.

    This is a little different than the solution you propose above where it is only exceptions that percolate to the base handler that are passed off to the OS. If they’re caught and handled elsewhere, the OS will never know about them. With the JITEnable technique, the exception is passed along at any point exceptions are handled.

    Allen.

  2. Jim McKeeth says:

    Great thanks Allen! No yelling from me, that is for sure. JITEnable is a cool feature if I ever feel the need to crash like VB.