Nested Exception

delphi package - automated exception handling
Rynardald
Posts: 6
Joined: Fri Jun 07, 2019 9:15 am

Nested Exception

Post by Rynardald »

Hi Madshi,
i am using madExcept and have a problem with nested Exception. When i nest exceptions with RaiseOuterException and madExcept is enabled then the inner exception gets lost (value is nil). When I disable madExcept all is fine.
I am using madExcept with version 2.8.3.0 (but I could also reproduce it with version 2.8.8.0) and Delphi 10 Seattle. There were no special settings for madExcept, only the default values. Below a small console-project with which I tested it. With madExcept the variable 'LInnerException' was nil and without the variable contains the first exception.
Do I have to set some options or is madExcept not able to handle nested exceptions?

Code: Select all

program TestProject;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

var
  LInnerException: Exception;

begin
  try
    try
      raise Exception.Create('First Exception...');
    except
      Exception.RaiseOuterException(Exception.Create('Second Exception...'));
    end;
  except
    on E: Exception do
    begin
      LInnerException := E.InnerException;
    end;
  end;
end.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Nested Exception

Post by madshi »

It's been a looong time since I looked into nested exceptions. I have quite a bit of extra code in madExcept just for nested exceptions. But I'm not sure I've ever seen anybody manually accessing the "InnerException" property. So far all users only wanted to get the inner exceptions reported automatically by madExcept. I guess maybe it's possible that whatever madExcept is doing to automatically show nested exceptions in its exception box might render the "Exception.InnterException" property not working, anymore? I'm not sure. If that's the case, then you're the first user actually noticing... :lol:

May I ask for which purpose you need to have access to the "InnerException" property?
Rynardald
Posts: 6
Joined: Fri Jun 07, 2019 9:15 am

Re: Nested Exception

Post by Rynardald »

I would use it for chains of exception. Currently when I have the case that one exception is raised because of another exception i take the first one and append some text to the error-message. But when I make it this way I loose all data from the second exception and my idea was to change this by using inner exception. Which is also the recommended way for such cases.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Nested Exception

Post by madshi »

Ok, but can't you simply raise a new exception then in your try..except block? Why do you need to access "E.InnerException"? madExcept should still report all the inner exceptions correctly in its bug reports. Or does that not work?
Rynardald
Posts: 6
Joined: Fri Jun 07, 2019 9:15 am

Re: Nested Exception

Post by Rynardald »

I cannot simply raise another exception with 'raise' because in this case the first exception will be destroyed. As far as i know the first exception will not be destroyed only when i reraise it with 'raise' or wrap it with another exception with 'RaiseOuterException'.

I made some debugging and for some reasons when a activate madexcept this function will not be executed anymore 'Exception.SetInnerException' (which is responsible for setting the current exception as inner exception) when i call 'RaiseOuterException'.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Nested Exception

Post by madshi »

Have you tried simply raising another exception? As I mentioned before it's been a very long time since I last looked into this, but from the top of my head, I think it might just work, because madExcept also tried to make inner exceptions work even for older Delphi versions which had no explicit support for that. But I might be wrong...
Rynardald
Posts: 6
Joined: Fri Jun 07, 2019 9:15 am

Re: Nested Exception

Post by Rynardald »

Yes I tried, but it does not work.

I changed my first program a little bit. And only with 'RaiseOuterException' a innerException is available at all, but also only when a disable madexcept. When madexcept is enabled the innerexception is also nil (even with 'RaiseOuterException').

Code: Select all

program TestProject;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

var
  LInnerException: Exception;
  LExeption      : Exception;

begin
  try
    // First try except
    try
      raise Exception.Create('First Exception...');
    except
      on LExeption: Exception do
      begin
        Exception.RaiseOuterException(Exception.Create('Second Exception...'));
        // raise Exception.Create('Second Exception...');
        // raise;
      end;
    end;
  except
    on LExeption: Exception do
    begin
      LInnerException := LExeption.InnerException;
      Writeln('BaseException: ' + LExeption.Message);
      if Assigned(LInnerException) then
      begin
        Writeln('InnerException: ' + LInnerException.Message);
      end
      else
      begin
        Writeln('No Inner Exception');
      end;
    end;
  end;

  Readln;
end.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Nested Exception

Post by madshi »

Just ran a quick test myself, using Delphi Seattle, using this code:

Code: Select all

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    raise Exception.Create('original exception');
  except
    raise Exception.Create('secondary exception');
  end;
end;
Here's a part of the resulting bug report:

Code: Select all

[...]
exception class    : Exception
exception message  : secondary exception.

main thread ($50a0):
006371b3 +053 Project1.exe   Unit1             31   +4 TForm1.Button1Click
7710cd81 +021 ntdll.dll                                KiUserExceptionDispatcher
75ef1890 +050 KERNELBASE.dll                           RaiseException
005883c7 +073 Project1.exe   Vcl.Controls    7365   +9 TControl.Click
0059f4c6 +01e Project1.exe   Vcl.StdCtrls    5327   +3 TCustomButton.Click
0059ffd4 +010 Project1.exe   Vcl.StdCtrls    5788   +1 TCustomButton.CNCommand
00587e59 +2bd Project1.exe   Vcl.Controls    7249  +91 TControl.WndProc
0058c999 +5e9 Project1.exe   Vcl.Controls   10079 +158 TWinControl.WndProc
0059f170 +06c Project1.exe   Vcl.StdCtrls    5164  +13 TButtonControl.WndProc
00587a94 +024 Project1.exe   Vcl.Controls    7027  +10 TControl.Perform
0058caff +023 Project1.exe   Vcl.Controls   10148  +12 DoControlMsg
0058d587 +00b Project1.exe   Vcl.Controls   10423   +1 TWinControl.WMCommand
00627a89 +045 Project1.exe   Vcl.Forms       6242   +6 TCustomForm.WMCommand
00587e59 +2bd Project1.exe   Vcl.Controls    7249  +91 TControl.WndProc
0058c999 +5e9 Project1.exe   Vcl.Controls   10079 +158 TWinControl.WndProc
006248b8 +62c Project1.exe   Vcl.Forms       4459 +206 TCustomForm.WndProc
0058bfb8 +02c Project1.exe   Vcl.Controls    9786   +3 TWinControl.MainWndProc
0052f780 +014 Project1.exe   System.Classes 16886   +8 StdWndProc
7615bc52 +132 user32.dll                               SendMessageW
76157af8 +088 user32.dll                               CallWindowProcW
0058caaa +0e6 Project1.exe   Vcl.Controls   10120  +30 TWinControl.DefaultHandler
0058881c +010 Project1.exe   Vcl.Controls    7498   +1 TControl.WMLButtonUp
00587e59 +2bd Project1.exe   Vcl.Controls    7249  +91 TControl.WndProc
0058c999 +5e9 Project1.exe   Vcl.Controls   10079 +158 TWinControl.WndProc
0059f170 +06c Project1.exe   Vcl.StdCtrls    5164  +13 TButtonControl.WndProc
0058bfb8 +02c Project1.exe   Vcl.Controls    9786   +3 TWinControl.MainWndProc
0052f780 +014 Project1.exe   System.Classes 16886   +8 StdWndProc
76177a7b +00b user32.dll                               DispatchMessageW
0062df13 +0f3 Project1.exe   Vcl.Forms      10443  +23 TApplication.ProcessMessage
0062df56 +00a Project1.exe   Vcl.Forms      10473   +1 TApplication.HandleMessage
0062e289 +0c9 Project1.exe   Vcl.Forms      10611  +26 TApplication.Run
00640835 +049 Project1.exe   Project1          18   +4 initialization
73aa8492 +022 KERNEL32.DLL                             BaseThreadInitThunk

main thread ($50a0), inner exception level 1:
>> Exception, original exception
0063718e +02e Project1.exe Unit1             29   +2 TForm1.Button1Click
005883c7 +073 Project1.exe Vcl.Controls    7365   +9 TControl.Click
0059f4c6 +01e Project1.exe Vcl.StdCtrls    5327   +3 TCustomButton.Click
0059ffd4 +010 Project1.exe Vcl.StdCtrls    5788   +1 TCustomButton.CNCommand
00587e59 +2bd Project1.exe Vcl.Controls    7249  +91 TControl.WndProc
0058c999 +5e9 Project1.exe Vcl.Controls   10079 +158 TWinControl.WndProc
0059f170 +06c Project1.exe Vcl.StdCtrls    5164  +13 TButtonControl.WndProc
00587a94 +024 Project1.exe Vcl.Controls    7027  +10 TControl.Perform
0058caff +023 Project1.exe Vcl.Controls   10148  +12 DoControlMsg
0058d587 +00b Project1.exe Vcl.Controls   10423   +1 TWinControl.WMCommand
00627a89 +045 Project1.exe Vcl.Forms       6242   +6 TCustomForm.WMCommand
00587e59 +2bd Project1.exe Vcl.Controls    7249  +91 TControl.WndProc
0058c999 +5e9 Project1.exe Vcl.Controls   10079 +158 TWinControl.WndProc
006248b8 +62c Project1.exe Vcl.Forms       4459 +206 TCustomForm.WndProc
0058bfb8 +02c Project1.exe Vcl.Controls    9786   +3 TWinControl.MainWndProc
0052f780 +014 Project1.exe System.Classes 16886   +8 StdWndProc
7615bc52 +132 user32.dll                             SendMessageW
76157af8 +088 user32.dll                             CallWindowProcW
0058caaa +0e6 Project1.exe Vcl.Controls   10120  +30 TWinControl.DefaultHandler
0058881c +010 Project1.exe Vcl.Controls    7498   +1 TControl.WMLButtonUp
00587e59 +2bd Project1.exe Vcl.Controls    7249  +91 TControl.WndProc
0058c999 +5e9 Project1.exe Vcl.Controls   10079 +158 TWinControl.WndProc
0059f170 +06c Project1.exe Vcl.StdCtrls    5164  +13 TButtonControl.WndProc
0058bfb8 +02c Project1.exe Vcl.Controls    9786   +3 TWinControl.MainWndProc
0052f780 +014 Project1.exe System.Classes 16886   +8 StdWndProc
76177a7b +00b user32.dll                             DispatchMessageW
0062df13 +0f3 Project1.exe Vcl.Forms      10443  +23 TApplication.ProcessMessage
0062df56 +00a Project1.exe Vcl.Forms      10473   +1 TApplication.HandleMessage
0062e289 +0c9 Project1.exe Vcl.Forms      10611  +26 TApplication.Run
00640835 +049 Project1.exe Project1          18   +4 initialization
73aa8492 +022 KERNEL32.DLL                           BaseThreadInitThunk
As you can see, inner exception catching works perfectly. I've tested both 32bit and 64bit, works in both.

And what is more, this works even in old Delphi versions which didn't have built in support for nested exceptions!
bkdroid13
Posts: 4
Joined: Fri Jun 07, 2019 8:43 am

Re: Nested Exception

Post by bkdroid13 »

Sometimes it is desirable to catch an exception and throw another exception. If the new exception keeps a reference to the first exception, the first exception is called a nesting exception. The above code is an example of a nesting exception. ... The stack-trace starts when the exception is thrown.

public class NestingExceptionExample {

public static void main(String[] args) throws Exception {
Object[] localArgs = (Object[]) args;

try {
Integer[] numbers = (Integer[]) localArgs;
} catch (ClassCastException originalException) {
Exception generalException = new Exception(
"Horrible exception!",
originalException);
throw generalException;
}
}
}
Rynardald
Posts: 6
Joined: Fri Jun 07, 2019 9:15 am

Re: Nested Exception

Post by Rynardald »

Hi madshi,
thanks for the clarification. I didn't expected to see the both exceptions only in the madexcept callstack. I not really happy that with madexcept i have not more a reference to the first exception object from my sourcecode. Or is there a way how i can get the first exception object?
But anyway now I know where I have to look for the nested exceptions. Thanks.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Nested Exception

Post by madshi »

I'm still trying to understand why you need to access the "InnerException" property in the first place, considering that madExcept already reports all this perfectly, without you having to do anything at all in your code?
Rynardald
Posts: 6
Joined: Fri Jun 07, 2019 9:15 am

Re: Nested Exception

Post by Rynardald »

Now that i know where i can find the inner exception in the bug-report it is only a theoretical problem which happens when I want to evaluate the innerexception-property inside of a try except. Currently i don't do this (therefor it's only theoretical). What i don't like is the fact that I loose the access to this object (inner exception at run-time) by using madexcept. But i don't need the access right now and I have currently no real world example where I would need it in near future.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Nested Exception

Post by madshi »

Ok, thanks. I understand that it's kinda annoying that access to the property is no longer working when using madExcept, even if you don't have a practical need for it right now. But that said, as I mentioned earlier, no other madExcept user even brought this topc up before - ever. So I assume that in real life there's just no need for this property, because madExcept automatically reports everything correctly in its bug reports already, anyway.

I don't recall the exact reason why the property stops working. But I think it has to do with trying to make everything work without you having to do anything manually (like e.g. calling RaiseOuterException instead of RaiseException etc), and also trying to make everything work with older Delphi versions etc. I'm not sure if it would be easy or hard for me to make the property work. It's been too long since I've dived into the depth of the inner exception logic. So I'd have to reinvestigate. But considering that there's no real practical need for that right now, anyway, I'd rather ignore this, until a critical need for access to the property actually comes up.
Vitalik
Posts: 12
Joined: Tue Oct 27, 2009 8:38 pm

Re: Nested Exception

Post by Vitalik »

Hi! I would like to join this old descussion to add a case that explaines why I do need innerExceptions

So I have an exception class ExceptionFields that contains a set of field-value pairs that I add to bug report. This happens in the code that is called indirectly from exception handler that I register with RegisterExceptionHandler:

Code: Select all

procedure AddFieldsToException(const exceptIntf: IMEException);
var
  e: ExceptionFields;
  Field: string;
  Value: string;
begin
  if exceptIntf.ExceptObject is ExceptionFields then
  begin
    e := exceptIntf.ExceptObject as ExceptionFields;
    try
      for Field in e.Fields do
        if e.GetValue(Field, Value) then
          exceptIntf.BugReportHeader.Add(Field, Value);
    except
    end;
  end;
end;

So that I can write descendants for this class and add some specific extra information for exceptions that I can get later from bug reports, that will help me to fix the problems. The good example is ESQLException = class(ExceptionFields) which provides me SQL that caused the exception.

The problem is that without Exception.InnerException I only can handle the most outer ExceptionFields and get data from its Fields, not any inner.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Nested Exception

Post by madshi »

I think you might be misunderstanding the concept of "InnerExceptions"? They do not give you access to inherited classes of the same exception object! If you have ESQLException which is inherited from ExceptionFields, then ESQLException will provide you with all the properties of ExceptionFields, as well. So you can directly e.g. use "someESQLException.Fields". This works just fine. InnerExceptions is something completely different.
Post Reply