如何在Delphi 7中获取当前方法的名称?

30

有没有办法知道我当前所在的方法名称?

这样就可以:

procedure TMyObject.SomeMethod();
begin
  Writeln('my name is: ' + <hocus pocus>); 
end;

将产生此输出:

我的名字是:SomeMethod

3个回答

33

JCL是免费的,并且具有相关功能。它取决于堆栈跟踪的质量以及调试信息的多少。

JclDebug.pas

function FileByLevel(const Level: Integer = 0): string;
function ModuleByLevel(const Level: Integer = 0): string;
function ProcByLevel(const Level: Integer = 0): string;
function LineByLevel(const Level: Integer = 0): Integer;

15
我会简单地补充一下:如果没有某种形式的调试信息,这是不可能完成的。 - Craig Stuntz

11

请参阅我们的TSynMapFile

它能够加载一个.map文件,并将其压缩成优化的二进制格式。它比.map本身要小得多(例如900 KB的.map -> 70 KB的.mab)。这个.mab可以很容易地嵌入到exe中。因此,它比JCL或MadExcept使用的格式更小,也比Delphi编译时嵌入的信息更小。

您可以按如下方式使用它:

Map := TSynMapFile.Create; // or specify an exe name
try
  i := Map.FindSymbol(SymbolAddr);
  if i>=0 then 
    writeln(Map.Symbols[i].Name);
  // or for your point:
  writeln(Map.FindLocation(Addr)); // e.g. 'SynSelfTests.TestPeopleProc (784)'
finally
  Map.Free;
end;

例如,以下是我们的日志类如何使用它。
procedure TSynLog.Log(Level: TSynLogInfo);
var aCaller: PtrUInt;
begin
  if (self<>nil) and (Level in fFamily.fLevel) then begin
    LogHeaderLock(Level);
    asm
      mov eax,[ebp+4]  // retrieve caller EIP from push ebp; mov ebp,esp
      sub eax,5        // ignore call TSynLog.Enter op codes
      mov aCaller,eax
    end;
    TSynMapFile.Log(fWriter,aCaller); // here it will call TSynMapFile for the current exe
    LogTrailerUnLock(Level);
  end;
end;

这种方法能够获取调用者的地址,并记录其单元名称、方法名称和行号。
注意/编辑:mORMot日志单元的源代码为SynLog.pas。更新后的文档可在此URI处找到。

你能否在普通的调试构建(或带有外部映射的发布版本)中使用此代码来记录调用堆栈,而无需添加Jedi调试代码等内容?在某些特定情况下,拥有能够记录和报告调用位置的代码可能非常有用。 - David
@DavidM 是的,你可以这样做。如果没有附加.map/.mab文件,它会记录十六进制地址。然后我们的LogView工具能够从现有的与.exe匹配的.map文件中检索源代码行。但是,当然,由于我们的.mab格式非常小,我不认为在编译时将其嵌入到.exe中会有任何理由不这样做。 - Arnaud Bouchez
@ArnaudBouchez,你能告诉我如何在编译时将其嵌入到.exe文件中吗? - SOUser
@XichenLi 请查看 https://github.com/synopse/mORMot/blob/master/SQLite3/Samples/11%20-%20Exception%20logging/Map2Mab.dpr - Arnaud Bouchez
@ArnaudBouchez,答案顶部的链接已经失效。对于其他人来说,相关单元似乎在这里:https://github.com/synopse/mORMot/blob/master/SynLog.pas - Dave Nottage

1
如果您有EurekaLog:
uses
  EDebugInfo;

procedure TMyObject.SomeMethod();
begin
  Writeln('my name is: ' + __FUNCTION__); 
end;

还有__FILE____MODULE____UNIT____LINE__,以及一个通用的GetLocationInfoStr函数。

然而:

  1. 它只在编译时使用一些调试信息(并且启用了相应的调试信息提供程序)才能工作:
  • EurekaLog有自己的调试信息格式,可以选择压缩(实际上不建议这样做,因为会消耗更多的内存和CPU)。
  • 它还支持JCL/JEDI、Synopse/MAB,以及.map、.tds/TD32、.dbg、.pdb。
  1. 不是一个常量。名称将被动态查找,因此它具有一些运行时成本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接