Page 1 of 2
Nested Exception
Posted: Fri Jun 07, 2019 10:56 am
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.
Re: Nested Exception
Posted: Fri Jun 07, 2019 3:04 pm
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...
May I ask for which purpose you need to have access to the "InnerException" property?
Re: Nested Exception
Posted: Tue Jun 11, 2019 9:17 am
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.
Re: Nested Exception
Posted: Wed Jun 12, 2019 9:44 am
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?
Re: Nested Exception
Posted: Wed Jun 12, 2019 12:37 pm
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'.
Re: Nested Exception
Posted: Wed Jun 12, 2019 2:18 pm
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...
Re: Nested Exception
Posted: Thu Jun 13, 2019 6:56 am
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.
Re: Nested Exception
Posted: Fri Jun 14, 2019 10:05 am
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!
Re: Nested Exception
Posted: Sat Jun 15, 2019 9:36 am
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;
}
}
}
Re: Nested Exception
Posted: Wed Jun 19, 2019 6:55 am
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.
Re: Nested Exception
Posted: Wed Jun 19, 2019 7:08 am
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?
Re: Nested Exception
Posted: Thu Jun 20, 2019 2:42 pm
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.
Re: Nested Exception
Posted: Fri Jun 21, 2019 1:01 pm
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.
Re: Nested Exception
Posted: Sat Sep 12, 2020 4:49 am
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.
Re: Nested Exception
Posted: Sat Sep 12, 2020 7:57 am
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.