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
CTearoff2 implements the split interface ITEAROFF2. Its class definition is the same as the CTearoff1 seen by the previous section, regardless of which division interface 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
Explanation:
1: Pouter-> QueryInterface (IID_ITEAROFF2, (Void **) & PTEAR1);
2-5: This code saw many times, no need to talk, now the program is executed
HRESULT HRES = Pentries-> PFUNC (PTHIS, IID, PPVObject, Pentries-> DW); It seems that we have to see this macro definition to know what PFUNC is performing.
#define com_interface_entry_cached_tear_off (IID, X, PUNK) /
{& IID, /
(DWORD) & _ CCOMCACHEDATA
CCMCREATOR
(DWORD) OFFSTOF (_CommapClass, PUNK) /
? gt ;:: data, /
_Cache},
The definition of the macro we came in the previous section is not too 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 = CComobject HRES = CComobjectRootBase :: _ Cache (PV, IID, PPVObject, DW); /
......
} /
7: Take a look at CComobjectRootBase :: _ cache source code:
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) & _ CCMCAMCHEDATA ccomcreator
What means.
Template
_TL_CacheData _ccomcachedata
CCOMCREATOR We have seen its definition in front, it only has a member function
CreateInstance template
Let's take a look at the definition of Offsetof:
#define offsetof (s, m) (SIZE_T) & ((S *) 0) -> M)
For DWORD OFFSTOF (_CommapClass, punk) is the offset value in the _CommapClass class.
Let's take a look at _ATL_CACHEDDATA.
Struct _tl_cachedata {DWORD DWOFFSETVAR; _TL_CREATORFUNC * PFUNC;}; typedef hResult (WinAPI _TL_CREATORFUNC) (Void * PV, REFIID RIID, LPVOID * PPV);
It should be noted that the parameter DW from the _Cached () function is the variable of the _TL_CACHEDATA structure, so it is known that the PV PCD-> DWOFFSetVar is the offset value of the m_punktearoff2 defined in class couter, So IUNKNOWN ** PP is a pointer pointing to a pointer to M_PUNKTEAROFF2.
PDC-> PFUNC () will call CCOMCREATOR
8: CCOMCREATOR :: CreateInstance will create a CCOMCACHEDTEAROFFOBJECT <> object instance. Its 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
Here Contained is CTEAROFF2, Contained :: _ OwnerClass is the couter, you can see M_Contained to save 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
9: Because the base class of ccomcontainedObject m_contained is ctearoff2, CTEAROFF2 :: _ InternalQueryinterface will be called (...)
The rest of the operation is nothing special, and 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
Explanation:
1: The first step may make us confused, why do ccomcontainedObject :: queryinterface
In the previous section, executing atl :: ccomtearoffObject
template
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 OUTERQUERYINTERFACE (REFIID IID, VOID ** PPVObject) {Return M_PouterunkNown-> 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;}
I still remember that we define an iUTerOwn pointer m_punktearoff2, we created a CTEAROFF2 object when I first queryed the ITEAROFF2 interface, and queried an IUnknown pointer to it. Now it works, in _Cache Judgment If m_punktearoff2 does not equal empty, it indicates that CTEAROFF2 has been created, but it is directly used to use it to query the interface.
9: So now call CCOMCachedTearoffObject
to sum up:
COM_TERFACE_ENTRY_CACHED_TEAR_OFF is a relatively troublesome macro, which is different from the macro described in the previous section is that the process of creating a split interface object is only once. If the object has been created, the next query is not the interface of the object. Create a new split object. 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
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.