ATL interface mapping macro detailed [4]

zhaozj2021-02-16  90

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 too much, huh, huh)

Let's take a 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 is inherited from ccomtearoffObjectBase, and CCOMTEAROFFOBJECTBASE is 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 look at the core code:

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 {// is simple interface, ... Else // Actual Function Call {HRESUNC (PTHIS, IID, PPVObject, Pentries-> DW); if (HRES == S_OK || (! BBLIND && Failed (HRES))) Return HRES; }}}} Return E_NOINTERFACE;}

When ITEAROFF1 is found in the interface mapping array in the interface, because it is not a simple interface, it is necessary to perform Pentries-> PFUNC (....).

Let's take a look at the definition of com_interface_entry_tear_off:

#DEFINE COM_ITERFACE_ENTRY_TEAR_OFF (IID, X) / {& IID, / (DWORD) & _ CCOMCREATORDATA > /> :: data, / _creator},

I don't quite understand, or continue our route.

5: The 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: Wrought a big circle, now we call it should be

CComInternalCreator <...> :: 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-> infinalconstructaddref (); hres = p-> firmconstruct (); P- > INTERNALCONSTRUCTRELEASE (); 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) {atlastert (m_powner == null); m_powner = reinterpret_cast *> (pv); m_powner-> addref ();

Remember that CTEAROFF1 is inherited from CCOMTEAROFFOBJECTBASE, this base class contains a member variable m_powner, now it is assigned to point 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.

Function Stack 2:

4 ............ 3.couter :: _ internalQueryinterface (...) 2.atl :: ccomobject :: queryinterface (...) 1.atl :: ccomtearoffObject < CTEAROFF1> :: queryinterface (...)

Explanation:

1:

STDMETHOD (REFIID IID, VOID ** PPVObject) {Return M_Powner-> Queryinterface (IID, PPVObject);

Remember the segmentation 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? Ha ha

2, 3: Sure enough, now no longer have to look down, 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!

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

New Post(0)