我试图弄清楚如何在 Delphi 中抛出异常后获取堆栈跟踪。但是,当我尝试使用下面的函数读取 Application.OnException 事件中的堆栈时,堆栈似乎已经被刷新并被抛出过程替换。
function GetStackReport: AnsiString;
var
retaddr, walker: ^pointer;
begin
// ...
// History of stack, ignore esp frame
asm
mov walker, ebp
end;
// assume return address is present above ebp
while Cardinal(walker^) <> 0 do begin
retaddr := walker;
Inc(retaddr);
result := result + AddressInfo(Cardinal(retaddr^));
walker := walker^;
end;
end;
这是我得到的结果:
001A63E3: TApplication.HandleException (Forms)
00129072: StdWndProc (Classes)
001A60B0: TApplication.ProcessMessage (Forms)
这显然不是我想要的,尽管它是正确的。我想检索抛出异常之前的堆栈,或者换句话说,在 OnException 调用之前(之后也可以)的内容。
有什么办法可以做到这一点吗?
我知道我正在重新发明轮子,因为 madExcept/Eurekalog/jclDebug 的人们已经做到了这一点,但我想知道它是如何完成的。
最佳答案
无法从 OnException
事件内部手动获取可行的堆栈跟踪。正如您已经注意到的,当事件被触发时,错误发生时的堆栈已经消失了。您要查找的内容需要获取引发异常时的堆栈跟踪。第三方异常记录器(例如 MadExcept、EurekaLog 等)通过 Hook RTL 本身内部的关键函数和核心异常处理程序来为您处理这些详细信息。
在最近的 Delphi 版本中,SysUtils.Exception
类现在确实具有公共(public) StackTrace
和 StackInfo
属性,这在OnException
事件,但 Embarcadero 由于未知原因选择不在 native 实现这些属性。它需要第三方异常记录器将处理程序分配给 Exception 类公开的各种回调,以生成属性的堆栈跟踪数据。但是,例如,如果您安装了 JclDebug,那么您可以在自己的代码中提供自己的回调处理程序,使用 JCL 的堆栈跟踪函数来生成属性的堆栈数据。
关于Delphi在异常后获取堆栈跟踪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15890029/