• Delphi:Exception输出堆栈信息


    起源:

    用习惯了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

  • 相关阅读:
    Google-C++编码规范中文版.pdf
    100个gdb小技巧(v1.0).pdf
    NSIS 3.0 发布,Windows 安装程序制作工具
    python爬取各类文档方法归类汇总
    【转】openwrt中ubus
    OpenWrt源码分析之ubus
    详解C语言中的fopen()函数和fdopen()函数
    IPsec技术介绍(转)
    mxml 详解
    Delphi IDE Theme Editor, Delphi IDE 主题编辑器,支持D7~Rad Studio 10.3 RIO及Lazarus
  • 原文地址:https://www.cnblogs.com/crwy/p/8561019.html
Copyright © 2020-2023  润新知