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();
这是按预期工作的.输出是:
Entering F1
Doing some stuff…
Leaving F1 – Result = Booyah!
我现在正在寻找一种方法来最小化调用TraceMethod()所需的参数数量,理想情况下允许我完全跳过与Result相关的参数.我自己没有使用汇编程序或堆栈布局的经验但是如果我没有弄错,从我见过的其他人所做的“魔法”判断,至少隐含的魔法Result-variable的内存地址应该可以以某种方式获得,不应该它?也许有人可以从那里得到它的类型信息吗?
当然,如果有可能甚至可以确定“周围”函数本身的名称,这将消除完全将参数传递给TraceMethod的需要……
我正在使用Delphi XE2,因此可以使用所有最近引入的语言/框架功能.
在任何人提到它之前:我的实际代码已经使用CodeSite.EnterMethod / ExitMethod而不是Writeln调用.我也知道这个简化的例子不能处理复杂的类型,也不会执行任何错误处理.
你最好的选择是真正传递@Result.如果不这样做,则无法保证Result甚至有地址.返回简单类型(如Integer和Boolean)的函数将结果放入EAX寄存器.如果没有理由让结果有地址,那么编译器就不会为它分配任何内存.使用@Result表达式强制编译器为其指定地址.但是,仅仅知道地址不会得到返回类型.可能有一种方法可以通过RTTI发现它.这将涉及三个步骤:
>从方法名称中提取类名.然后你可以get the RTTI for that type.这将要求方法名称包含类的明确名称(包括单位名称).
>使用该类型的list of methods,找到该方法的RTTI.由于名称不一定唯一地标识方法,因此这将变得复杂.重载都将显示相同的名称. (Rruz在Invoke
方法的上下文中显示了how to deal with RTTI of overloaded methods.)此外,从调试信息中获得的方法名称不一定与RTTI名称匹配.
您可以改为遍历所有类的方法,而不是尝试匹配名称,搜索其CodeAddress
属性与调用方地址匹配的方法.然而,确定如何获得调用者的起始地址(而不是返回地址)比我预期的更难找到.
>获取the method’s return type并使用Handle
属性最终获得所需的PTypeInfo值.