Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 如何获取给定类的虚拟重载过程的地址_Delphi - Fatal编程技术网

Delphi 如何获取给定类的虚拟重载过程的地址

Delphi 如何获取给定类的虚拟重载过程的地址,delphi,Delphi,一点背景。。。我需要给你写一个补丁 我不太喜欢重新编译VCL,我通常会选择 通常,它非常简单,编写替换方法并将其挂接在原始方法的位置: HookProc(@TMenu.ProcessMenuChar, @TPatchMenu.ProcessMenuChar, Backup); 但这一次,该方法是虚拟的,并且重载。我似乎找不到可靠的方法来获得正确的方法地址 如果它没有重载,我可以简单地使用@TField.CopyData,它会工作,但这不能保证为重载函数提供正确的地址 如果它不是虚拟的,我可以扩

一点背景。。。我需要给你写一个补丁

我不太喜欢重新编译VCL,我通常会选择

通常,它非常简单,编写替换方法并将其挂接在原始方法的位置:

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;