Developers Workshop to COM and ATL 3.0

 < Free Open Study > 


To wrap up this chapter, we have a handful of remaining ATL COM map macros to examine. First, we have COM_INTERFACE_ENTRY_BREAK. This macro may be placed in your COM map to force the debugger to break if the interface in question is queried for by a client. Here is the expansion:

// Force a break in the debugger if interface 'x' is ever queried for. #define COM_INTERFACE_ENTRY_BREAK(x)\ {&_ATL_IIDOF(x), \ NULL, \ _Break},

As you can see, the final field of the _ATL_INTMAP_ENTRY structure is pointed to the _Break() helper function provided by CComObjectRootBase. This method issues the break by calling DebugBreak():

// Recall CComObjectRootBase supplies a small batch of helper functions used // by various COM map macros. static HRESULT WINAPI _Break(void* /* pv */, REFIID iid, void** /* ppvObject */, DWORD /* dw */) { iid; _ATLDUMPIID(iid, _T("Break due to QI for interface "), S_OK); DebugBreak(); return S_FALSE; }

Next up is the COM_INTERFACE_ENTRY_NOINTERFACE macro. This macro exists to explicitly tell the world your coclass does not support the interface in question:

// When you need to bar an interface from working in your coclass... #define COM_INTERFACE_ENTRY_NOINTERFACE(x)\ {&_ATL_IIDOF(x), \ NULL, \ _NoInterface},

_NoInterface() which is also provided by CComObjectRootBase, simply returns the appropriate HRESULT, E_NOINTERFACE:

// Issue an HRESULT. static HRESULT WINAPI _NoInterface(void* /* pv */, REFIID /* iid */, void** /* ppvObject */, DWORD /* dw */) { return E_NOINTERFACE; }

Rolling Your Own Mapping Functions

The next two ATL COM map macros are used when you wish to trigger the invocation of a specific custom function, as a result of a QueryInterface() call. The COM_INTER- FACE_ENTRY_FUNC macro may be used to call a custom method in your coclass for a specific IID. COM_INTERFACE_ENTRY_FUNC_BLIND may be used to call an alternative function for any IID. Both macros take a DWORD parameter, which in turn is placed in the second field of the _ATL_INTMAP_ENTRY structure. Here are the expansions:

// These macros allow you to create a custom function which should be called in // response to a given interface (or all interfaces). #define COM_INTERFACE_ENTRY_FUNC(iid, dw, func)\ {&iid, \ dw, \ func}, #define COM_INTERFACE_ENTRY_FUNC_BLIND(dw, func)\ {NULL, \ dw, \ func},

Notice that the final field of the _ATL_INTMAP_ENTRY structure is func. This method is actually the name of the custom method. The name can be anything at all, but must have the following signature:

// The parameters of your custom method must be as so... HRESULT WINAPI CallWhenQIed(void* pv, REFIID riid, LPVOID* ppv, DWORD dw) { return S_OK; // Signals that the custom function has found the interface. }

The parameters to this custom global method are the class object (*pv), the GUID of the interface (REFIID), a place to hold the vPtr (void**), and finally a DWORD which is the value of the second parameter of the FUNC macros. This allows you to send in any specific information to the custom function. Note that the custom function returns an HRESULT. If the custom function has found (and set) the interface pointer, return S_OK. If not, return S_FALSE to return to the COM map.

For example, assume you have an ATL coclass that supports some number of interfaces. Beyond handing out vPtrs for each, you might wish to call a custom helper function during each client-side query. This would entail a custom global function and the COM_INTERFACE_ENTRY_FUNC_BLIND macro:

// This method is called with every QI. // Returning S_FALSE tells the function to return to the COM map to find the // interface. S_OK signals that the custom function has already set the void**. HRESULT WINAPI CallWhenQIed(void* pv, REFIID riid, LPVOID* ppv, DWORD dw) { ATLTRACE("Someone wants to get an interface\n"); ATLTRACE("DWORD param is %d\n", dw); return S_FALSE; } // Just for a simple example, 888 is sent in as DWORD data. This is realized as the 4th // parameter of our custom function. BEGIN_COM_MAP(CFinalMacros) COM_INTERFACE_ENTRY(IFinalMacros) COM_INTERFACE_ENTRY(IOther) COM_INTERFACE_ENTRY(IBaaz) COM_INTERFACE_ENTRY_FUNC_BLIND(888, CallWhenQIed) END_COM_MAP()

So onto the big question: Why? The ATL _FUNC macros exist to allow developers to extend the functionality of the COM map should the functionality of the core COM map macros fall short of your needs.

The Last Stand: COM_INTERFACE_ENTRY_CHAIN

Last of all, we have the COM_INTERFACE_ENTRY_CHAIN macro. This macro is used to link your derived ATL coclass into the COM map of a base ATL coclass. In other words, this macro allows you to "inherit" the COM map of another class and include it as part of your own. Here is the definition:

// If you derive an ATL class from another ATL base class, you may chain // each map together. This allows your subclass to direct QueryInterface() calls // to the base class. #define COM_INTERFACE_ENTRY_CHAIN(classname)\ {NULL,\ (DWORD)&_CComChainData<classname, _ComMapClass>::data,\ _Chain},

The _Chain() helper function is used to calculate the location of the parent class and call InternalQueryInterface() accordingly:

// Another helper function supplied by 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); }

That's a wrap, folks. This chapter dove headlong into the ATL COM map macros. In the next chapter, we will dig into how the ATL framework supports component housing, and learn a number of advanced COM topics along the way such as component categories, object models, and custom registration à la ATL.


 < Free Open Study > 

Категории