在浏览System.Zip(Delphi XE2)以查看其工作原理时,我发现了这个功能:
procedure VerifyWrite(Stream: TStream; var Buffer; Count: Integer);
begin
if Stream.Write(Buffer, Count) <> Count then
raise EZipException.CreateRes(@SZipErrorWrite) at ReturnAddress;
end;
at ReturnAddress
部分让我感到困惑。
我不知道at
是一个有效的关键字(语法高亮显示器似乎也无法识别它)。
根据IDE,它被声明为System.ReturnAddress
,但我只能在procedure _HandleAnyException;
的(asm)代码中找到它被声明为标签。系统单元虽然充满了对它的引用。
所以我想知道的是:
ReturnAddress
? Raise Exception.Create ... at ReturnAddress
究竟做了什么? 如果你可以给出一个真实世界的例子,说明这将是一个有用的结构,或者你可以建议不要使用它。
答案 0 :(得分:20)
ReturnAddress
是完成后VerifyWrite
将返回的地址。
Raise Exception.Create... at ReturnAddress
表示当显示异常对话框时,它会将异常的地址指示为ReturnAddress
。换句话说,异常消息将显示为Exception <whatever> raised at <ReturnAddress>: <Exception Message>
。
以下是Delphi 7帮助文件的摘录。它与the online version几乎相同。
要引发异常对象,请使用异常的实例 带有加注声明的类。例如,
raise EMathError.Create;
一般来说,加注声明的形式是
raise object at address
其中object和at地址都是可选的;看到 重新提出例外。指定地址时 它可以是任何求值为指针的表达式 type,但通常是指向过程或函数的指针。 例如:
raise Exception.Create('Missing parameter') at @MyFunction;
使用此选项可以从较早的点引发异常 在堆栈中,而不是实际发生错误的那个。
特别注意最后一句话。关于at <address>
的使用非常具体。
答案 1 :(得分:8)
ReturnAddr
不是以前Delphi版本的谜题。考虑下一个测试(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;
如果您在调试器下按下Button3并在异常消息框中按'Break',调试器将停在
procedure TForm1.Button3Click(Sender: TObject);
begin
RaiseTest1; // <-- here
end;
如果按下Button4,调试器将停在
procedure RaiseTest2;
begin
raise Exception.Create('OOPS!'); // <-- here
end;
正如您所看到的,RaiseTest1修改了默认的异常堆栈帧并使调试更加简单,因为RaiseTest1(2)过程的唯一目的是引发异常。
我想XE2中的某些内容发生了变化,因此简化了ReturnAddr
语法。