How to get an object instance that implements the interface through the COM interface - Heroyin
Origin
My program is a COM-based plug-in structure, and the framework needs to pass an IResource interface to the plugin. IResource needs to pass different content according to different plugins. Interface definition IResource = Interface (IDispatch) Function GetPath: String; safecall; End; implementation class TResource = TClass (TAutoObject, IResource) protected Function GetPath: String; SafeCall; Public Path: String; End; Function GetPath: String; Begin Result: = Path; End; call part: Var Resource: IResource; ResourceObj: TResource; Begin Resource: = CreateComObject (CLASS_Resource) As IResource; // want to get TResource by casting; and failed :( ResourceObj: = TResource (Resource); ResourceObj.path: = 'Set different value'; end; How can I get TRESOURCE through IResource to achieve the purpose of setting the PATH value? The current I use is to define an ISetValue interface to modify the Path property in the interface to modify the Path property inside, feel It is more troublesome.
Extension of the problem
If you start from a solution, you can get an object by defining the configuration interface, such as: iobjref = interface function getobjref: Tobject; SaFECALL; END; This is obtained, then assigns the PATH, which does not destroy the COM package, which is also clear. The problem is basically solved. However, in the start of the relationship between the analysis Delphi object and the interface, we continue to be proposed in the title: How to get an object instance that implements the interface through the COM interface?
Savetime's clue
Http://www.delphibbs.com/delphibbs/dispq.asp?lid=2433841 Savetime Required Memory Space Implementing between Objects and Interfaces between Delphi in Delphi: -------- ---------------------------------------------------------------------------------------------------------------------- ---- | ----------------- Object / Interface Pointer | Object Memory Space | | Universal Method | -------------- - | ----------------- | ------------------------------------------------------------------------------------------------ | -------------- MyObject -> | VMTPTR 00 | ---------> | Virta 00 | | FREFCOUNT 04 | | Virtb 04 | Myintf -> | | iterface 08 | ---- | | Ffielda
0C
| | | Iterface jump table | | ffieldb 10 | | ---------> | Addr of queryinterface | myintfb -> | intentfb 14 | --------- | | | Myintfa -> | IINTFA 18 | - | | | | | | | | | | | | | | | | | | | | | | | | | ingtfa jump Table | | -----------> | AddR of Proca | | ------------------------- -------------------------------------------------- --- An object is when the class is called, for example, execute MyObject.proca, implies a Self pointer to this member function: MyObject.Proca (Self). Self is the address of the object data space. So how do the compiler know the SELF pointer? The original object pointer myObject points to the address is Self, and the compiler takes out myObject ^ as Self. When calling a member function in an interface, such as Myintfa.Proca, this time the compiler does not know which type (Class) object pointing to the end, can't know the distance between Myintfa and Self (actually, on it) In the example, the Delphi compiler knows the distance between MyINTFA and Self, just to compatibility with COM's binary format, so that other languages can use the interface pointer to invoke the interface member function, and must use the later Self pointer correction), the compiler directly MYINTFA The pointing address is set to Self. As you can see from the above figure, Myintfa points to $ 18 offset addresses in myObject object space. At this time, the Self pointer is of course wrong. The compiler cannot directly call TMYObject.proca, but call the PROCA in IINTFA's "Interface Jump Table". The content of Proca in the Interface Jump Table is to fix the Self Pointer (Self - $ 18), then call TMYOBJECT.PROCA, which is the correct call of the object.
Since each class implements the order of the interface is not necessarily the same, there is a different interface jump watch for the same interface, there is a different interface jump table (of course, the editor can be smart to check to some classes "interface jump The table "The offset can also be shared). The key to solving the problem is obtained here. If you can get the offset address of the interface, you can get the object instance ~~ See the dawn, come on! Looking for offset addresses
As we all know, all Delphi objects are inherited from TOBJECT, and create objects are also classified by Class Function Tobject.initInstance (Instance: Pointer): TOBJECT; to allocate memory space and carefully analyze this code. class function TObject.InitInstance (Instance: Pointer): TObject; {$ IFDEF PUREPASCAL} var IntfTable: PInterfaceTable; ClassPtr: TClass; I: Integer; begin FillChar (Instance ^, InstanceSize, 0); PInteger (Instance) ^: = Integer (Self); classptr: = self; while classptruth: = classptr.getinterface: = classptr.getinterface; if INTFTABLE <> NIL THEN for i: = 0 to intftable.EnTrycount-1 do with intftable.entries [i] do begin IF vTable <> nil dam is its Ioffset, which is an offset address of the interface Pinteger (@pchar (instance) [ioffset]) ^: = integer (vtable); end; classptr: = Classptr.classparent; Result: = Instance; end; found iOffset, tracking found that it belongs to the interface identifier of the interface items (PInterfaceEntry) PInterfaceEntry = ^ TInterfaceEntry; TInterfaceEntry = packed record IID: TGUID; VTable: Pointer; iOffset: Integer; ImplGetter: Integer; end The problem came out, getting PinterfaceEntry get everything
Easy getting PinterfaceEntry
Var eResourceObj: TResource; eEntry: PInterfaceEntry; eAutoObjFactory: TAutoObjectFactory; Begin eResource: = CreateComObject (CLASS_Resource) as IResource; // get the class factory eAutoObjFactory: = TAutoObjectFactory (ComClassManager.GetFactoryFromClassID (CLASS_Resource)); // get the interface identifier of the interface items Eentry: = EAUTOBJFACTORY.DIENTFENTRY; / / IOFFSET For the offset address of the interface, ERESOURCE minus the address obtained by IOFFSET is the object instance ERESOURCEOBJ: = TRESOURCE (INTEGERES) --EENTRY.IOffset; ERESourceObj.path: = ' Set different values ''; end; conclusion
The results of Fei Jin Week may have not much significance for the entire problem, but the process is really very meaningful. Through this process, I have a deeper understanding of the essence of Delphi objects and interfaces.