Anyway, in both examples we leak important information: an information about previous
exception, which can give us hints to the problem. And it is when nested exceptions appears.
The new
exception class (whoa, that sure took a lot of time, but I should introduce newbies to nested exceptions after all) have InnerException and BaseException properties. Both properties are set (managed) by SysUtils
unit automatically, you don't need to fill them manually. So, you can read and use them. InnerException property stores the previous
exception. BaseException stores
exception with most nested level - i.e. the very first
exception, which started this
exception chain. If there are only two exceptions in the chain (old and new, as in examples above), then InnerException will be equal to BaseException. If there is only one
exception (i.e. there is no nested
exception at all) - then both properties will be equal to nil.
Nested exceptions, however, aren't saved by default. In order to save nested
exception, you need to raise new exceptions via
Exception.RaiseOuterException (Delphi's style) or
Exception.ThrowOuterException (C++ Builder's style). For example:
Delphi-Quellcode:
procedure TSomeClass.SaveToStream(
const AStream: Stream);
begin
try
// ... saving instance to stream here
except
Exception.RaiseOuterException(ESomeClassSaveError.Create('
Error saving data to stream'));
end;
end;
After executing this code, we will get
exception of ESomeClassSaveError class, which will have non-nil InnerException property (and BaseException too), which, in turn, will contain original
exception (EStreamError or whatever it was).
In the second example (the one with
exception in destructor) we still get InnerException = nil, cause RaiseOuterException isn't used anywhere.
How this support of nested exceptions affects error messages? Well, Message property didn't changed - it's a message of current
exception only. So, any code, which isn't awared about nested exceptions, will show only message of the last
exception. You can use ToString method of
Exception class to show all messages from all exceptions - each
exception will be on new line (obviously, ToString will be equal to Message property in case of single
exception in the chain).
On the other side, Application.ShowException method looks a bit strange: this method shows message from BaseException - I suppose that it's not what you want (see our first example). That's why I think that you may want to make your own Application.OnException event handler to change this behaviour. For example:
Delphi-Quellcode:
procedure TForm1.ApplicationEvents1Exception(Sender: TObject; E:
Exception);
var
Msg:
String;
begin
Msg := E.
Message;
// or E.ToString
Application.MessageBox(PChar(Msg), PChar(Application.Title), MB_OK + MB_ICONSTOP);
end;