This article provides some information on setting up error handling
with Delphi.
One of the big advantages of using exceptions is that you do not have to check
the result of every function call that you make. Typical C programming
involves making a function call (such as allocmem) and checking the result
to see if the function succeeded or not. More than likely, the function will
always work. Having an exception being raised when an error does happen, makes
less work for the programmer.
The best way to catch exceptions is by defensive programming. Any code
which could possibly raise an exception can be wrapped in one of two blocks:
a try ... except block or a try ... finally block.
Here is an example of using try ... except:
procedure TForm1.FormCreate(Sender: TObject);
begin
try
// Some code which may raise an exception
except
on E: EConvertError do
begin
// Handle only EConvertError, or any derived classes
end;
on E: Exception do
begin
// Handle only Exception, or any derived classes
end;
end;
end;
Once an exception is raised, the exception handler jumps immediately to the
nearest except (or finally) block. Notice how you can pick exactly
what exceptions you want to handle. Also, notice that EConvertError derives
from Exception. You must list more specific (derived) exceptions before
generic exceptions. Also, if you do not catch the exception (such as, if
we had not put E: Exception in the handler), then the exception would
continue to propagate outside of the try ... except block, and be caught else where.
The big difference between a try ... except block and a try ... finally block is that the try ... except block is only executed if an exception was raised, while the try ... finally is always executed. Because finally blocks are always executed, it doesn't make sense to catch specific exceptions with the on keyword, so this is not possible in them. Try ... finally's are typically used 3 to 1 times over try ... except's, as handling for an exceptional case it is much rarer than simply doing resource mangement. Normal resource management with a try ... finally block typically looks like this:
procedure TForm1.FormCreate(Sender: TObject);
var
Form: TForm1;
begin
Form := TForm1.Create(Self); // Allocate some memory
try
// Some code which may raise an exception
Form.DoSomethingWhichMayRaiseAnException;
finally
Form.Free; // Always free the memory.
end;
end;
The Form will always be freed. One may ask why the line Form := TForm1.Create(Self);
is not inside the try ... finally block. The reason is simple: what if
Create raised an exception? Then you would be freeing the Form variable when it
was never allocated! Now, the compiler is smart enough to realize this, and would
give you a warning if you put the Form inside the block:Now, raising an exception is simple:
raise Exception.Create('some exception!');
Notice that it is possible to raise an exception inside of a try...except/finally
block. If you want to re-raise the current exception, you can simply use raise:
procedure TForm1.FormCreate(Sender: TObject);
var
Form: TForm1;
begin
Form := TForm1.Create(Self); // Allocate some memory
try
// Some code which may raise an exception
Form.DoSomethingWhichMayRaiseAnException;
except
on E: Exception do
raise; // re-raise the currently caught exception
end;
end;
Note that it is wrong to try and re-raise it with:
raise E; // DON't DO THIS!
After you catch an exception, it is implicitly freed by the compiler (since, it
has to be freed at some time!). Re-raising it with just raise doesn't
free the current exception object. Calling raise E, where E is the
caught exception, means that the compiler will implicitly free E twice!
This should give you a pretty good overview as to how to handle exceptions with Delphi.
Last Modified: 01-MAY-01