ATL interface map macro detailed [3]

zhaozj2021-02-16  70

Second, COM_ITERFACE_ENTRY2 (x, x2)

The ATL is a multi-inheritance method to implement the components, but if there are multiple branches in the inheritance tree to implement the same interface, when querying this interface, you need to know which branch is returned to it. This macro is that this macro is used for the iDispatch interface. Let's take a look at its typical usage:

class COuter: public IDispatchImpl , // IOuter1 a dual interface public IDispatchImpl , // IOuter2 is a dual interface public ... {public: COuter () {}. .. begin_com_map (couter) com_interface_entry2 (iDispatch, IOUTER2), // will expose the route inherited by IOUTER2, COM_ITERFACE_ENTRY (IOUTER1) COM_INTERFACE_ENTRY (IOUTER2) ​​... END_COM_MAP};

IdispatchImpl <...> This class implements the IDispatch interface, so there are two iDispatch implementations in the components. Which implementation is returned when querying the idispatch interface?

Let's take a look at the definition of COM_ITERFACE_ENTRY2 (X, X2)

#define begin_com_map (x) public: / typedef x _commapclass; / .................. #define com_interface_entry2 (x, x2) / {& _tl_iidof (x), / // Get the IID value (DWORD) of the interface ((x *) (_ command *) 8) - 8, / _ATL_SIMPLEMAPENTRY}, // indicates a simple interface

The problem is in (dword) ((x *) (_ command *) 8) - 8 What does it mean?

Let's take a look at the following code:

Class a1 {public: Virtual void test () {}}; Class A2: public a1 {public: Virtual void test () {}}; class a3: public a1 {public: Virtual void test () {}}; Class A : Public A2, Public A3 {}; {DWORD DW; DW = (DWORD) ((A *) 8); // dw = 0x08 dw = (dword) ((A3 *) (A *) 8); // DW = 0x0C DW = (DWORD) ((A1 *) (A3 *) (A *) 8); // DW = 0x0C DW = (DWORD) ((A1 *) (A3 *) (A *) 8) 8; // dw = 4}

This inheritance is a typical diamond structure, and two virtual function table pointers are saved in class A, which represents two branches of it. When an object is declared for class a, the system will allocate memory. The top of this memory retains two virtual function table pointers. The results of the analysis program run can be seen that the last result 4 represents the offset between the virtual function table pointer to the interface A3 and the tip of the memory block of the class A object.

Let's take a more complex point inheritance:

Class b1 {public: Virtual void test () {}}; class b2 {public: Virtual void test () {}}; class b3 {public: public: Virtual void test () {}}; Class B4: Public B1, PUBLIC B2 {Public: Virtual Void Test () {}}; Class B5: Public B2, Public B3 {PUBLIC: Virtual Void Test () {}}; Class B: Public B4, Public B5 {}; {DWORD DW; DW = (DWORD) ((b *) 8); // dw = 0x08 dw = (dWord) ((b5 *) (b *) 8); // dw = 0x10 dw = (dword) ((b2 *) ((b2 *) B5 *) (b *) 8); // dw = 0x10 dw = (dWord) ((b2 *) (b5 *) (b *) 8) - 8; // dw = 8} class B will retain four Virtual function table pointer because it has four branches. Our goal is to get B2 interface in this branch in this branch, and the last result 8 is what we need, it represents the offset of the block B memory block.

From the above two examples, we have understood (DWORD) ((x *) (xMMAPCLASS *) 8)) - 8 The role we can get the interface we need through this value.

Below we analyze our actual situation COM_ITERFACE_ENTRY2 (Idispatch, IOUTER2)

IdispatchImpl template class is derived from class T, so couter wants to inherit from two of its template classes, IOUTER1, IOUTER2 is a double interface, which is derived from idispatch, so you can get COUTER has two branches, also a rhombus structure, so according to our example, this offset value should also be 4. To prove our ideas, let's verify our results through the function stack.

Function Stack:

5.atl :: atlinternalQueryinterface (...) 4.atl :: ccomobjectrootbase :: InternalQueryInterface (...) 3.cmyObject :: _ internalQueryinterface (...) 2.atl :: ccomobject :: queryinterface ..) 1.Punk-> queryinterface (IID_IDISPATCH, (Void **) & pdispatch)

Explanation:

1: This is our verification code, PUNK is the iUnknown pointer of the component.

2 - 5: These code we are now very familiar, we only need to see the specific implementation of AtlinternalQueryInterface.

ATLINLINE ATLAPI AtlInternalQueryInterface (void * pThis, const _ATL_INTMAP_ENTRY * pEntries, REFIID iid, void ** ppvObject) {........... while (pEntries-> pFunc! = NULL) {BOOL bBlind = (pEntries- > PIID == NULL); if (bblind || inlineisequalguid (* (pentries-> piid), IID)) {if (pentries-> pfunc == _tl_simplemapentry) // offset {atlassert (! BBLIND); iUnknown * punk = (IUNKNOWN *) ((int) PTHIS PENTRIES-> DW); punk-> addref (); * ppvobject = punk; return s_ok;} ...../ If it is not simple interface ...} Pentries ; } Return E_NOINTERFACE;} Key sentence is iUnknown * punk = (iUnknown *) ((int) PTHIS PENTRIES-> DW); By observing the variable, as we expected Pentries-> dw = 4. (int) PTHIS PENTRIES-> DW) guarantees that we can get the virtual functions of the IOUTER2 branch, but because iDispatch is inherited from iUnknown, putting the virtual function pointer of iUnknown in the virtual function table, so (IUNKNOWN) *) Forced conversion, you can get the top address of this virtual function table, which is what we need. Maybe I will ask why the address of the virtual function table is not the address of a class instance? Don't forget, the interface is no data, it only has a pure virtual function. For customers, it can only access it through the virtual function defined by the interface, and it is impossible to access the member variables of the class that implement the interface, and the components are not visible to the customer, so only the virtual function table is used. The address is OK.

转载请注明原文地址:https://www.9cbs.com/read-15320.html

New Post(0)