1. Technology
Send to a Friend via Email

On Reraising Exceptions in Delphi Exception Handling

Provide More Descriptive Exception Messages To Application Users

By

Modified EDivByZero

Modified EDivByZero

Modified EDivByZero
In the On Handling Exceptions in Delphi Exception Handling article, a discussion on how to handle and what happens when you handle exceptions in your Delphi code is provided.

This time, we'll see how you can explore exceptions being raised and handled and how to properly re-raise exceptions.

Nicer Exception Messages

Here's a piece of code that would throw the EDivByZero error. The EDivByZero exception instance (where EDivByZero exception class in declared in SysUtils.pas) is raised by Delphi when you try to divide a number by zero.
function DivideByZero(const inValue : integer) : integer;
var
  zero : integer;
begin
  zero := 0;
  result := inValue div zero;
end;
If you would have a function like above somewhere in your application, called as "DivideByZero(2012);", the error dialog would be displayed to the users stating "Division by zero".

Your application will not crash, due to the global exception handling mechanism provided to you through the Application object.

Having the global application object always looking upon all unhandled exceptions, you can think of this "parent" protection as:

try 
  //your ENTIRE APPLICATION
except
  on E : Exception do Application.ShowException(E);
end;

The user (and finally you) from the message itself can not know where in your code the exceptional condition happened and thus you could/would (as the application writer) have hard time locating the origin of the exception.

Ok, let's see how to tell to the user where the exception has been raised, to help the user help us locate and fix the problem. We do this by guarding the block we know will/might raise the EDivByZeroException and provide a more descriptive message:

function DivideByZero(const inValue : integer) : integer;
var
  zero : integer;
begin
  try
    zero := 0;
    result := inValue div zero;
  except
    on E : EDivByZero do
    begin
      E.Message := E.Message + ' in unit myUnit.pas function DivideByZero';
      Application.ShowException(E);
    end;
  end;
end;
Note that without the "Application.ShowException(E);" the exception will not get displayed to the user!?

As stated in the On Handling Exceptions in Delphi Exception Handling article, handing the exception automatically destroys the exception object - and the exception, since handled, will not be processed by the global Application's HandleException method - thus no error dialog will be displayed to the user.

ReRaising Exceptions

In the above example, the exception handling block has caught the exception, it was handled (by providing a more descriptive message).

When you caught the exception but still want any enclosing parent exception handler to react (even if the only parent in the Application itself) you can reraise the exception.

To reraise or rethrow the exception we call the "raise" method without specifying the exception instance, as in:

function DivideByZero(const inValue : integer) : integer;
var
  zero : integer;
begin
  try
    zero := 0;
    result := inValue div zero;
  except
    on E : EDivByZero do
    begin
      RAISE;
    end;
  end;
end;
If the DivideByZero function is declared in a "library" and you would have an enclosing, "outer", try/except block in your application code, you allow application code to again react upon the exception.

By reraising exceptions, you can easily provide special handling for exceptions in special cases without losing (or duplicating) the existing handlers.

Don't do "RAISE E;"

Note that the exception is being reraised by only calling "raise;" and not "raise E;". This was a question in "Reraising Exceptions in Delphi - How to Properly - A Poll"

Since the exception handling block will free the exception instance, using "raise E;" would result in an Access Violation - as you would be using the exception instance memory that is already fred.

"Reraising" Modified Exception

By modifying the EDivByZero exception message and calling Application.ShowException in the above example, we have handled the exception and it will not get passed to any outer guarding block.

Here's how to throw a new exception instance but with a more descriptive message to the user:

function DivideByZero(const inValue : integer) : integer;
var
  zero : integer;
begin
  try
    zero := 0;
    result := inValue div zero;
  except
    on E : EDivByZero do
    begin
      E.Message := E.Message + ' in unit myUnit.pas function DivideByZero';
      RAISE EDivByZero.Create(E.Message);
    end;
  end;
end;
That it. Now you should know how exceptions are being handled even if you do not handle them :)

Zarko Gajic
About.com Delphi

©2014 About.com. All rights reserved.