ATL interface mapping macro detailed

xiaoxiao2021-03-06  45

Preface: After recenting the interface mapped by the ATL, I don't know how deep it, I suddenly wrote the idea of ​​writing it. ATL defines a lot of interface mapping macro, there are still more important, although it is not necessary to make it very clear, but in the process of in-depth learning, you can learn other ATL classes. The mechanism for it can also be more clear, it should still be some benefit. I will write it out according to the process of learning, and I don't know if you can understand. I want to imitate the teacher's hand to explain the internal details, but I don't dare to say the beautiful name "in-depth shallow", huh, I only hope that I can help everyone.

Introduce the interface map macro macro in each form of COM_Interface_entry_xx in ATL, and will be explained in the order from an easy to difficult, and each part will be based on the previous part. Every part will analyze the actual call function stack, and the writing of the stack is from bottom to. The code involved in the article is a slight written, only listing the relevant part.

First, com_interface_entry (x)

First we start from a typical application:

Define a simplest ATL DLL:

class ATL_NO_VTABLE CMyObject: public CComObjectRootEx, public CComCoClass, public IDispatchImpl {..... BEGIN_COM_MAP (CMyObject) COM_INTERFACE_ENTRY (IMyObject) // one pair of interfaces COM_INTERFACE_ENTRY (IDispatch) END_COM_MAP () .....};

Write a shortest query interface code:

IUnknown * pUnk; IMyObject * pMyObject; CoCreateInstance (CLSID_MyObject, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **) & pUnk); pUnk-> QueryInterface (IID_IMyObject, (void **) & pMyObject);

Execute a customer code, first we look at how the component object is created.

Function call stack one:

4 ......... 3.atl :: ccomcreator > :: createinstance (...) 2.atl :: ccomcreator2 >, atl :: ccomcreator >> :: createInstance (...) 1.atl :: ccomclassfactory :: createInstance (...) 4.atl :: atlmodulegetClassObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObjectObject (.) ) 9 :: ccombobjectcached :: ccombobjectcached :: Queryinterface (...) 5.atl :: ccomcreator> :: createinstance (...) 4.atl :: atlmodulegetClassObject (...) 3.atl :: ccommodule :: getclassobject (...) 2.dllgetClassObject (...) 2.dllgetClassObject ...) 1.CocreateInstance (...) (client) is explained below:

1:

CoCreateInstance (CLSID_MYOBJECT, NULL, CLSCTX_INPROC_SERVER, IID_IUNKNOWN, (VOID **) & PUNK);

The inside will call the OLE API function COGETCLASSOBJECT (), and COGETCLASSOBJECT will load DLL through LoadLibrary (...), and call the dllgetClassObject () function in the DLL.

2:

STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID * ppv) {return _Module.GetClassObject (rclsid, riid, ppv);} where Notably _Module variables, global variables are defined in the DLL: CComModule _Module; ATL by a Group macro:

BEGIN_OBJECT_MAP (ObjectMap) OBJECT_ENTRY (CLSID_MyObject, CMyObject) END_OBJECT_MAP () #define BEGIN_OBJECT_MAP (x) static _ATL_OBJMAP_ENTRY x [] = {#define OBJECT_ENTRY (clsid, class) {& clsid, class :: UpdateRegistry, / class :: _ ClassFactoryCreatorClass :: CreateInstance /// key class :: _ CreatorClass :: CreateInstance, / NULL, 0, class :: GetObjectDescription, / class :: GetCategoryMap, class :: ObjectMain}, #define END_OBJECT_MAP () {NULL, NULL, NULL, NULL, NULL , NULL, NULL, NULL}; generating a static global _tl_objmap_entry type: Objectmap []; then ATL is in Bool WinApi Dllmain (Hinstance Hinstance, DWORD DWREASON, LPVOID / * LPRESERVED * / {..... _Module. INIT (Objectmap, Hinstance, & libid_test2lib); .....} In initialization _Module // Note What is the initialization in InitInstance () _Module So _Module initialization what? In fact, what is he? Nor did it, in ccommodule :: INIT, it calls atlmoduleinit (_ATL_MODULE * PM, _ATL_OBJMAP_ENTRY * P, Hinstance H), in which only one sentence is only one sentence: PM-> m_pobjmap = P; visible _Module is just to put this global object The mapping array ObjectMap [] is given. So why can I get the class factory with _Module.GetClassObject? In fact, The key lies in another base class ccomcoclass! In the CCOMCOCLASS defined in CCOMCOCLASS defines a macro declare_classfactory ().

#define DECLARE_CLASSFACTORY () DECLARE_CLASSFACTORY_EX (CComClassFactory) #define DECLARE_CLASSFACTORY_EX (cf) typedef CComCreator > _ClassFactoryCreatorClass; CComCreator, CComObjectCached Leave aside, but saw CComClassFactory, as the name suggests, we knew we were finally appeared to be the class factory ! There is a class factory object inside each component. Wrought around a large circle, we have now known that _Module contains the objects of each component we want, which is already enough for the current, and now continue the route! 3:

HRESULT CComModule :: GetClassObject (REFCLSID rclsid, REFIID riid, LPVOID * ppv) {return AtlModuleGetClassObject (this, rclsid, riid, ppv);} CComModule :: GetClassObject the implementation is very simple, is call the API function ATL. 4: ATLINLINE ATLAPI AtlModuleGetClassObject (_ATL_MODULE * pM, REFCLSID rclsid, REFIID riid, LPVOID * ppv) {_ATL_OBJMAP_ENTRY * pEntry = pM-> m_pObjMap; // remove the object mapping array while (pEntry-> pclsid = NULL!) From the _Module {if ((pEntry-> pfnGetClassObject! = NULL) && InlineIsEqualGUID (rclsid, * pEntry-> pclsid)) {if (pEntry-> pCF == NULL) {hRes = pEntry-> pfnGetClassObject (pEntry-> pfnCreateInstance, IID_IUnknown, (Lpvoid *) & pentry-> pcf);} if (PENTRY-> PCF! = Null) hres = pentry-> pcf-> queryinterface (riid, ppv); Break;} PENTRY = _nextObjectMapenTry (PM, Pentry);}} It's a bit not understanding now, it seems that we have to see the structure of _ATL_OBJMAP_ENTRY.

struct _ATL_OBJMAP_ENTRY {const CLSID * pclsid; HRESULT (WINAPI * pfnUpdateRegistry) (BOOL bRegister); _ATL_CREATORFUNC * pfnGetClassObject; _ATL_CREATORFUNC * pfnCreateInstance; IUnknown * pCF; DWORD dwRegister; _ATL_DESCRIPTIONFUNC * pfnGetObjectDescription; _ATL_CATMAPFUNC * pfnGetCategoryMap;} pclsid clearly represents us Component's CLSID; PFNGETCLASSOBJECT We also know it is CMYObject :: _ ClassFactoryCreatorClass :: CreateInstance (the CreateInstance function of the class object contained in our component); PCF we can also guess it is pointing to this type of Iunknown pointer, representative Whether this class is created, if the class object already exists, you don't have to create a new type of factory object. We still don't understand what is going on now. In fact, the answer is still in ccomcoClass! In CCOMCOLASS, the macro deflare_AGGREGATABLE (X) is defined. This macro indicates that this component can be gathered or uncomfortable. We are temporarily ignore, first look at its definition: #define declare_AGGREGATABLE (X) Public: / typef ccomcreator2 >, / ccomcreator >> _creatorclass; we have seen a familiar string _CREATORCLASS, which still has a component containing objects. But there is still a problem, we don't know why _classfactorycreator and _CREATORCLASS have to follow a CREATEINSTANCE? It seems that we must first see what CCOMCREATOR is something. template class CComCreator {public: static HRESULT WINAPI CreateInstance (void * pv, REFIID riid, LPVOID * ppv) {.....}}; original function CreateInstance which there is only one, we are finally generally understood _ClassFactoryCreatorClass :: CreateInstance means what it means, it represents ccomclassfactory :: CreateInstance (..), almost like this. Then let's take a look at the difference between ccomcreator2:

template class CComCreator2 {public:? static HRESULT WINAPI CreateInstance (void * pv, REFIID riid, LPVOID * ppv) {return (pv == NULL) T1 :: CreateInstance (NULL, riid, ppv): T2 :: CreateInstance (PV, RIID, PPV);}}; this class is very similar to CCMCREATOR, only a CREATEINSTANCE member function, from _creatorclass we can know that it actually contains two CComobject, ccomaggobject's CreateInstance function ( Through ccomcreator, where ccomobject is used for non-aggregated objects, ccomaggObject is used to set objects according to the situation. (The actually generated component objects in ATL is not CMYObject, but ccomobject, ccomaggobject, or ccompolyobile, this concept is very important, but now, don't talk now) Now we basically know what is going on, it It is created based on the address of the creation of the creation of the creation plant in the presence object mapping. PfNgetClassObject and PfncreateInstance We basically have already known what is going on, but there is a question why Pentry-> PFNCREATEINSTANCE is passed as a parameter in Pentry-> PfNgetClassObject (...)? The answer is below, let us continue to route! 5: ccomcreator :: CreateInstance (Void * PV, Refiid Riid, LPVOID * PPV) {T1 * P = NULL; Atltry (P = NEW T1 (PV)) // Create a class factory object if (p! = Null) {P -> setvoid (PV); p-> infinalconstructaddref (); hres = p-> firmconstruct (); p-> infinalconstructrelease (); if (hres == s_ok) hres = p-> queryinterface (riid, ppv); if (hres! = s_ok) delete p;}} Note that the T1 here is ccomobjectcached , this is the template parameter we give CCOMCREATOR. We have seen our familiar operators 'new'! Until now we have finally created a component of the component. But I haven't finished, continue to go down, what do you do in Setvoid (PV)?

void CComClassFactory :: SetVoid (void * pv) {m_pfnCreateInstance = (_ATL_CREATORFUNC *) pv;} We also remember when we used to CMyObject :: _ CreatorClass :: CreateInstance as an argument to pEntry-> pfnGetClassObject (...) it, then we I don't understand what is going on, I have already suddenly turned over! It turns out that the class is a factory that needs it to create a component object! Although we just guess this from literally, it is actually as it is expected, in CCOMCLASSFAACTORY: CREATEINSTANCE (...), we have seen M_PFNCREATEINSTANCE (Punkouter, Riid, PPVOBJ); now everything is already Understand, ATLs have been opened for us to create a class, and we have opened it. The rest of the process of creating components is already our very familiar process! But now, now we need to query an IUNKNOWN pointer for the class object, this pointer exists in the PENTRY-> PCF seen in front. 6: stdmethod (queryinterface) (Refiid IID, Void ** PPVObject) {return_internalQueryinterface (IID, PPVObject);} CCOMOBJECTCACHED :: queryinterface, as for this class, we are still not needed now I know, I am very tired, huh, huh. 7:

HRESULT _INTERNALQUERYINTERFACE (REFIID IID, VOID ** PPVObject) / {Return InternalQueryinterface (this, _Getentries (), IID, PPVObject);} All class_InternalQueryInterface (...) is defined in Begin_COM_MAP. CCOMOBJECTCACHED does not have a begin_com_map macro, so call now CCOMCLASSFAACTory. Note that the THIS pointer and interface mapping array _Getentries () is passed to INTERNALQUERYINTERFACE (), which is the basis for implementing queries (...). A static interface mapping array is defined in Begin_COM_MAP (X): _TL_INTMAP_ENTRY _ENTRIES []; Each interface mapping macro is actually an increase in this array. An interface map macro includes three parts: the IID number of the interface, the offset value (most of the time), the function that needs to be executed, does not have other functions for the general interface. _Getentries () is returning to this array. There are still some details of the issue. 8:

static HRESULT WINAPI InternalQueryInterface (void * pThis, const _ATL_INTMAP_ENTRY * pEntries, REFIID iid, void ** ppvObject) {... HRESULT hRes = AtlInternalQueryInterface (pThis, pEntries, iid, ppvObject); ...} is now called CComObjectRootBase: : InternalQueryinterface (...) 9: Now we finally arrived at QueryInterface's nose. AtlinternalQueryInterface (...) is the end point of the entire query process, which traverses the interface mapping table and makes the corresponding action according to each item. There are many kinds of news maps in ATL, there are many ways, but now we don't care about those, now we have to do it is to find an iUnknown interface, which is easy, we don't even need to traverse interface mapping tables.

ATLINLINE ATLAPI AtlInternalQueryInterface (void * pThis, const _ATL_INTMAP_ENTRY * pEntries, REFIID iid, void ** ppvObject) {ATLASSERT (pEntries-> pFunc == _ATL_SIMPLEMAPENTRY); if (ppvObject == NULL) return E_POINTER; * ppvObject = NULL; if ( Inlineisequalunknown (IID)) // USE First Interface {iUnknown * punk = (iUnknown *) ((int) PTHIS PENTRIES-> DW); punk-> addref (); * ppVObject = punk; return s_ok;} ... // There is also a lot of pile, but now you can't use it, save some space} There is a provision that the first interface of the interface mapping table must be _ATL_SIMPLEETRY. As for why this requirement, and pthis pentries-> DW, let's talk later, it is also a bunch of problems. In short, we are now having easy access to the class objects we need and the iUnknown pointer. 4: I think we can get back to the first step, but in atl :: atlmodulegetclassobject, stop, look at its source code, but also query the iClassFactory pointer by the iUnknown pointer you just received. . It is also the same call, just as in step 6 to 9, we will make the same call. Note that in step 9, we are no longer the iUnknown pointer, so we need to see the code I have not listed, but this is left to the next function stack and look at 1: Finally finally All operations of the creation of a class object have been completed, and now we have to do is the process of creating components of the CreateInstance (...) function we are familiar with the Create of the Capture Factory. As we see, now OLE starts calling ccomclassfactory :: createInstance (), we haven't forgotten, and the CreateInstance () function for creating components has been reserved in the class factory object, which is already very clear. 2. Don't repeat it, see step 4. 3. Don't repeat it, see step 4. 4. If we continue to route, our stack can be very long, but this is just a repeated mood. I didn't continue to go, I was very tired, hehe. Function call stack 2:

0: .......... 5.atl :: atlinternalQueryinterface (...) 4.atl :: ccomobjectrootbase :: InternalQueryinterface (...) 3.cmyObject :: _ InternalQueryinterface (...) 2.atl :: ccomobject :: queryinterface (...) 1.Punk-> queryinterface (iid_imyobject, (void **) & pMyObject); (client) Explain: 1. We have just obtained components objects The iUnknown interface pointer to query the IMYObject pointer, which is the pointer we really need. 2. Remember that we said that the ATL really created components is not CMYObject, but ccomobject, ccomaggobject or ccompolyobile, here we create ccomobject. So we must call CComobject :: queryinterface (...), and indeed CComobject Implement this function. STDMETHOD (QueryInterface) (REFIID IID, VOID ** PPVObject) {Return_internalQueryinterface (IID, PPVObject);} It is just simply call _internalQueryinterface (...), we also said that only the class has declated Begin_COM_MAP Hongcupo There is _internalQueryinterface (...), so now do it to its parent class CMYObject, so CMYObject :: _ interfaceQueryinterface (...) 3. After we are very familiar, it is already very familiar, but also to say it again Oh, huh 4. This call is also very familiar. Don't say more 5. Now we will query is a non-iunknown interface, so let's take a look at the code we have not listed before.

Atlinline Atlapi AtlinternalQueryInterface (Void * Pthis, Const _Tr_INTMAP_ENTRY * PENTRIES, REFIID IID, VOID ** PPVObject) {// Make sure the first item of the interface map is a simple interface // If you query the iUnknown interface, perform the appropriate operation // below will be traversed Interface mapping table, trying to find the corresponding interface while (pentries-> pfunc! = Null) {bool bblind = (pentries-> piid == null); if (bblind || Inlineisequalguid (* (Pentries-> PIID), IID) ) {// _ATL_SIMPLEMAPENTRY indicates a simple interface if (Pentries-> PFUNC == _ATL_SIMPLEMAPENTRY) // offset {atlassert (! BBLIND); IUNKNOWN * PUNK = (IUNKNOWN *) ((int) PTHIS PENTRIES-> DW); Punk-> addRef (); * pPVObject = punk; return s_ok;} Else // If not a simple interface, you need to perform the corresponding function {HRESULT HRES = Pentries-> Pfunc (PTHIS, IID, PPVObject, Pentries-> DW ); If (hres == s_ok || (! BBLIND && Failed (HRES))) Return HRES;}}} } Return E_NOINTERFACE;}} The logic of the function is very clear, only two points may not understand, one is (iUnknown *) ((int) pTHIS PENTRIES-> DW) What is the meaning, the other is Pentries-> PFUNC to the end Do something. The previous problem will tell in COM_ITERFACE_ENTRY2, and the latter problem will explain the different types of interfaces later. The meal is always a bit to eat, huh, huh. Now we just need to care about how our iMyObject is found. Look at its macro We will unwind the com_interface_entry (iMyObject) to follow:

{& _ATL_IIDOF (IMyObject), // obtain the IID value offsetofclass IMyObject (IMyObject, CMyObject), // offset defined _ATL_SIMPLEMAPENTRY}, // indicates that the interface is the same for a simple offsetofclass (IMyObject, CMyObject) we will leave to the next Tell again. According to this structure, we can easily get the IMYOBJECT interface pointer. 0: OK, IT is over. Return it in turn. In fact, the process of this query also happened in the call sequence just in the call, and there is a similar process when querying the iClassFactory interface, but it is still a separately, just to take a look at the typical situation, huh, huh. Second, COM_ITERFACE_ENTRY2 (X, X2) ATL is a multi-inheritance method to implement components, but if there is a number of branches in inherited trees to implement the same interface, when querying this interface, you need to know which branch returns 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 is a dual interface public IDispatchImpl , // IOuter2 also a dual interface public .. . {public: COuter () {} ... BEGIN_COM_MAP (COuter) COM_INTERFACE_ENTRY2 (IDispatch, IOuter2), // inherited exposed IOuter2 route, COM_INTERFACE_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 ((_ CommapClass *)) - 8, / _tl_simpentry}, // indicates that a simple interface is now (DWORD) (DWord) x *) (x2 *) (_ CommapClass *) 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, representing 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 *) (b5 *) (b *) 8); // dw = 0x10 dw = (dword) ((b2 *) (b5 *) (b *) 8) - 8 ; // dw = 8} class B will retain four virtual function table pointers 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 the IdispatchImpl template class from class T, ...> template class from class T, ...., IOUTER1, IOUTER2 It is a double interface, which is classified from idispatch, so you can have two branches, which is also a diamond structure, so according to our example, this offset value should 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 component's IUNKNOWN pointer 2--5: These code is now very familiar We only need to look at 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. Third, com_interface_entry_tear_off (IID, X) The purpose of using this macro is to implement some of the very few interfaces in a separate component, only this component is created only when querying this interface, and when its reference If the count is reduced to 0, it will be released. We know that the components in the ATL are implemented by multiple inheritance, each inherit an interface, will make more virtual function table pointers in the memory block allocated, and this macro can save this virtual for each component. Function table pointer comes (a pointer 4 bytes, it seems to be not much, huh, huh) Let's look at its typical usage:

class CTearOff1: // class is designed to implement the public IDispatchImpl divided interfaces ITearOff1 , public CComTearOffObjectBase // external object {public: CTearOff1 () {} ~ CTearOff1 () {} BEGIN_COM_MAP (CTearOff1) COM_INTERFACE_ENTRY (ITearOff1) END_COM_MAP () HRESULT STDMETHODCALLTYPE get_Name (BSTR * pbstrName) {* pbstrName = :: SysAllocString (L "ITearOff1"); return S_OK;}}; class COuter: public ..... // we really want to achieve Components {public: ......... Begin_COM_MAP (COUTER) ......... COM_ITERFACE_ENTRY_TEAR_OFF (IID_ITEAROFF1, CTEAROFF1) End_com_map () .......... .}; CTEAROFF1 implements the Tear-OFF interface ITEAROFF1, and the implementation method is not different from other components. The only difference is that it inherits from CComTearOffObjectBase, CComTearOffObjectBase defined as follows: template class CComTearOffObjectBase: public CComObjectRootEx {public: typedef Owner _OwnerClass; CComObject * m_pOwner; CComTearOffObjectBase () {m_pOwner = NULL;}}; We also saw a class CComobject we are familiar with, which is the real generation class of the component. From the above definition, you can know that the CCOMTEAROFFOBJECTBASE main feature is to include a pointer to an external object (here is our component CComObject). Its features will be seen later. We continue to track our execution process with our old way. Suppose Pouter is the IOUTER interface pointer of the components we have gained. Perform Pouter-> Queryinterface (IID_ITEAROFF1, (Void **) & PTEAR1); function stack 1:

7.CTEAROFF1 :: _ InternalQueryinterface (...) 6.atl :: ccominternalCreator > :: createinstance (...) 5.atl :: ccomobjectRootbase :: _ creator (...) 4. Atl :: atlinternalQueryinterface (...) 3.atl :: ccomobjectrootbase :: inloads :: _ infruit qerface (...) 1. COUTERFACE (...) 1. COUTER> :: queryinterface (... ) Explanation: 1--4: These codes have been encountered many times, we still concentrate to see core code: atlinline atlapi atlinternalQueryinterface (void * pthis, const _tl_intmap_entry * pentries, refiid iid, void ** ppvobject) {/ / ........ while! = Null) {bool bblind = (pentries-> piid == null); if (bblind || Inlineisequalguid (* (Pentries-> PIID), IID)) {if (pentries-> pfunc == _tl_simplemapentry) // offset {// is a simple interface, ....} else // actual function call {hResult hres = pentries-> pfunc (PTHIS, IID, PPVObject, Pentries-> dw); if (hres = = S_OK || (! BBLIND && Failed (HRES))) Return HRES;}}}}}}}} Return E_NOINTERFACE;} When ITEROFF1 is found in the interface mapping array in the COUTER, because it is not a simple interface, so to perform Pentries- > pfunc (....). Let's take a look at the definition of com_interface_entry_tear_off:

#define com_interface_entry_tear_off (IID, X) / {& IID, / (DWORD) & _ CComcreatorData > /> :: data, / _creator}, I don't understand, continue our route 5: original _ Creator is a static member function of CComobjectRootBase, which is a base class of the COUTER, so you can write this way without compiling an error. Take a look at its implementation:

static HRESULT WINAPI _Creator (void * pv, REFIID iid, void ** ppvObject, DWORD) {_ATL_CREATORDATA * pcd = (_ATL_CREATORDATA *) dw; return pcd-> pFunc (pv, iid, ppvObject);} struct _ATL_CREATORDATA {_ATL_CREATORFUNC * pFunc ;}; typedef HRESULT (WINAPI _ATL_CREATORFUNC) (void * pv, REFIID riid, LPVOID * ppv); template _ATL_CREATORDATA _CComCreatorData :: data = {Creator :: CreateInstance}; source code is listed, Needless Said that everyone can understand. Continue routing 6: About a large circle, now we call should be CCMinternalCreator <...> :: createinstance template class ccominternalcreator {public: static hResult WinApi CreateInstance (Void * Pv, Refiid Riid, lpvoid * PPV) {Atlassert (* ppv == null); HRESULT HRES = E_OUTOFMEMORY; T1 * P = NULL; ATLTRY (P = New T1 (PV)) IF (p! = null) {P-> setvoid (PV); P -> InternalFinalConstructAddref (); hres = p-> finalconstruct (); p-> infinalconstructrelease (); if (hres == s_ok) hres = p -> _ internalQueryinterface (riid, ppv); if (hres! = S_ok) delete P } Return hres;}}; like most Creator classes we have seen, it also has only one static CREATEINSTANCE function. Now we can finally create our split component, it is not ctearoff1, it is also a pack of packaging, is ccomtearoffObject! Now let's take a look at its constructor, what is going on:

CComTearOffObject (void * pv) {ATLASSERT (m_pOwner == NULL); m_pOwner = reinterpret_cast *> (pv); m_pOwner-> AddRef ();} Remember CTearOff1 is inherited from CComTearOffObjectBase do, This base class contains a member variable m_powner, which is now assigned a pointer to its external object. 7. Now, the component of this implementation segmentation interface is now created, and the rest of the work in ctearoff1 is already repeated labor, and will not be described again. Perform PTEAR1-> QueryInterface (ITEAROFF1, (Void **) & PTEAR2) A component that implements a split interface may contain multiple split interfaces, let's detect its query process. Functional Stack 2: 4 ............ 3.couter :: _ InternalQueryinterface (...) 2.atl :: ccomobject :: queryinterface (...) 1.atl :: CCOMTEAROFFOBJECT :: queryinterface (...) Explanation: 1:

STDMETHOD (Queryinterface) (REFIID IID, VOID ** PPVObject) {return m_powner-> queryinterface (iid, ppvobject);} Remember that the split component we created is CCOMTEAROFFOBJECT ? Now performing query operations is its member function. It is very simple, in fact it doesn't do anything, just hand it over to its external object (ie ccomobject ). Remember that m_powner assigns a value in the constructor. Do you feel bad now? Haha 2, 3: Sure enough, now you don't have to look back, the rest will be repeated everything we do in calling the first query operation. This process is simple, but it also implies a point: If you query a component of the partial interface, you will call a new instance! ! ! In the above example, the last result of PTEAR2 and PTEAR1 are different! ! This is obviously a waste! In the next section, we will introduce a macro that can solve this problem! Four .com_interface_entry_cached_tear_off (IID, X, PUNK) This macro and the top section of the COM_ITERFACE_ENTRY_TEAR_OFF macro are the most important differences, and new objects will not be created when querying other interfaces in the split object. Let's take a look at its typical usage:

class CTearOff2: public IDispatchImpl , public CComTearOffObjectBase {public: CTearOff2 () {} ~ CTearOff2 () {} BEGIN_COM_MAP (CTearOff2) COM_INTERFACE_ENTRY (ITearOff2) END_COM_MAP () HRESULT STDMETHODCALLTYPE get_Name (BSTR * pbstrName ) {* pbstrName = :: SysAllocString (L "ITearOff2"); return S_OK;}}; class COuter: public .... {public: BEGIN_COM_MAP (COuter) COM_INTERFACE_ENTRY_CACHED_TEAR_OFF (IID_ITearOff2, CTearOff2, m_pUnkTearOff2.p) .... .. END_COM_MAP () ccomptr m_punktearoff2; .....}; ctearoff2 implements the split interface ITEAROFF2, and its class definition is the same as the ctearoff1 seen by the previous section, no matter which partition interface, the implementation is the same Different places lies in couter. A member variable m_punktearoff2 is added to a parameter of the macro in the COUTER. We continue to track its internal execution with the old way, assuming that the POUTER is the components that have been obtained have interface IOUTER pointers. Perform Pouter-> Queryinterface (IID_ITEAROFF2, (Void **) & PTEAR1); function stack 1:

9.ctearoff2 :: _ internalQueryinterface (...) 8.atl :: ccomcachedtearoffObject :: queryinterface (...) 9.atl :: ccomcachedtearoffObject :: queryinterface (... ) 8.atl :: ccomcreator > :: createinstance () 7.atl :: ccomobjectRootBase :: _ cache (...) 5.couter :: _ cache (...) 5.atl :: : AtlinternalQueryinterface (...) 4.atl :: ccomobjectrootbase :: InternalQueryInterface (...) 3, couter :: _ internalQueryinterface (...) 2.atl :: ccomobject :: queryinterface (...) 1 .Ctestdlg :: onbutton1 () line 187 22 bytes explanation: 1: pouter-> queryinterface (IID_ITEAROFF2, (Void **) & PTEAR1); 2-5: This code saw many times, no need to talk, now The program is executed to HRESULT HRES = Pentries-> PFUNC (PTHIS, IID, PPVObject, Pentries-> DW); It seems that we have to see this macro definition to know what is the function of execution. #define COM_INTERFACE_ENTRY_CACHED_TEAR_OFF (iid, x, punk) / {& iid, / (DWORD) & _ CComCacheData >, / (DWORD) offsetof (_ComMapClass, punk) / gt;? :: data, / _Cache} The definition of the macro asked in the previous section is not very the same, or then track it first. 6: It turns out in Begin_COM_MAP also defined _cache functions:

Static HRESULT WINAPI _CACHE (Void * PV, Refiid IID, Void ** PPVObject, DWORD DW) / {/ ... hResult hres = ccomobjectrosbase :: _ cache (PV, IID, PPVObject, dw); / ... ...} / 7: Take a look at the source code of ccomobjectrootbase :: _ cache:

static HRESULT WINAPI _Cache (void * pv, REFIID iid, void ** ppvObject, DWORD dw) {HRESULT hRes = E_NOINTERFACE; _ATL_CACHEDATA * pcd = (_ATL_CACHEDATA *) dw; IUnknown ** pp = (IUnknown **) ((DWORD) PV PCD-> dwoffsetvar; if (* pp == null) hres = pcd-> PFUNC (PV, IID_IUNKNOWN, (VOID **) PP); if (* pp! = null) hres = (* pp) - > Queryinterface (IID, ppvobject); return hres;} The key to the problem is DW, DW is passed from Pentries-> DW. We have to look at the macro definition (dword) & _ ccomcachedta >, / (dword) offsetof (_CommapClass, punk) /> :: data, /.

Template _tl_cachedata _ccomcachedata :: data = {dwvar, ccomcreator we have seen its definition, it has only one member function

CreateInstance template class CComCachedTearOffObject: public IUnknown, public CComObjectRootEx {public:. Typedef contained _BaseClass; CComCachedTearOffObject (void * pv): m_contained (((contained :: _ OwnerClass *) pv) -> GetControllingUnknown ()) {m_contained.m_pOwner = reinterpret_cast *> (pv);} CComContainedObject m_contained;}; CComCachedTearOffObject is on a macro and the macro key speak different location Because it contains an object of CCOMCONTAINEDOBJECT. The role of this object is in the query. Let's take a look at Offsetof's definition: #define offsetof (s, m) (SIZE_T) & ((S *) 0) -> M) For DWORD OFFSTOF (_CommapClass, punk, Punk is _commapclass Offset value in the class. Let's take a look at _ATL_CACHEDDATA.

struct _ATL_CACHEDATA {DWORD dwOffsetVar; _ATL_CREATORFUNC * pFunc;}; typedef HRESULT (WINAPI _ATL_CREATORFUNC) (void * pv, REFIID riid, LPVOID * ppv); It is noted that from _Cached () function is passed in the parameter dw structure _ATL_CACHEDATA The variable, so it is known that the PV PCD-> DWOFFSetVar is the offset value of M_PunkTearoff2 defined in class couter, so iunknown ** PP is a pointer to a pointing pointer to M_PunkTearoff2. And PDC-> PFUNC () will call CCMCREATOR > :: CreateInstance 8: CCOMCREAROFFOBJECT <> object instance will be created below. The constructor is defined as follows: CComCachedTearOffObject (void * pv): m_contained (((contained :: _ OwnerClass *) pv) -> GetControllingUnknown ()) {ATLASSERT (m_contained.m_pOwner == NULL); m_contained.m_pOwner = reinterpret_cast *> (pv);} The contained is CTEAROFF2, Contained :: _ OwnerClass is the COUTER, you can see M_Contained saves the pointer of the external object. 9: After the object is created, the interface item ITEAROFF2 will be queried.

STDMETHOD (QueryInterface) ({// If it is iUnknown, ..., return IUNKNWON interface pointer else hres = m_contained._internalQueryinterface (IID, PPVObject); .....} Attention, here The query work is handed over to M_Contained, that is, a CCOMCONTAINEDOBJECT object. But now the IUNKNOWN pointer is now, don't forget, we also define an iunknown pointer in the couter, now query is it! ! 8: After a series of decall, back to _Cache (), now continue to check the ITEAROFF2 interface. Yes ITEAROFF2 is queried according to the iUnknown pointer we just queried. So once again entered atl :: ccomcachedtearoffObject :: queryinterface (...), but this will call M_Contained._internalQueryInterface (...). 9: Because the base class of ccomcontainedObject m_contained is ctearoff2, CTEAROFF2 :: _ InternalQueryInterface (...) is not particularly, only general query operations. Perform PTEAR1-> QueryInterface (ITEAROFF2, (Void **) & PTEAR2); Function Stack 2:

12.ATL :: AtlInternalQueryInterface (...) 11.ATL :: CComObjectRootBase :: InternalQueryInterface (...) 10.CTearOff2 :: _ InternalQueryInterface (...) 9.ATL :: CComCachedTearOffObject :: QueryInterface (. .) 8.Atl :: ccomobjectrootbase :: _ cache (...) 7.couter :: _ cache (...) 6.atl :: atlinternalQueryinterface (...) 5.atl :: ccomobjectRootbase :: interNalQueryInterface ........................................................................................................................................................................................................................................................... ... CCOMCONTAINEDOBJECT :: queryinterface (...) explanation: 1: The first step may make us confused, why do ccomcontainedObject :: queryinterface in the previous section, execute atl :: CCOMTEAROFOBJECT :: queryinterface (...), so we naturally guess, here should execute the function of ccomcachedtearoffObject. However, to look at the definition CComCachedTearOffObject: template class CComCachedTearOffObject: public IUnknown, public CComObjectRootEx {...}; no original CComCachedTearOffObject (CTearOff2 is herein) inherit from the class contained And CCOMTEAROFFOBJECT is inherited from ctearoff1! So the PTEAR1 we just got will not be the object of CCOMCHEDTEAROFFOBJECT. In fact, ccomcontainedObject is inherited from ctearoff2, and query the ITEAROFF2 interface in step 9 in the above function stack, the work is handed over to m_contained, this is a ccomcontainedObject object, so I actually query ITEAROFF2 pointing Is CCOMCONTAINEDOBJECT object. So now the execution will be ccomcontainedObject :: queryinterface (...)! ! !

STDMETHOD (QueryInterface) (REFIID iid, void ** ppvObject) {HRESULT hr = OuterQueryInterface (iid, ppvObject); if (! FAILED (hr) && _GetRawUnknown () = m_pOuterUnknown) hr = _InternalQueryInterface (iid, ppvObject); return hr; } //? m_pouterunknown 2: HRESULT OTERQUERYINTERFACE (Refiid IID, Void ** PPVObject) {return m_pouterunknoven-> queryinterface (IID, pPVObject);} Turn the query work to the external object, that is, COUTER. First, the functionality of the second step is the same as that of the previous section, is handed over to the external object to process, and the difference between the following 3-8: Query process in couter is different, we can Jump to step 8

Static HRESULT WINAPI _CACHE (Void * Pv, Refiid IID, Void ** PPVObject, DWORD DW) {.... if (* pp == null) hres = pcd-> pfunc (PV, IID_IUNKNOWN, (Void **) PP ); If (* pp! = Null) hres = (* pp) -> queryinterface (IID, ppvobject); return hres;} Remember that we define an IUNKNOWN pointer m_punktearoff2 in the COUTER, we query ITEAROFF2 in the first time When the interface, CTEAROFF2 object was created and a IUnknown pointer gave it. Now it works, it will judge in _Cache, if M_PunkTearoff2 does not equal, it indicates that CTEAROFF2 has created it will create it, and It is directly to use it to query the interface. 9: So now will call CCMCHEDTEAROFFOBJECT :: queryinterface (...), in step 9 in the previous function stack, we have seen this queryinterface (...) The code is handed over to M_Contained._internalQueryInterface (.), In fact, because the ccomcontainedObject is not defined in the ccomcontainedObject, there is no _internalQueryInterface (), so actually calling the function of the class it contains, ie ctearoff2 :: _InternalQueryinterface (...) 10-12: The following work is very simple, no longer described. Summary: com_interface_entry_cached_tear_off is a relatively troubled macro, which is different from the macro described in the previous section, is that the process of creating a split interface object is only used. If the object has been created, the next query is the interface of the object. No new split objects will be created again. To achieve this, it contains an IUNKNOWN pointer in the external object and queries this IUNKNOWN pointer when you first create a split object, so you can know if this pointer is empty, you can know if this split object has been created, thus Decide whether a new split object is created and the other interfaces within the split object are queried. It is particularly important to note that there are actually two objects being created, one is ccomcachedtearoffObject , the other is CCOMCONTAINEDOBJECT . And the second object is implemented inside the first object, and the real query work is also handed over to the second object. COUTER:: M_PUNKTEAROFF2 is an iUnknown pointer for an object. When you use it to query itemoff2, it is actually handed over to its internal object M_Contained, which can be seen very clear on the 8th, 9 steps. I finally finished this macro, I feel that this macro may be the most complicated in the ATL interface map macro, in fact it does not use the true function of CCOMCONTAINEDObject. I feel that this macro may not be so troublesome. Fives. COM_ITERFACE_ENTRY_AGGREGATE (IID, PUNK) This section will be introduced to the macro used to aggregate objects in the ATL. Please refer to other reference books for the concept of gathering objects. Let's take a look at this macro test now:

class CAgg: public IDispatchImpl , public ISupportErrorInfo, public CComObjectRoot, public CComCoClass {.....}; CAgg aggregate is a class, its implementation and the general assembly of ATL no difference just noted in its class definition not added DECLARE_NO_AGGREGATABLE.class COuter: public CChainBase, public IDispatchImpl , public CComCoClass {HRESULT FinalConstruct (); void FinalRelease (); BEGIN_COM_MAP (COuter ) COM_INTERFACE_ENTRY_AGGREGATE (IID_IAgg, m_pUnkAgg.p) END_COM_MAP () DECLARE_GET_CONTROLLING_UNKNOWN () CComPtr m_pUnkAgg;}; COuter polymerization assembly comprises CAgg, it contains several differences: (1) adding COM_INTERFACE_ENTRY_AGGREGATE (IID_IAgg, m_pUnkAgg. p) macro.

#define com_interface_entry_aggregate (IID, PUNK) / {& IID, / (DWORD) Offsetof (_CommapClass, punk), / _delegate}, offsetof We have seen in the previous section, can guess it is in the class position. That is, M_Punkagg is in the position of the COUTER. (2) Join the macro Declare_Get_Controlling_unkNown (), which is defined as:

#define declare_get_controlling_unknown () public: / virtual iUnknown * getControllingunkNown () {Return getUnknown ();} We don't have to continue to go abroad, only from literally, you can see that this function will return the component's iUnknown pointer. (3) Add a member variable in the COUTER: ccomptr m_punkagg; m_punkagg will be used to obtain an IUNKNOWN pointer to the aggregated component. (4) Heavy loaded FinalConstruct, FinalRelease

HRESULT COuter :: FinalConstruct () {IUnknown * pUnkOuter = GetControllingUnknown (); HRESULT hRes = CoCreateInstance (CLSID_CAgg, pUnkOuter, CLSCTX_ALL, IID_IUnknown, (void **) & m_pUnkAgg); return hRes;} void COuter :: FinalRelease () {m_pUnkAgg .Release (); .....} FinalConstruct will be called after creating component couter, so you will create a gathering component here. In principle, the aggregation component can be created only when needed, but can also be created along with the components that contain it. There is no special place for the creation of the aggregation components, just pay attention to it will query the IUnknown pointer and return to m_punkagg. External components will operate the components via the M_PUnkagg operation. Also note that Punkouter as a parameter of CocreateInstance, which will cause CCOMAGGGOBJECT object, which contains a CCOMCONTAINEDOBJECT containing objects. With ccomcachedtearoff <> in the previous section, ccomaggobject is not derived from COUTER, so the real component object is not a ccomaggobject object, but it is included in the ccomcontainedObject object. The same PUNKOUTER obtained will be the iUnknown pointer of ccomaggobject <>, and also calling its QueryInterface will turn to CCOMCONTAINEDOBJECT _InternalQueryInterface function (Oh, now I still guess, see me guess, right) running POUTER- > Queryinterface (IID_IAGG, (Void **) & PAGG1) Function Stack 1: 9.atl :: atlinternalQueryinterface (...) 8.atl :: ccomobjectRootbase :: InternalQueryinterface (...) 7.cagg :: _ internalQueryinterface (.. 6.atl :: ccomobjectbootbase :: _ delegate (...) 4.atl :: atlinternalQueryinterface (...) 3.atl :: ccomobjectRootBase :: CCOMOBJECTROOTBASE :: InternalQueryinterface (...) 2.couter :: _ InternalQueryInterface (...) 1.tl :: ccomobject :: queryinterface (...) Explanation: 1-5: These steps to call us have already seen It has been many times because _Delegate will be called in this macro definition, so CComObjectRootBase :: _ delegate (...).

Static HRESULT _DELEGATE (Void * PV, Refiid IID, Void ** PPVObject, DWORD DW) {HRESULT HRES = E_NOINTERFACE; IUNKNOWN * P = * (iUnknown **) ((DWORD) PV DW); if (p! = null HRES = P-> Queryinterface (IID, PPVObject); Return Hres;} The meaning of the second sentence we have seen in the previous section, the last result p = couter :: m_punkagg. 6: As we just According to the ccomaggobject :: queryinterface () stdmethod (queryinterface) (REFIID iID, void ** ppvobject) {// If the query is iUnknown, then .... else hres = m_contained._internalQueryinterface (IID) , PPVObject; return hres;} is also as expected, it will be handed over to it. (This code is seen in the previous section, huh, huh) 7-9: The same as the same Will be handed over to CAGG :: _ InternalQueryInterface (...), and the remaining work will be done by CAGG. The final returned pointer is actually the interface pointer of the CCOMCONTAINEDOBJECT component. Run PAGG1-> QueryInterface (IID_IAGG, (Void **) & PAGG2) Function Stack 2:

9.cagg :: _ InternalQueryinterface (...) 8.atl :: ccomaggobject :: queryinterface (...) 7.atl :: ccomobjectRootBase :: _ DELEGATE (...) 6.atl :: atlinternalQueryinterface ........................................................................................... ... CComObjectRootBase :: OuterQueryinterface (...) 1.atl :: ccomcontainedObject :: queryinterface (...) Explanation: 1-9: Browse the entire stack, with the stack of the previous section of us too similar, This is because it is used to include objects. The role of the object has gone a proxy, he first handed the query to the external object (step 1, 2), when the external object is found to query the interface of the aggregated component (IAGG), The query is returned to the pointer to the aggregated component (M_PUnkagg, step 7, pay attention to this is not a real aggregation component), M_PUnkagg will hand over the query to the included object (step 8), including the object and give the query That's that the external object finds that the external object is found to query the interface of the external component, it is very simple, and directly queries. This prevents inconsistencies of external components and aggregated component query operations. Hey, the truth is really troublesome, but it is ok, it is very similar to the macro of the previous section. Related source code can be found in the previous section. Six, com_interface_entry_aggregate_blind Part ITL RROR COMMAP Previous We talked about com_interface_entry_aggregate, the macro to be introduced in this section is very similar. #define com_interface_entry_aggregate_blind (punk) / {null, / (dword) Offsetof (_CommapClass, punk), / _DELEGATE}, it can be seen from the definition, it is the only difference between its introduction to the previous section is that it does not specify the interface ID! ! So the first item in its definition is NULL. This macro is used in the same way as our com_interface_entry_aggregate. You can refer to the previous section and the ATL routine COMMAP. Let's take a look at the related code in atlinternalQueryInterface ().

ATLINLINE ATLAPI AtlInternalQueryInterface (void * pThis, const _ATL_INTMAP_ENTRY * pEntries, REFIID iid, void ** ppvObject) {// If IUnknown, .... 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;} else {hresult hres = pentries-> pfunc (pthis, iid, ppvobject, pentries-> DW); if (hres == s_ok || (! BBLIND && Failed (HRES))) Return HRES;}}}}}}} Return E_NOINTERFACE;} Note Variable BBLIND: BOOL BBLIND = (Pentries-> PIID == NULL); If the interface ID is not specified, continue to perform the following operations, it can be seen that even if the IID we need is not _delegate. From the above, this macro is suitable for a gathering group There are multiple interfaces, so as long as it is queried to the interface of this aggregation component, it will enter the _dlegate function. But special attention is the location of this macro! ! For example, if it is such a order: begin_com_map com_interface_entry_aggregate_blind (m_punkaggblind.p) COM_INTERFACE_ENTRY (IOUTER) End_com_map When Query IOUTER, it will be wrong! ! ! Seven, com_interface_entry_autoaggregate (IID, PUNK, CLSID) First look at this macro definition:

#define COM_INTERFACE_ENTRY_AUTOAGGREGATE (iid, punk, clsid) / {& iid, / (DWORD) & _ CComCacheData , / (DWORD) offsetof (_ComMapClass, punk) /> :: data, / _Cache}, look Seeing its typical usage:

class CAutoAgg: public IDispatchImpl , public ISupportErrorInfo, public CComObjectRoot, public CComCoClass {......}; comp no two general components.

class COuter: public CChainBase, public IDispatchImpl, public CComCoClass {BEGIN_COM_MAP (COuter) COM_INTERFACE_ENTRY_AUTOAGGREGATE (IID_IAutoAgg, m_pUnkAutoAgg.p, CLSID_CAutoAgg) END_COM_MAP () CComPtr m_pUnkAutoAgg;}; macro COM_INTERFACE_ENTRY_AGGREGRATE (_) different, COuter without creating the aggregation component in FinalConstruct . External components will automatically create aggregated components! ! ! 1.

Template _TL_CACHEDATA _CCOMCACHEDATA :: DATA = {DWVAR, CREATOR :: CREAT Einstance}; 2.

static HRESULT WINAPI _Cache (void * pv, REFIID iid, void ** ppvObject, DWORD dw) {HRESULT hRes = E_NOINTERFACE; _ATL_CACHEDATA * pcd = (_ATL_CACHEDATA *) dw; IUnknown ** pp = (IUnknown **) ((DWORD) PV PCD-> dwoffsetvar; if (* pp == null) hres = pcd-> PFUNC (PV, IID_IUNKNOWN, (VOID **) PP); if (* pp! = null) hres = (* pp) - > QueryInterface (IID, PPVObject); Return HRES;} 3.

template class CComAggregateCreator {public: static HRESULT WINAPI CreateInstance (void * pv, REFIID / * riid * /, LPVOID * ppv) {ATLASSERT (* ppv == NULL); ATLASSERT (pv =! NULL); T * p = (t *) PV; Return CocreateInstance (* PCLSID, P-> getControllingunkNown () (), clsctx_all, iid_iunknown, ppv);}}; because _cache, _ccomcachedata, ccomaggregatecreator these classes and functions we I have seen it in front or have seen it, so I will not talk more. In short, we can see that if m_punkautoagg.p does not directly query the flight, create a gathering component. Compared with the macro COM_INTERFACE_ENTRY_AGGREGATE, this macro seems to be better, only when it is needed, it is easier to use. Eight, COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND (punk, clsid) to see if it is defined: #define COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND (punk, clsid) / {NULL, / (DWORD) & _ CComCacheData , / (DWORD) offsetof (_ComMapClass, punk) /> :: data, / _cache}, huh, this macro combines the characteristics of com_interface_entry_autoaggregate () and com_interface_entry_aggregate_blind (), which can automatically create multiple interfaces in the aggregated component. No longer! ! Nine, com_interface_entry_chain (classname) Part of the ATL routine COMMAP first look at its definition:

#define com_interface_entry_chain (classname) / {null, / (dword) & _ ccomchaindata :: data, / _chain}, typical usage:

class CChain: public IDispatchImpl , public ISupportErrorInfo, public CComObjectRoot, public CComCoClass {........}; it is no different from the general assembly.

Class couter: public cchain, .... {begin_com_map (couter) ... com_interface_entry_chain (cchain) end_com_map ()}; We have been very familiar with the process of queries, you can take a look at _chain's function. _Chain () member function is CComObjectRootBase: static HRESULT WINAPI _Chain (void * pv, REFIID iid, void ** ppvObject, DWORD dw) {_ATL_CHAINDATA * pcd = (_ATL_CHAINDATA *) dw; void * p = (void *) (( DWORD) pv pcd-> dwOffset); return InternalQueryInterface (p, pcd-> pFunc (), iid, ppvObject);} struct _ATL_CHAINDATA {DWORD dwOffset; const _ATL_INTMAP_ENTRY * (WINAPI * pFunc) ();}; we look See the DW section in the macro:

Template _ccomchaindata :: data = {offsetofclass (base, derived), base :: _ getentries}; basically we have already understood what is going on, void * p will get The base class pointer, INTERALALQUERYINTERFACE we are very familiar, _Chain passes the base class's pointer and the base class interface map macro to it, and actually querying the interface of the base class! ! ! Under normal circumstances, this macro is placed between BeGin_COM_MAP and END_COM_MAP, which means that only the interface of the Father class is only when the interface is not checked in the current class. However, it is often placed in the first place. At this time, I will first check the father-class interface. Only when the parent class does not implement this interface. In ATL components are implemented in multiple inheritance, ATL defines a number of categories to implement some common interfaces, which are often used as the base class of components, so this macro is used in a large number of use. All of the important macros have been said, the rest is very simple macro. Oh, still put them all, good start, good end, Ten, com_interface_entry_iid (IID, X)

#define COM_ITERFACE_ENTRY_ID (IID, X) / {& IID, / OffsetOfClass (X, _COMMAPCLASS), / _TL_SIMPLEMAPENTRY}, 11, COM_ITERFACE_ENTRY2_IID (IID, X, X2)

#DEfine COM_ITERFACE_ENTRY2_IID (IID, X, X2) / {& IID, / (DWORD) ((_) (_ CommapClass *) 8)) - 8, / _TL_SIMPLEMAPENTRY}, from the definition, these two macros Compared with COM_ITERFACE_ENTRY () and com_interface_entry2 (), it is just a "IID". There is no other benefit, but it is clearly pointed out by the user to the interface IID, without the system to convert according to the interface name. 12. COM_ITERFACE_ENTRY_FUNC (IID, DW, FUNC)

#DEfine COM_ITERFACE_ENTRY_FUNC (IID, DW, FUNC) / {& IID, / DW, / FUNC}, remember the code in atlinternalQueryInterface ()? If you find the interface we have to find in the interface mapping table, and this interface is not _ATL_SIMPLEENTRY, the specified function in the macro definition is executed. This macro gives us a function of writing a handset. This function must be as defined in: HRESULT WINAPI FUNC (Void * PV, Refiid Riid, LPVOID * PPV, DWORD DW); When the AtlinternalQueryInterface calls FUNC, the relevant information will be transferred. PV is the pointer of the class object, the RIID is the interface to query, the PPV is the interface pointer to which the query is obtained, DW is the parameters specified in the macro definition. Also if you do not intend to return the interface pointer in the function, PPV should be assigned to null and return S_FALSE or E_NOIInterface. Returns S_FALSE just continues to find it, if you return to E_NOIInterface, the query will be terminated. If you return the interface pointer, you should return to S_ok. 13, com_interface_entry_func_blind (dw, func) #define com_interface_entry_func_blind (dw, func) / {null, / dw, / func}, as for the _blind type feature, you can see the previous section. 14, com_interface_entry_nointerface (x)

#define COM_INTERFACE_ENTRY_NOINTERFACE (x) / {& _ATL_IIDOF (x), / NULL, / _NoInterface}, _NoInterface is CComObjectRootBase member function to see if it is defined: static HRESULT WINAPI _NoInterface (...) {return E_NOINTERFACE;} it is only the original Returns E_NOINTERFACE and terminates the query. Haha, it seems that I don't want someone to find this interface! ! ! Fifteen, com_interface_entry_break (x)

#define com_interface_entry_break (x) / {& _tl_iidof (x), / null, / _break}, _break is also a member function of CComobjectRootBase, see its definition: static hResult winapi _break (...) {IID; _TLDUMPIID (IID, _T ("Break Due To Qi for Interface", S_OK); debugbreak (); returnous s_false;} If you check this interface, debugbreak () will be called, and return S_FALSE, continue to query. DEBUGBREAK () What is the effect of everyone try it, must be familiar, huh, huh. At this time, all fifteen interface maps have all been finished, 唉, it is not easy, especially the previous macro tracks. Because of the limitations of text, many things are not easy to express. Some calls are also what I call it myself, may be different from the habits of others. No way, everyone will be just, huh, huh.

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

New Post(0)