一些上下文......我需要为a bug in the VCL's TField.CopyData routine编写修复程序。
不是重新编译VCL的忠实粉丝,我通常选择method hooks。
通常,它非常简单,写下替换方法并将其挂钩代替原始方法:
HookProc(@TMenu.ProcessMenuChar, @TPatchMenu.ProcessMenuChar, Backup);
但这一次,该方法是虚拟的并且过载了。我似乎无法找到一种可靠的方法来获得正确的方法地址。
如果它没有超载,我可以简单地使用@TField.CopyData
并且它会起作用,但是不能保证为重载函数提供正确的地址。
如果它不是虚拟的,我可以扩展the method described here并执行以下操作
type
TCopyDataMethod = procedure(Source, Dest : TValueBuffer) of object;
procedure DoHook;
var vOldMethod : TCopyDataMethod;
begin
vOldMethod := TField(nil).CopyData;
HookProc(TMethod(vOldMethod).Code, @TSomeClass.NewMethod, Backup);
end;
但是这会产生访问冲突(它使用nil引用在VMT中查找正确的CopyData地址)。
我尝试了各种语法,这些语法都给了我"不兼容的类型"在编译时。
我确实提出了一个解决方案(发布为答案),但它并不理想。
答案 0 :(得分:1)
受David Heffernan的回答启发,这是我正在寻找/希望的解决方案。
type
TCopyDataMethod = procedure(Source, Dest : TValueBuffer) of object;
procedure Proc;
var VMT : NativeInt;
vMethod : TCopyDataMethod;
begin
VMT := NativeInt(TField);
vMethod := TField(@Vmt).CopyData;
uOriginalAddress := @vMethod;
[...]
end;
我相信这是最直接的。
答案 1 :(得分:0)
我发现此问题的唯一解决方案是创建类
的实例procedure Proc;
var vMethod : TCopyDataMethod;
vFieldOrig : TField;
[...]
begin
vFieldNew := nil;
vFieldOrig := TField.Create(nil);
try
vMethod := vFieldOrig.CopyData;
uOriginalAddress := @vMethod;
[...]
finally
vFieldOrig.Free;
end;
end;
希望有一个更优雅的解决方案。
答案 2 :(得分:0)
如果您知道VMT中的索引,则可以直接从该类获取地址。如果您在调试器下查看对CopyData
的调用,您会看到以下内容:
Project1.dpr.19: THackField(Field).CopyData(TValueBuffer(nil), TValueBuffer(nil)); 0053886B 33C9 xor ecx,ecx 0053886D 33D2 xor edx,edx 0053886F A104345400 mov eax,[$00543404] 00538874 8B18 mov ebx,[eax] 00538876 FF93A4000000 call dword ptr [ebx+$000000a4]
在这里我们可以看到,在我的XE7中,方法点与VMT的偏移量为$000000a4
个字节。该类是指向VMT的指针。
所以,你可以得到这样的方法地址:
Pointer(NativeUInt(TField) + $00a4)
我希望您能够使用增强型RTTI提出更优雅的解决方案。但是,当您想要修补特定库版本中的错误时,通常会这样做。所以你硬编码特定版本的值,否则抛出编译器错误。至少,这就是我的工作!
说完所有这些之后,在你的答案中证明,在启动时实例化实例并没有什么可羞愧的。只要这没有不必要的副作用,我就看不到任何真正的缺点。