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
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
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
template
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
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
#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
5.atl :: atlinternalQueryinterface (...) 4.atl :: ccomobjectrootbase :: InternalQueryInterface (...) 3.cmyObject :: _ internalQueryinterface (...) 2.atl :: ccomobject
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
7.CTEAROFF1 :: _ InternalQueryinterface (...) 6.atl :: ccominternalCreator
#define com_interface_entry_tear_off (IID, X) / {& IID, / (DWORD) & _ CComcreatorData ccominternalcreator
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
CComTearOffObject (void * pv) {ATLASSERT (m_pOwner == NULL); m_pOwner = reinterpret_cast
STDMETHOD (Queryinterface) (REFIID IID, VOID ** PPVObject) {return m_powner-> queryinterface (iid, ppvobject);} Remember that the split component we created is CCOMTEAROFFOBJECT
class CTearOff2: public IDispatchImpl
9.ctearoff2 :: _ internalQueryinterface (...) 8.atl :: ccomcachedtearoffObject
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 ccomcreator
Template
CreateInstance template
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
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
12.ATL :: AtlInternalQueryInterface (...) 11.ATL :: CComObjectRootBase :: InternalQueryInterface (...) 10.CTearOff2 :: _ InternalQueryInterface (...) 9.ATL :: CComCachedTearOffObject
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
class CAgg: public IDispatchImpl
#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
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
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
9.cagg :: _ InternalQueryinterface (...) 8.atl :: ccomaggobject
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 CComAggregateCreator <_ComMapClass, & clsid>, / (DWORD) offsetof (_ComMapClass, punk) /> :: data, / _Cache}, look Seeing its typical usage:
class CAutoAgg: public IDispatchImpl
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
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
#define com_interface_entry_chain (classname) / {null, / (dword) & _ ccomchaindata
class CChain: public IDispatchImpl
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
#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.