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值.
