Interface Map Chaining

C++ programmers are accustomed to using inheritance of implementation for reuse. For example, we reuse the implementation provided in CComObjectRootEx as well as the various ATL implementation classes (such as IDispatchImpl) tHRough inheritance. For each implementation class used, one or more corresponding entries must be made in the interface map. However, what about deriving from a class that already provides an interface map? For example

class CBigBeachBall :   public CBeachBall,   public IBigObject { public: BEGIN_COM_MAP(CBigBeachBall)   COM_INTERFACE_ENTRY(IBigObject)   // All entries from CBeachBall base? END_COM_MAP() ... }; 


When inheriting from a base class that provides its own interface map, we want to avoid duplicating all the entries in the deriving class's interface map. The reason is maintenance. If the base class decides to change how it supports an interface or wants to add or remove support for an interface, we've got to change the deriving classes, too. It would be much nicer to "inherit" the interface map along with the interface implementations. That's what COM_INTERFACE_ENTRY_CHAIN does:

#define COM_INTERFACE_ENTRY_CHAIN(classname) \           { NULL, (DWORD_PTR)&ATL::_CComChainData<classname, \     _ComMapClass>::data, _Chain },                     

The _CComChainData template simply fills the dw member of the interface entry with a pointer to the base class's interface map so that the _Chain function can walk that list when evaluating a query request:

static HRESULT WINAPI                                             CComObjectRootBase::_Chain(void* pv, REFIID iid,                    void** ppvObject, DWORD dw) {                                     _ATL_CHAINDATA* pcd = (_ATL_CHAINDATA*)dw;                        void* p = (void*)((DWORD_PTR)pv + pcd->dwOffset);                 return InternalQueryInterface(p, pcd->pFunc(), iid, ppvObject); } 

If the _Chain function returns a failurefor example, if the base class doesn't support the requested interfacethe search continues with the next entry in the table:

class CBigBadBeachBall :   public CBeachBall,   public IBigObject,   public IBadObject { public: BEGIN_COM_MAP(CBigBadBeachBall)   COM_INTERFACE_ENTRY(IBigObject)   COM_INTERFACE_ENTRY_CHAIN(CBeachBall)   COM_INTERFACE_ENTRY(IBadObject) END_COM_MAP() ... }; 

It might seem natural to put the chaining entries first in the interface map. However, remember that the first entry must be a simple entry, so you must put at least one of the derived class's interfaces first. If the derived class has no additional interfaces, use IUnknown as the first entry:

class CBetterBeachBall :   public CBeachBall { public: BEGIN_COM_MAP(CBetterBeachBall)   COM_INTERFACE_ENTRY(IUnknown)   COM_INTERFACE_ENTRY_CHAIN(CBeachBall) END_COM_MAP() ... }; 

ATL Internals. Working with ATL 8
ATL Internals: Working with ATL 8 (2nd Edition)
ISBN: 0321159624
EAN: 2147483647
Year: 2004
Pages: 172

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: