Delphi 中的“at ReturnAddress”是什么意思?

What does `at ReturnAddress` mean in Delphi?

提问人:Wouter van Nifterick 提问时间:1/21/2012 最后编辑:JohanWouter van Nifterick 更新时间:5/17/2015 访问量:2714

问:

在浏览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,它被声明为 ,但我只能在 (asm) 代码的某处找到它被声明为标签。不过,系统单元充满了对它的引用。System.ReturnAddressprocedure _HandleAnyException;

所以我想知道的是:

  1. 什么是 ReturnAddress
  2. Raise Exception.Create 到底是什么?在 ReturnAddress 做吗?

如果你能举出一个真实世界的例子来说明这将是一个有用的结构,或者如果你能建议不要使用它,那就加分了。

德尔菲 异常

评论


答:

24赞 Ken White 1/21/2012 #1

ReturnAddress是完成后将返回的地址。VerifyWrite

Raise Exception.Create... at ReturnAddress表示当显示异常对话框时,它将指示异常的地址位于 。换言之,异常消息将显示为 .ReturnAddressException <whatever> raised at <ReturnAddress>: <Exception Message>

以下是 Delphi 7 帮助文件的摘录。它与在线版本几乎相同。

若要引发异常对象,请使用异常的实例 类。例如

raise EMathError.Create;

一般来说,raise 语句的形式是

raise object at address

其中 object 和 at 地址都是可选的;看 重新引发异常。指定地址时, 它可以是计算结果为指针的任何表达式 类型,但通常是指向过程或函数的指针。 例如:

raise Exception.Create('Missing parameter') at @MyFunction;

使用此选项可引发较早点的异常 在堆栈中,而不是实际发生错误的堆栈中。

特别注意最后一句话。它非常具体地使用了 。at <address>

评论

0赞 Ken White 1/21/2012
@ain:感谢您的格式帮助。并不是要删除您编辑的事实 - 只是想强调引用文本的最后一句话。:)
6赞 Rob Kennedy 1/22/2012
此构造的实际用途通常是使用帮助程序函数来引发异常。例如,在 VCL 中,所有与 相关的错误都来自此。知道在该函数中引发了异常对于调试没有用处,因此它使用语法将异常地址放回调用的函数中,因此当您在映射文件中查找地址时,您可以更好地了解罪魁祸首是谁。(为什么要使用帮手?首先,它使调用方的编码更简单。TList.ErrorTListatError
1赞 afrazier 1/23/2012
@RobKennedy:调用堆栈不会显示相同的信息吗?
2赞 Rob Kennedy 1/23/2012
如果您调用堆栈,@Afrazier,那么确定。但该语法自 1995 年以来一直存在。那时,MadExcept 并没有从客户那里给你一个很好的崩溃报告。at
10赞 kludg 1/21/2012 #2

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 并在异常消息框中按“中断”,则调试器将在

procedure TForm1.Button3Click(Sender: TObject);
begin
  RaiseTest1; // <-- here
end;

如果按 Button4,调试器将停止在

procedure RaiseTest2;
begin
  raise Exception.Create('OOPS!');  // <-- here
end;

如您所见,RaiseTest1 修改了默认的异常堆栈帧,并使调试更加简单,因为 RaiseTest1(2) 过程的唯一目的是引发异常。

我想 XE2 中发生了一些变化,因此语法得到了简化。ReturnAddr

评论

0赞 Rob Kennedy 9/30/2013
我不明白你认为改变了什么语法。
1赞 ventiseis 2/14/2016
@RobKennedy我认为他的意思是从到,但我找不到任何文档。但这似乎是系统单元中嵌套函数的名称(请参阅此错误)。ReturnAddrReturnAddressReturnAddr