Why are changes to an Exception object lost when re-raising it?
I was sure this used to work for me, and I've seen it out on the net (Jolyon Smith and David Moorhouse). Having just tried it in a simple program both in D2007 and 开发者_开发知识库in XE2 trial, it doesn't keep the modified Message. As soon as the "raise" happens, the message reverts back to the original exception.
What blindlingly obvious thing am I missing ? The alternative is to "raise Exception.Create(...)" but I want to just propogate the original exception back up the chain, only with additional information tagged along at each exception block.
var a: Integer;
begin
try
a := 0;
Label1.Caption := IntToStr(100 div a);
except
on e: Exception do
begin
e.Message := 'Extra Info Plus the original : ' + e.Message;
raise;
end;
end;
end;
Well blow me! This looked so wrong that I had to try it myself, and you're absolutely right! I've narrowed it down to the fact that this is an OS exception (divide by zero) that is generated by the OS itself and not by Delphi. If you try and raise an EIntError yourself you get the expected behaviour, and not what you see above. Note that expected behaviour occurs whenever you raise an exception yourself.
Update: In the System.pas unit there is the following code called when the exception is re-raised:
{ Destroy any objects created for non-delphi exceptions }
MOV EAX,[EDX].TRaiseFrame.ExceptionRecord
AND [EAX].TExceptionRecord.ExceptionFlags,NOT cUnwinding
CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiException
JE @@delphiException
MOV EAX,[EDX].TRaiseFrame.ExceptObject
CALL TObject.Free
CALL NotifyReRaise
So if the exception is not a Delphi exception (in this case an OS exception) then the (modified) "Delphi" exception is freed and the original exception is re-raised, thereby throwing away any changes made to the exception. Case closed!
Update 2: (couldn't help myself). You can reproduce this with the following code:
type
TThreadNameInfo = record
InfoType: LongWord; // must be $00001000
NamePtr: PAnsiChar; // pointer to message (in user address space)
ThreadId: LongWord; // thread id ($ffffffff indicates caller thread)
Flags: LongWord; // reserved for future use, must be zero
end;
var
lThreadNameInfo: TThreadNameInfo;
with lThreadNameInfo do begin
InfoType := $00001000;
NamePtr := PAnsiChar(AnsiString('Division by zero'));
ThreadId := $ffffffff;
Flags := $00000000;
end;
RaiseException($C0000094, 0, sizeof(lThreadNameInfo) div sizeof(LongWord), @lThreadNameInfo);
Have fun!
See Misha's explanation. As a workaround, you can do this:
except
on E: Exception do
begin
E := Exception(ExceptObject);
E.Message := '(Extra info) ' + E.Message;
AcquireExceptionObject;
raise E;
end;
end;
精彩评论