考虑这个用于方法跟踪的典型代码(为了说明而简化):

type
  IMethodTracer = interface
  end;

  TMethodTracer = class(TInterfacedObject, IMethodTracer)
  private
    FName: String;
    FResultAddr: Pointer;
    FResultType: PTypeInfo;
  public
    constructor Create(
      const AName: String;
      const AResultAddr: Pointer = nil;
      const AResultType: PTypeInfo = nil);
    destructor Destroy; override;
  end;

constructor TMethodTracer.Create(
  const AName: String;
  const AResultAddr: Pointer;
  const AResultType: PTypeInfo);
begin
  inherited Create();
  FName := AName;
  FResultAddr := AResultAddr;
  FResultType := AResultType;
  Writeln('Entering ' + FName);
end;

destructor TMethodTracer.Destroy;
var
  lSuffix: String;
  lResVal: TValue;
begin
  lSuffix := '';
  if FResultAddr <> nil then
    begin
      //there's probably a more straight-forward to doing this, without involving TValue:
      TValue.Make(FResultAddr, FResultType, lResVal);
      lSuffix := ' - Result = ' + lResVal.AsString;
    end;
  Writeln('Leaving ' + FName + lSuffix);

  inherited Destroy;
end;

function TraceMethod(
  const AName: String;
  const AResultAddr: Pointer;
  const AResultType: PTypeInfo): IMethodTracer;
begin
  Result := TMethodTracer.Create(AName, AResultAddr, AResultType);
end;

//////

function F1: String;
begin
  TraceMethod('F1', @Result, TypeInfo(String));
  Writeln('Doing some stuff...');
  Result := 'Booyah!';
end;

F1();

这是按预期工作的。输出是:



我现在正在寻找一种方法来最小化调用 TraceMethod() 所需参数的数量,理想情况下允许我完全跳过与 Result 相关的参数。我自己没有汇编程序或堆栈布局的经验,但如果我没有弄错,根据我看到其他人所做的“魔术”判断,至少应该可以以某种方式获得隐含的魔术 Result -变量的内存地址,不应该它?也可能有人可以从那里工作以获取其类型信息?

当然,如果有可能甚至确定“周围”函数本身的名称,这将完全消除将参数传递给 TraceMethod 的需要......

我使用的是 Delphi XE2,因此可以使用所有最近引入的语言/框架功能。

在任何人提到它之前:我的实际代码已经使用 CodeSite.EnterMethod/ExitMethod 而不是 Writeln -calls。我也知道这个简化的例子不能处理复杂的类型,也不执行任何错误处理。

最佳答案

最好的办法是直接传入 @Result 。如果不这样做,则无法保证 Result 甚至有地址。返回简单类型(如 IntegerBoolean)的函数将结果放在 EAX 寄存器中。如果结果没有理由有地址,那么编译器就不会为其分配任何内存。使用表达式 @Result 强制编译器给它一个地址。

但是,仅仅知道地址不会让您获得返回类型。可能有一种方法可以通过 RTTI 发现它。它将涉及三个步骤:

  • 从方法名中提取类名。然后你可以 get the RTTI for that type 。这将要求方法名称包含类的明确名称(包括单元名称)。
  • 使用该类型的 list of methods,找到该方法的 RTTI。由于名称不一定唯一标识方法,这将变得复杂。重载将全部显示相同的名称。 (Rruz 在 how to deal with RTTI of overloaded methods 方法的上下文中显示了 Invoke 。)此外,您从调试信息中获得的方法名称不一定与 RTTI 名称匹配。

    您可以不尝试匹配名称,而是循环遍历所有类的方法,搜索其 CodeAddress 属性与调用者地址匹配的方法。不过,事实证明,确定如何获取调用者起始地址(而不是返回地址)比我预期的更难找到。
  • 获取 the method's return type 并使用 Handle 属性最终得到你想要的 PTypeInfo 值。
  • 关于Delphi 汇编程序/RTTI 大师 : Can I obtain the memory address and type info of the implied Result variable in a function?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10819770/

    10-12 16:27