Delphi 如何获取给定类的虚拟重载过程的地址
一点背景。。。我需要给你写一个补丁 我不太喜欢重新编译VCL,我通常会选择 通常,它非常简单,编写替换方法并将其挂接在原始方法的位置:Delphi 如何获取给定类的虚拟重载过程的地址,delphi,Delphi,一点背景。。。我需要给你写一个补丁 我不太喜欢重新编译VCL,我通常会选择 通常,它非常简单,编写替换方法并将其挂接在原始方法的位置: HookProc(@TMenu.ProcessMenuChar, @TPatchMenu.ProcessMenuChar, Backup); 但这一次,该方法是虚拟的,并且重载。我似乎找不到可靠的方法来获得正确的方法地址 如果它没有重载,我可以简单地使用@TField.CopyData,它会工作,但这不能保证为重载函数提供正确的地址 如果它不是虚拟的,我可以扩
HookProc(@TMenu.ProcessMenuChar, @TPatchMenu.ProcessMenuChar, Backup);
但这一次,该方法是虚拟的,并且重载。我似乎找不到可靠的方法来获得正确的方法地址
如果它没有重载,我可以简单地使用@TField.CopyData
,它会工作,但这不能保证为重载函数提供正确的地址
如果它不是虚拟的,我可以扩展并执行以下操作
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地址)
我尝试了各种语法,这些语法在编译时都给了我“不兼容类型”
我确实提出了一个解决方案(作为答案发布),但它并不理想。我发现解决这个问题的唯一方法是创建类的实例
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;
希望有一个更优雅的解决方案。如果您知道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]
我希望您可以使用增强的RTTI提出一个更优雅的解决方案。但您通常在查找特定库版本中的修补程序错误时执行此操作。因此,您需要为特定版本硬编码值,否则会抛出编译器错误。至少,我是这么做的
说到这里,正如您在回答中所演示的,在启动时实例化一个实例并没有什么好羞愧的。只要没有不必要的副作用,我就看不到任何真正的负面影响 受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;
我相信这是最简单的。不是因为我感到羞耻或其他什么,我只是觉得为应该(并且是)随时可用的东西添加创建对象的开销很愚蠢。并不是说在这个特定的环境中,开销很大。但当我无法以我认为应该可以实现的方式完成某件事情时,它会让我感到厌烦。如果你知道assembler,它有一个vmtoffset,但对于重载的虚拟方法,它可能会失败。@Rudy不,偏移量是固定的。每个重载都不同,但都是固定的。我知道虚拟方法的偏移量是固定的,但VMTOFFSET可能无法识别您可能要解决的重载,只需选择第一个,与原始问题相同,即解决正确的重载。这应该有一个机制。。
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;