我知道Delphi XE2具有新的TVirtualInterface,用于在运行时创建接口的实现.不幸的是,我没有使用XE2,我想知道在旧版本的Delphi中如何处理这样的事情.
让我说我有以下界面:
IMyInterface = interface ['{8A827997-0058-4756-B02D-8DCDD32B7607}'] procedure Go; end;
在编译器的帮助下,是否可以在运行时绑定到此接口?
TMyClass = class(TObject,IInterface) public function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; procedure Go; //I want to dynamically bind IMyInterface.Go here end;
我试过一个简单的硬盘:
var MyInterface: IMyInterface; begin MyInterface := IMyInterface(TMyClass.Create); end;
但是编译器阻止了这一点.
然后我尝试一个cast,至少编译:
MyInterface := TMyClass.Create as IMyInterface;
所以我想象的关键是让QueryInterface返回一个有效的指针到被查询的接口的实现.在运行时如何构建一个?
我已经通过System.pas挖掘了,所以我至少对GetInterface,GetInterfaceEntry和InvokeImplGetter的工作方式很熟悉. (非常感谢Embacadero选择离开pascal源与优化的程序集).我可能没有正确阅读,但似乎可以有一个偏移量为零的接口条目,在这种情况下,有一种替代方法使用InvokeImplGetter分配接口.
我的最终目标是模拟一些具有反思支持语言的动态代理和嘲讽的能力.如果我可以成功地绑定到具有与接口相同的方法名称和签名的对象,那将是一个很大的第一步.这是甚么可能的,或者我正在树上错误的树?
解决方法
在运行时添加对现有类的接口的支持理论上可以做到,但是真的很棘手,并且需要D2010或更高版本来支持RTTI.
每个类都有一个VMT,而VMT有一个接口表指针. (请参阅TObject.GetInterfaceTable的实现.)接口表包含接口条目,其中包含一些元数据,包括GUID,以及指向接口vtable本身的指针.如果你真的想要,你可以创建一个接口表的副本(不要这样做原来的一个;你可能最终会破坏内存!)添加一个新的条目,它包含一个新的接口vtable与指针指向正确的方法(您可以通过使用RTTI查找它们来匹配),然后将类的接口表指针更改为指向新表.
要特别小心.这种工作真的不是因为微弱的心脏,在我看来,这是有限的效用.但是,这是可能的.