作者:蒋易之 | 来源:互联网 | 2023-08-06 09:39
起源:用习惯了c#之Exception的StackTrace,在程序出异常crash时候能够以其定位出问题的模块及行号,用回Delphi2009,发现没有这东西。显然,在编译环境日新月异的今
起源:
用习惯了c#之Exception的StackTrace,在程序出异常crash时候能够以其定位出问题的模块及行号,用回Delphi 2009,发现没有这东西。
显然,在编译环境日新月异的今天,是不科学的。分析Delphi的Exception,发现些线索:StackTrace。
应该有戏!
继续下去,验证输出这个StackTrace,它是空的,里面没有预想要的内容。再深究去,发现并没想象那么容易,它扯到不少蛋。
1、Exception类
Delphi 2009中,StackTrace是如此定义的,与它一起的还有几个var:
Exception = class(TObject)
private
...
protected
...
public
...
property StackTrace: string read GetStackTrace;
property StackInfo: Pointer read FStackInfo;
{$IFDEF MSWINDOWS}
class var
// Hook this function to return an opaque data structure that contains stack information
// for the given exception information record. This function will be called when the
// exception is about to be raised or if this is an external exception such as an
// Access Violation, called soon after the object is created.
GetExceptionStackInfoProc: function (P: PExceptionRecord): Pointer;
// This function is called to return a string representation of the above opaque
// data structure
GetStackInfoStringProc: function (Info: Pointer): string;
// This function is called when the destructor is called to clean up any data associated
// with the given opaque data structure.
CleanUpStackInfoProc: procedure (Info: Pointer);
// Use this function to raise an exception instance from within an exception handler and
// you want to "acquire" the active exception and chain it to the new exception and preserve
// the context. This will cause the FInnerException field to get set with the exception
// in currently in play.
// You should only call this procedure from within an except block where the this new
// exception is expected to be handled elsewhere.
class procedure RaiseOuterException(E: Exception); static;
// Provide another method that does the same thing as RaiseOuterException, but uses the
// C++ vernacular of "throw"
class procedure ThrowOuterException(E: Exception); static;
{$ENDIF}
end;
明明白白的说,我东西给你了,你要用,自己想办法。你编译环境都不能实现,让我想什么办法?去它主页上看看:
蛋疼之极,欲用StackTrace,它居然要求我们用第三方的解决方案。如此不负责任!
好吧,上JEDI。
2、JclDebug
StackOverflow是个技术的绝佳去处,曾在其上受益不少,再去找找,果然有所得。
有个家伙封装了实现方法,借鉴下来,代码如下:
unit StackTrace;
interface
uses
SysUtils, Classes, JclDebug;
implementation
function GetExceptionStackInfoProc(P: PExceptionRecord): Pointer;
var
LLines: TStringList;
LText: String;
LResult: PChar;
begin
LLines := TStringList.Create;
try
JclLastExceptStackListToStrings(LLines, True, True, True, True);
LText := LLines.Text;
LResult := StrAlloc(Length(LText));
StrCopy(LResult, PChar(LText));
Result := LResult;
finally
LLines.Free;
end;
end;
function GetStackInfoStringProc(Info: Pointer): string;
begin
Result := string(PChar(Info));
end;
procedure CleanUpStackInfoProc(Info: Pointer);
begin
StrDispose(PChar(Info));
end;
initialization
// Start the Jcl exception tracking and register our Exception
// stack trace provider.
if JclStartExceptionTracking then
begin
Exception.GetExceptionStackInfoProc := GetExceptionStackInfoProc;
Exception.GetStackInfoStringProc := GetStackInfoStringProc;
Exception.CleanUpStackInfoProc := CleanUpStackInfoProc;
end;
finalization
// Stop Jcl exception tracking and unregister our provider.
if JclExceptionTrackingActive then
begin
Exception.GetExceptionStackInfoProc := nil;
Exception.GetStackInfoStringProc := nil;
Exception.CleanUpStackInfoProc := nil;
JclStopExceptionTracking;
end;
end.
很是优美。但单步去跟,发现GetExceptionStackInfoProc中,其返回值为空。
作者也发现了,所幸另个人解决这个问题,替换其函数如下:
function GetExceptionStackInfoProc(P: PExceptionRecord): Pointer;
var
LLines: TStringList;
LText: String;
LResult: PChar;
jcl_sil: TJclStackInfoList;
begin
LLines := TStringList.Create;
try
jcl_sil := TJclStackInfoList.Create(True, 7, p.ExceptAddr, False, nil, nil);
try
jcl_sil.AddToStrings(LLines, true, true, true, true);
finally
FreeAndNil(jcl_sil);
end;
LText := LLines.Text;
LResult := StrAlloc(Length(LText));
StrCopy(LResult, PChar(LText));
Result := LResult;
finally
LLines.Free;
end;
end;
验证OK,解决问题!
这样,可以挂接Application.OnException,自个处理程序的Crash异常了。
注意事项:用JEDI求堆栈信息,须设置Delphi工程属性之Linking->Map file值为Detailed,它是基于解析map文件生成数据:
参考资料:
System.SysUtils.Exception.StackTrace
JEDI Code Library
Delphi - Trying to get StackTrace for an exception