在ReturnData中``在Delphi中是什么意思?

Wou*_*ick 25 delphi exception

在浏览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;
Run Code Online (Sandbox Code Playgroud)

at ReturnAddress这是让我困惑的部分.

我不知道这at是一个有效的关键字(语法高亮显示器似乎也没有识别它).

根据IDE声明它System.ReturnAddress,但我只能在(asm)代码中的某处找到它声明为标签procedure _HandleAnyException;.但系统单元充满了对它的引用.

所以我想知道的是:

  1. 什么是ReturnAddress
  2. 到底是Raise Exception.Create ... at ReturnAddress做什么的?

如果你可以给出一个真实世界的例子,说明这将是一个有用的结构,或者你可以建议不要使用它.

Ken*_*ite 20

ReturnAddressVerifyWrite完成后将返回的地址.

Raise Exception.Create... at ReturnAddress表示当显示异常对话框时,它将指示异常的地址为ReturnAddress.换句话说,将读取异常消息Exception <whatever> raised at <ReturnAddress>: <Exception Message>.

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

要引发异常对象,请使用带有raise语句的异常类的实例.例如,

raise EMathError.Create;
Run Code Online (Sandbox Code Playgroud)

一般来说,加薪声明的形式是

raise object at address
Run Code Online (Sandbox Code Playgroud)

其中object和at地址都是可选的; 请参阅重新提出例外情况.指定地址时,它可以是任何求值为指针类型的表达式,但通常是指向过程或函数的指针.例如:

raise Exception.Create('Missing parameter') at @MyFunction;
Run Code Online (Sandbox Code Playgroud)

使用此选项可以从堆栈中的早期点引发异常,而不是实际发生错误的异常.

请特别注意最后一句.它的使用非常具体at <address>.

  • 如果你使用*辅助函数*来引发异常,那么这种构造的实际用途通常是这样的.例如,在VCL中,存在"TList.Error",其中所有与"TList"相关的错误都来自.知道在该函数中引发异常对于调试没有用,所以它使用`at`语法将异常地址放回到调用`Error`的函数中,所以当你在map文件中查找地址时,你有一个更好的主意,谁是罪魁祸首.(为什么要使用帮助器呢?对于一个,它可以使调用者的codegen更简单.) (4认同)
  • 如果您*拥有*调用堆栈,@Afrazier,那么当然。但“at”语法自 1995 年以来就已存在。当时,MadExcept 还没有为您提供来自客户的良好崩溃报告。 (2认同)

klu*_*udg 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;
Run Code Online (Sandbox Code Playgroud)

如果您在调试器下按下Button3并在异常消息框中按'Break',调试器将停止

procedure TForm1.Button3Click(Sender: TObject);
begin
  RaiseTest1; // <-- here
end;
Run Code Online (Sandbox Code Playgroud)

如果您按下Button4,调试器将停止

procedure RaiseTest2;
begin
  raise Exception.Create('OOPS!');  // <-- here
end;
Run Code Online (Sandbox Code Playgroud)

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

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