例如,假装有一个类:
TLocalization = class(TObject) ... public function GetLanguageName: string; end;
或者假装有一个班级:
TTestCase = class(TObject) ... public procedure Run; virtual; end;
或者假装有一个班级:
TPlugIn = class(TObject) ... public procedure Execute; virtual; end;
或者假装有一个班级:
TTheClassImInterestedIn = class(TObject) ... public procedure Something; end;
在运行时,我想找到从TTestCase下降的所有类,以便我可以使用它们.
另外:德尔福有没有办法走每一堂课?然后我可以简单地打电话:
RunClass: TClass; if (RunClass is TTestCase) then begin TTestCase(RunClass).Something; end;
也可以看看
> Finding all classes that derive from a given base class in python
> Java: At runtime,find all classes in app that extend a base class
解决方法
仅供参考:以下描述是我在Delphi 5最新版本时实际编写的一段代码的高级概述.最大的.从那以后,该代码被移植到更新的Delphi版本(目前直到Delphi 2010)并且仍然有效!
对于初学者,您需要知道一个类只不过是VMT和附带函数的组合(可能还有一些类型信息,具体取决于编译器版本和设置).您可能知道,类(由TClass类型标识)只是指向该类VMT的内存地址的指针.换句话说:如果您知道类的VMT的地址,那么它也是TClass指针.
有了这条知识牢记在你的脑海中,你实际上可以扫描你的可执行内存,并为每个地址测试它是否看起来像’VMT.似乎是VMT的所有地址都可以添加到列表中,从而可以完整地概述可执行文件中包含的所有类! (实际上,这甚至可以让您访问仅在单元的实现部分中声明的类,以及从作为二进制文件分发的组件和库链接的类!)
当然,有一些地址似乎是一个有效的VMT,但实际上是一些随机的其他数据(或代码) – 但是我已经提出了测试,这在我身上从未发生过(大约6年)在十多个主动维护的应用程序中运行此代码).
所以这是你应该做的检查(按照这个确切的顺序!):
>地址是否等于TObject的地址?如果是这样,这个地址是VMT,我们就完成了!
>读取TClass(地址).ClassInfo;如果已分配:
>它应该属于一个代码段(不,我不会详细介绍它 – 只是google it up)
>此ClassInfo的最后一个字节(通过添加SizeOf(TTypeInfo)SizeOf(TTypeData)确定)也应该属于该代码段
>此ClassInfo(类型为PTypeInfo)应将其Kind字段设置为tkClass
>在此ClassInfo上调用GetTypeData,生成一个PTypeData
>这也应该属于有效的代码段
>它的最后一个字节(通过添加SizeOf(TTypeData)确定)也应该属于该代码段
>在这个TypeData中,它的ClassType字段应该等于被测试的地址.
>现在读取偏移量vmtSelfPtr上的VMT,并测试是否会导致测试地址(应指向自身)
>读取vmtClassName并检查它是否指向有效的类名(检查指针再次驻留在有效段中,字符串长度是否可接受,并且IsValidIdent应返回True)
>读取vmtParent – 它也应该属于有效的代码段
>现在转换为TClass并读取ClassParent – 它也应该属于有效的代码段
>读取vmtInstanceSize,它应该是> = TObject.InstanceSize和< = MAX_INSTANCE_SIZE(你要确定)
>从它的ClassParent读取vmtInstanceSize,它也应该是> = TObject.InstanceSize和< =先前读取的实例大小(父类永远不能大于子类)
>或者,您可以检查索引0及更高版本中的所有VMT条目是否都是有效的代码指针(尽管确定VMT中的条目数有点问题…但没有指标).
>使用ClassParent递归这些检查. (这应该达到上面的TObject测试,或者悲惨地失败!)
如果所有这些检查都成立,则测试地址是有效的VMT(就我而言)并且可以添加到列表中.
祝你好好实现这一切,我花了大约一个星期来做到这一点.
请告诉你它是如何工作的.干杯!