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