While browsing System.Zip (Delphi XE2) to see how it works, I found this function:
procedure VerifyWrite(Stream: TStream; var Buffer; Count: Integer);
begin
if Stream.Write(Buffer, Count) <> Count then
raise EZipException.CreateRes(@SZipErrorWrite) at ReturnAddress;
end;
It's the at ReturnAddress
part that sort of puzzles me.
I didn't know that at
was a valid keyword (the syntax highlighter doesn't seem to recognise it either).
According to the IDE it's declared as System.ReturnAddress
, but I can only find it declared as a label somewhere in the (asm) code of procedure _HandleAnyException;
. The system unit is full of references to it though.
So what I would like to know is this:
ReturnAddress
?Raise Exception.Create ... at ReturnAddress
do?Bonuspoints if you can give a real-world example of where this would be a useful construct, or if you can advice against using it.
ReturnAddress
is the address to which VerifyWrite
would have returned when finished.
Raise Exception.Create... at ReturnAddress
means that when the exception dialog is displayed, it would indicate the address of the exception as being at ReturnAddress
. In other words, the exception message would read Exception <whatever> raised at <ReturnAddress>: <Exception Message>
.
Here is an excerpt from the help file for Delphi 7. It's nearly the same as the online version.
To raise an exception object, use an instance of the exception class with a raise statement. For example,
raise EMathError.Create;
In general, the form of a raise statement is
raise object at address
where object and at address are both optional; see Re-raising exceptions. When an address is specified, it can be any expression that evaluates to a pointer type, but is usually a pointer to a procedure or function. For example:
raise Exception.Create('Missing parameter') at @MyFunction;
Use this option to raise the exception from an earlier point in the stack than the one where the error actually occurred.
Note the last sentence in particular. It's pretty specific about the use of at <address>
.
ReturnAddr
was not a puzzle with previous Delphi versions. Consider next test (Delphi XE):
procedure RaiseTest1;
procedure RaiseException(ReturnAddr: Pointer);
begin
raise Exception.Create('OOPS!') at ReturnAddr;
end;
asm
POP EAX
JMP RaiseException
end;
procedure RaiseTest2;
begin
raise Exception.Create('OOPS!');
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
RaiseTest1;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
RaiseTest2;
end;
if you press Button3 under debugger and press 'Break' in exception messagebox, debugger stops at
procedure TForm1.Button3Click(Sender: TObject);
begin
RaiseTest1; // <-- here
end;
if you press Button4, debugger stops at
procedure RaiseTest2;
begin
raise Exception.Create('OOPS!'); // <-- here
end;
As you can see RaiseTest1 modifies default exception stack frame and makes debugging a bit more straightforward since the only purpose of RaiseTest1(2) procedures is to raise an exception.
I guess something changed in XE2 so that ReturnAddr
syntax is simplified.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With