ATL and QueryInterface

[Previous] [Next]

Before digging too deeply into how ATL handles class composition, we need to quickly review how ATL manages QueryInterface. Take a look at the code below, which shows a basic ATL class with some interfaces. The CSomeATLObj class has all the parts that make this a living, breathing ATL object, including an implementation of IUnknown optimized on the threading model. In addition to providing reference counting, ATL's IUnknown implementation provides an implementation of QueryInterface inside the class CComObjectRootBase.

For QueryInterface to work properly, an ATL object needs an interface map. Whenever someone calls QueryInterface on an ATL COM object, ATL goes to the interface map to find the interface. ATL maintains object identity because all requests for interfaces filter through this interface map. For example, the following code shows some simple COM classes with two interfaces:

 class ATL_NO_VTABLE CSomeATLObj :      public CComObjectRootEx<CComSingleThreadModel>,     public CComCoClass<CSomeATLObj, &CLSID_SomeATLObj>,     public ISomeATLObj,     public IAnotherInterface { public:     CSomeATLObj()     {     } DECLARE_REGISTRY_RESOURCEID(IDR_SOMEATLOBJ) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CSomeATLObj)     COM_INTERFACE_ENTRY(ISomeATLObj)     COM_INTERFACE_ENTRY(IAnotherInterface) END_COM_MAP() // ISomeATLObj public:     STDMETHOD(AnotherMethod)(BSTR bstr, BSTR* pBstrOut);     STDMETHOD(SomeMethod)(short x); // IAnotherInterface     STDMETHOD(Add)(SHORT x, SHORT y, LONG * pz)     {         if(pz == NULL)             return E_POINTER;         return E_NOTIMPL;     }     STDMETHOD(Subtract)(LONG x, LONG y, LONG * pz)     {         if(pz == NULL)             return E_POINTER;         return E_NOTIMPL;     } }; 

ATL's interface map is comprised of three kinds of macros you can see in this code: the BEGIN_COM_MAP macro, the END_COM_MAP macro, and the COM_INTERFACE_ENTRY macros. When clients ask an object for one of its interfaces via QueryInterface, ATL looks to this map to find the interface. The interface map is made up of _ATL_INTMAP_ENTRY structures. These structures essentially pair an interface ID (an IID) to a means of finding a COM interface.

The following code shows the structures that make up the interface map. You'll want to refer to them as we cover the various COM class composition techniques.

 typedef HRESULT (WINAPI _ATL_CREATORARGFUNC)(void* pv,      REFIID riid,      LPVOID* ppv,      DWORD dw); struct _ATL_INTMAP_ENTRY {     const IID* piid;       // The interface ID (IID)     DWORD dw;     _ATL_CREATORARGFUNC* pFunc; //NULL:end, 1:offset, n:ptr }; 

The _ATL_INTMAP_ENTRY includes the IID, a DWORD utility value, and either a constant (_ATL_SIMPLEMAPENTRY), a pointer to a function, or NULL. If pFunc is _ATL_SIMPLEMAPENTRY, ATL interprets the structure member dw to be an offset into the object. If pFunc is non-null (but not _ATL_SIMPLEMAPENTRY), the function it points to will be called, passing the pointer to the object as the first parameter and the utility member dw as the last parameter. If the last entry in the interface map has a null pFunc value, you've reached the end of the map. InternalQueryInterface returns E_NOINTERFACE for any interface not found in the map.

Inside Atl
Inside ATL (Programming Languages/C)
ISBN: 1572318589
EAN: 2147483647
Year: 1998
Pages: 127 © 2008-2017.
If you may any questions please contact us: