如何访问私有成员变量和函数 (修正版)

这篇文章介绍一些 Delphi 穿透 OOP 约束的技巧。
访问保护的 (protected) 变量
如果是某个类的保护变量,可以在任何地方,通过如下方法访问:
type
  TSomeClassAccess = 
class(TSomeClass);

begin
  TSomeClassAccess(Object1).protected_Bool := False;
  TSomeClassAccess(Object1).protected_Int  := 
0;
  ...
end;

 
访问私有的 (private) 变量
如果是某个类的私有变量,我们需要计算该变量在 VMT 中的偏移量。同时由于类声明可能在不同的 Delphi 版本中也有所不同,所以最好事先先检查一下源码。例:访问 TMenuItem 的第二个内部成员变量 FHandle
type
  THackMenuItem = 
class(TComponent)
  
protected //<-- change to protected 
    FxxxxCaption: AnsiString;
    FHandle: HMENU;
//<-- the property you want to access
begin
  THackMenuItem(AMenuItem).FHandle := 0;
  ...
end;

 
访问部分私有的 (private) 函数
访问私有函数要相对困难许多。据我所知只有定义时声明为 virtualoverridedynamic message 私有函数才可以被访问或替换。其实现原理和访问私有变量相似:先计算该函数在 VMT/DMT 中的偏移量然后把该内存地址替换成新函数的内存地址。具体做法可以参考 TntControls TntSystem.pas 安装系统补丁或者是 Fastcode 控件包。
友情提醒:(1) 通常私有函数中会涉及到一些访问其它的私有函数/变量。往往为了访问一个私有函数,还需要修改更多个私有函数/变量。相对比较复杂,也不很可靠。(2) 内存地址修改不当会引发于一些软件的冲突,如 AQTime
注:如果变量定义时未设置关键字 strict,同一个单元里面的所有类的内部变量/函数可以直接相互访问。这个是 Delphi 开的一个不大不小的后门。
 
增加私有的 (private) 函数 (a)
我们真的需要吗?有时候要!比如我在写一个 Dunit 单元测试的时候,需要接受 WM_COPYDATA 消息。但是这个消息是发送给 GUITestRunner 的,我必须打入 GUITestRunner 才可以得到相应消息。实现方法和访问私有函数类似,我们计算 VMT/DMT 的长度,在尾端增加一个新的函数指针。我在网上找到一篇文章说的比较详细,请点击这里查看。这个方法有个限制:这个修改不是编译期完成的,你必须在类实例化之后,才进行修改。如果这个类被频繁使用,这个做法显然有些麻烦。如果能把这段修改代码放如类的构析函数中,问题就迎刃而解了。我在假想:我们是否可以使用介绍3中引入的方法,实现这个……

增加私有的 (private) 函数 (b)
还有一种更干净的修改方法,我们定义一个同名的类来欺骗编译器。例:我们创建一个新的单元,名为 GUITestRunnerPatch.pas
type
  THackGUITestRunner = 
class(GUITestRunner.TGUITestRunner)
  
private
    procedure WMCopyData(var Msg: TWMCopyData);
message WM_COPYDATA; //<-- the method you want to append
  end;

在需要使用GUITestRunner 的地方加上新的GUITestRunnerPatch。注意:一定要在其之后,否则编译器不会调用你修改过的这个类,而是调用了原先的那个。
 
总结:
在进入 Delphi 的内部世界时,我们要尽可能的考虑代码的移植性和通用性,要以少量修改换回最佳效果。如果上面介绍的修修补补不能解决你的问题,当然你也可以直接修改 Delphi 的控件源码。修改之后,勾选编译选项里面的 „Use Debug DCUs“,并编译程序,再将编译得到的 dcu 文件保存到编译目录下面。我一般创建2个目录:PatchedVCLs PreCompiled 放修改过的源码,和编译之后的版本。然后把这个2个目录定义到环境变量里面,这样只要在每个项目的路径设置中添加这些变量,就可以使用改动过的代码了。如果本文不正确的地方,欢迎拍砖。
 
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页