Chapter 6. Interface Maps

Chapter 4, “Objects in ATL,” discussed how ATL implements IUnknown, but it covered only AddRef and Release completely. This chapter takes a look first at the requirements that COM makes on an object’s implementation of QueryInterface and then at how ATL supports those requirements while still providing flexibility and extensibility.

Recall: COM Identity

From a client perspective, the rules of AddRef and Release are fairly stringent. Unless the client is careful about their use, objects can go away before expected or can stay around too long. However, the object is allowed to implement AddRef and Release in any number of ways, depending on how it wants to manage its own lifetime – for example, as a heap-based, stack-based, or cached object.

On the other hand, QueryInterface is easy to get right on the client side. Any client can ask an object if it supports any other functionality with a simple call. However, clients expect certain relationships between the interfaces on a COM object. These expectations form the laws of COM identity.

The Laws of COM Identity

The laws of COM identity say the following things about how an object must expose its interfaces via QueryInterface:

  • A client must be capable of getting directly to any interface implemented by the object via QueryInterface.

  • An object’s interfaces must be static throughout its lifetime.

  • QueryInterface for IUnknown must always succeed and must always return the same pointer value.

Direct Access to All Interfaces

The COM Specification states that an implementation of QueryInterface must be “reflexive, symmetric, and transitive.” This means that, given an interface, a client must be capable of using an interface to get directly to any interface implemented on the object, including the interface the client is using to perform the query. These relationships are mandated to maintain an object’s identity in the face of multiple references to the same object. If these relationships are not upheld, a client could find itself with some code that doesn’t work just because it asked for the interfaces in the wrong order. With a properly implemented QueryInterface, query order does not matter.

Static Types

Each object can decide for itself whether it wants to expose an interface via QueryInterface, regardless of the class to which it belongs. However, after it has been asked and has answered either “Yes, I support that interface” or “No, I don’t support that interface,” it must stick to that answer. The reason for this is simple: After an object answers the query, it may never be asked again. For example, a client can pass a resultant interface pointer to another client, which never has to ask the object at all.

The potential for clients “talking among themselves” means that an object cannot use QueryInterface to make client-specific decisions – for example, those based on security constraints. The object also cannot use QueryInterface to make context decisions that could change during the life of an object, such as time of day. If a client caches an interface pointer returned when the context is favorable, it might not ask again when the context has changed.

An Object’s Apartment-Specific Identifier

The remoting layer of COM uses the pointer returned when querying for IUnknown as an object’s unique identifier in that apartment. Clients can also compare IUnknown* s as an identity test:

 1 bool AreEqualObjects(IUnknown* punk1, IUnknown* punk2) {
 2   if( punk1 == null && punk2 == null ) return true;
 3   if( !punk1 || !punk2 ) return false;
 4   IUnknown* punka = 0; punk1->QueryInterface(IID_IUnknown,
 5     (void**)&punka);
 6   IUnknown* punkb = 0; punk2->QueryInterface(IID_IUnknown,
 7     (void**)&punkb);
 8   bool b = (punka == punkb);
 9   punka->Release(); punkb->Release();
10   return b;
11 }

In fact, the ATL smart pointer classes have a method called IsEqualObject for performing just this comparison:

1 STDMETHODIMP CBall::SetPlaySurface(IRollSurface* prsNew) {
2   if( m_sprs.IsEqualObject(prsNew) ) return S_OK;
3   ...
4 }

However, although COM dictates that the pointer value of IUnknown must always be the same, it places no such restrictions on any other interface. This particular loophole leads to such techniques as tear-off interfaces, discussed further in this chapter.

Nothing Else

As long as these three laws are upheld, an implementation of QueryInterface can be developed using scenes from your most vivid fever dreams. Frankly, I doubt you’ll be able to come up with any techniques wackier than those already known, as I present during the rest of this chapter. However, if you do, ATL’s implementation of QueryInterface is fully extensible, as you’ll see.

Table-Driven QueryInterface

The Raw Interface Map

ATL’s implementation of QueryInterface is called InternalQueryInterface and is provided as a static member function of CComObjectRootBase (shown here with debugging extensions removed):

 1 static HRESULT WINAPI
 2 CComObjectRootBase::InternalQueryInterface(
 3   void*                     pThis,
 4   const _ATL_INTMAP_ENTRY*  pEntries,
 5   REFIID                    iid,
 6   void**                    ppvObject)
 7 {
 8     // First entry in the com map should be a simple map entry
 9     ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);
10     HRESULT hRes = AtlInternalQueryInterface(pThis, pEntries,
11         iid, ppvObject);
12     return hRes;
13 }

I show you the implementation of the internal function, AtlInternalQueryInterface, later. First, let’s discuss the ATL_INTMAP_ENTRY structure, an array of which is passed to InternalQueryInterface:

1 struct _ATL_INTMAP_ENTRY {
2     const IID* piid;       // the interface id (IID)
3     DWORD_PTR dw;
4     _ATL_CREATORARGFUNC* pFunc; //NULL:end, 1:offset, n:ptr
5 };

Each entry provides a pointer to an interface identifier, a pointer to a function to retrieve the requested interface, and a user-defined parameter to pass to the function. Functions that fit into this table must have signatures defined by the _ATL_CREATORARGFUNC typedef:

1 typedef HRESULT (WINAPI _ATL_CREATORARGFUNC)(
2     void* pv,        // Object's this pointer
3     REFIID riid,     // IID of requested interface
4     LPVOID* ppv,     // Storage for returned interface pointer
5     DWORD_PTR dw);   // dw from the interface map entry

The job of the interface map function is to take the object’s this pointer, the interface that the client is requesting, and the dw argument, and to return the appropriate interface pointer in the ppv argument. The function is free to do whatever it likes, within the laws of COM identity, to perform this magic. For example, the following function assumes that the dw member is the offset of the vptr from the this pointer – that is, this function assumes that we’re using multiple inheritance (MI) to implement the interface:

1 HRESULT WINAPI _MI(void* pvThis, REFIID riid, LPVOID* ppv, DWORD dw) {
2   *ppv = (BYTE*)pvThis + dw;
3   reinterpret_cast<IUnknown*>(*ppv)->AddRef();
4   return S_OK;
5 }

To fill in the _ATL_INTMAP_ENTRY for use with this function, we need to be able to calculate the offset of a vptr from the base, preferably at compile time. To help with this chore, ATL provides an interesting macro:

1 #define _ATL_PACKING 8
2 #define offsetofclass(base, derived) \
3    ((DWORD_PTR)(static_cast<base*>((derived*)_ATL_PACKING))- \
4    _ATL_PACKING)

The offsetofclass macro makes it look like we’re asking the compiler to dereference a pointer with the value 8, which is not such a great value for a pointer. [1] Instead, we’re asking the compiler to imagine a pointer to an object and to calculate the difference between that and a member inside that object, such as a vptr associated with a specific base class. The offsetofclass macro performs the same offset calculation the compiler does whenever it needs to perform a static_cast to a base class. Using offsetofclass enables us to fill an entry in an interface map like this:

 1 class CBeachBall :
 2   public CComObjectRootEx<CComSingleThreadModel>,
 3   public ISphere,
 4   public IRollableObject,
 5   public IPlaything {
 6 public:
 7 const static _ATL_INTMAP_ENTRY* WINAPI
 8 _GetEntries() {
 9   static const _ATL_INTMAP_ENTRY _entries[] = {
10   { &IID_IUnknown, offsetofclass(ISphere, CBeachBall),
11     _ATL_SIMPLEMAPENTRY },
12   { &IID_ISphere, offsetofclass(ISphere, CBeachBall), _MI },
13   { &IID_IRollableObject, offsetofclass(IRollableObject,
14     CBeachBall), _MI },
15   { &IID_IPlaything, offsetofclass(IPlaything, CBeachBall),
16     _MI },
17   };
18 return _entries;
19 };
20
21 HRESULT _InternalQueryInterface(REFIID iid, void** ppvObject)
22 {
23   return InternalQueryInterface(this, _GetEntries(), iid,
24     ppvObject);
25 }
26 ...
27 };

Besides the population of the interface map, you can see a couple interesting things in this code snippet. First, the _InternalQueryInterface function calls _GetEntries to retrieve the static interface map and forwards it to the InternalQueryInterface static member function in CComObjectRootBase. CComObject et al requires the _InternalQueryInterface function to implement QueryInterface.

Second, notice that IUnknown is the initial entry in the list and uses _ATL_SIMPLEMAPENTRY instead of _MI. As discussed in Chapter 4, “Objects in ATL,” the first entry is the one used for IUnknown and is required to be a simple entry. A simple entry is a special case that indicates that the interface is being exposed using multiple inheritance and that an offset is all that is needed to calculate the requested interface pointer. _ATL_SIMPLEMAPENTRY is a special value used in the pFunc field of the _ATL_INTMAP_ENTRY structure to indicate this case:

1 #define _ATL_SIMPLEMAPENTRY ((ATL::_ATL_CREATORARGFUNC*)1)

When AtlInternalQueryInterface encounters this special value, it knows how to perform the offset calculation just like the example function, _MI. Because _ATL_SIMPLEMAPENTRY completely replaces the need for a function that performs the offset calculation, ATL provides no interface map functions like _MI, although it provides others, as I discuss later.

Convenience Macros

You might enjoy writing the required _InternalQueryInterface and _GetEntries methods, as well as the GetUnknown method discussed in Chapter 4, but I do not. To write these functions and begin the static definition of the interface map, ATL provides BEGIN_COM_MAP:

 1 #define BEGIN_COM_MAP(x) public: \
 2     typedef x _ComMapClass; \
 3     ...
 4     IUnknown* _GetRawUnknown() { \
 5         ATLASSERT(_GetEntries()[0].pFunc == \
 6             _ATL_SIMPLEMAPENTRY); \
 7         return (IUnknown*)((INT_PTR)this+_GetEntries()->dw); \
 8     } \
 9     _ATL_DECLARE_GET_UNKNOWN(x) \
10     HRESULT _InternalQueryInterface(REFIID iid, \
11         void** ppvObject) { \
12         return InternalQueryInterface(this, _GetEntries(), \
13         iid, ppvObject); } \
14     const static ATL::_ATL_INTMAP_ENTRY* WINAPI _GetEntries() { \
15     static const ATL::_ATL_INTMAP_ENTRY _entries[] = { \
16          DEBUG_QI_ENTRY(x)

To zero-terminate the interface map and round out the _GetEntries implementation, ATL provides END_COM_MAP:

1 #define END_COM_MAP() \
2     __if_exists(_GetAttrEntries) {{NULL, \
3         (DWORD_PTR)_GetAttrEntries, _ChainAttr }, }\
4     {NULL, 0, 0}}; return _entries;} \
5     virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0; \
6     virtual ULONG STDMETHODCALLTYPE Release( void) = 0; \
7     STDMETHOD(QueryInterface)(REFIID, void**) = 0;

END_COM_MAP also provides another set of pure virtual member function definitions for QueryInterface, AddRef, and Release. This makes calls to IUnknown member functions unambiguous while calling them in the member functions of your ATL-based classes. We discuss the _GetAttrEntries function later in this chapter when we examine how ATL attributes can be used to declare an object’s supported interfaces.

To populate each entry in the interface map, ATL provides a set of macros that begin with the COM_INTERFACE_ENTRY prefix. The simplest and most useful is COM_INTERFACE_ENTRY itself:

1 #define COM_INTERFACE_ENTRY(x)\
2     {&_ATL_IIDOF(x), \
3     offsetofclass(x, _ComMapClass), \
4     _ATL_SIMPLEMAPENTRY},

Notice the use of _ComMapClass as the name of the class associated with the static interface map. BEGIN_COM_MAP provides this type. The _ATL_IIDOF macro, on the other hand, is ATL’s way of turning an interface type name into the corresponding GUID. Based on the presence or absence of the _ATL_NO_UUIDOF symbol, ATL either uses the VC++-specific __uuidof operator [2] or uses the C preprocessor’s token-pasting operator:

1 #ifndef _ATL_NO_UUIDOF
2 #define_ATL_IIDOF(x)__uuidof(x)
3 #else
4 #define_ATL_IIDOF(x)IID_##x
5 #endif

Using these macros, the interface map can be defined much more simply than in the previous example:

 1 class CBeachBall :
 2   public CComObjectRootEx<CBeachBall>,
 3   public ISphere,
 4   public IRollableObject,
 5   public IPlaything {
 6 public:
 7 BEGIN_COM_MAP(CBeachBall)
 8   COM_INTERFACE_ENTRY(ISphere)
 9   COM_INTERFACE_ENTRY(IRollableObject)
10   COM_INTERFACE_ENTRY(IPlaything)
11 END_COM_MAP()
12 ...
13 };

AtlInternalQueryInterface

Checking for IUnknown

InternalQueryInterface delegates to a global function, AtlInternalQueryInterface, to provide its implementation. Before walking the table of interface entries, AtlInternalQueryInterface checks the IID of the request. If IUnknown is requested, it pulls the first entry from the table and hands it back immediately, without walking the rest of the table. This is a welcome optimization, but it’s more than that: It’s absolutely necessary that IUnknown not be calculated by calling a function. Functions can fail and functions can return different values for the same interface identifier, both of which violate the laws of COM identity. The practical meaning for your classes is that the first entry must always be a simple one. Luckily, ATL is laden with assertions to this affect, so as long as you test your implementations at least once in debug mode before you ship them to your customers, you should be safe (on this note, anyway).

Walking the Table

At each entry in the table, a decision is made based on whether the piid member, a pointer to the interface identifier for that entry, is NULL. If it is not NULL, the IID of the entry is compared with the IID of the request. If a match is found, the function pFunc references are called and the result is returned to the client. If there is no match, the search advances to the next entry in the table.

On the other hand, if the piid member is NULL, no matter what the IID of the request is, the pFunc is called. If the result is S_OK, the result is returned to the client. Otherwise, the search continues with the next entry. This behavior is used for any of the COM_INTERFACE_ENTRY_XXX_BLIND macros, such as COM_INTERFACE_ENTRY_AGGREGATE_BLIND.

Implementation

The following is the implementation of AtlInternalQueryInterface:

 1 ATLINLINE ATLAPI AtlInternalQueryInterface(
 2     void* pThis,
 3     const _ATL_INTMAP_ENTRY* pEntries,
 4     REFIID iid,
 5     void** ppvObject)
 6 {
 7     ATLASSERT(pThis != NULL);
 8     ATLASSERT(pEntries!= NULL);
 9
10     if(pThis == NULL || pEntries == NULL)
11         return E_INVALIDARG ;
12
13     // First entry in the com map should be a simple map entry
14     ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);
15     if (ppvObject == NULL)
16         return E_POINTER;
17     *ppvObject = NULL;
18     if (InlineIsEqualUnknown(iid)) { // use first interface
19         IUnknown* pUnk=(IUnknown*)((INT_PTR)pThis+pEntries->dw);
20         pUnk->AddRef();
21         *ppvObject = pUnk;
22         return S_OK;
23     }
24     while (pEntries->pFunc != NULL) {
25         BOOL bBlind = (pEntries->piid == NULL);
26         if (bBlind || InlineIsEqualGUID(*(pEntries->piid), iid)) {
27             if (pEntries->pFunc == _ATL_SIMPLEMAPENTRY) { //offset
28                 ATLASSERT(!bBlind);
29                 IUnknown* pUnk = (IUnknown*)((INT_PTR)
30                     pThis+pEntries->dw);
31                 pUnk->AddRef();
32                 *ppvObject = pUnk;
33                 return S_OK;
34             }
35             else { //actual function call
36                 HRESULT hRes = pEntries->pFunc(pThis,
37                     iid, ppvObject, pEntries->dw);
38                 if (hRes == S_OK || (!bBlind && FAILED(hRes)))
39                     return hRes;
40             }
41         }
42         pEntries++;
43     }
44     return E_NOINTERFACE;
45 }

Multiple Inheritance

To support multiple inheritance (MI) of interfaces, ATL provides four separate interface entry macros. Two are for straight casts and two are for branching casts. A straight cast is a static_cast that the compiler needs no extra information to perform – that is, there are no ambiguities. On the other hand, a branching cast is used when a class has several base classes that all derive from the same base class themselves. Because a straight cast to the common base class would be ambiguous, the compiler needs the inheritance branch to follow to resolve the ambiguity.

Straight Casting

COM_INTERFACE_ENTRY and COM_INTERFACE_ENTRY_IID

As I mentioned, COM_INTERFACE_ENTRY is the one you’ll use most of the time. Its close cousin is COM_INTERFACE_ENTRY_IID:

1#define COM_INTERFACE_ENTRY_IID(iid, x) \
2  { &iid, offsetofclass(x, _ComMapClass), _ATL_SIMPLEMAPENTRY},

This macro enables you to specify the IID separately from the name of the interface. The classic use of this macro is to avoid ambiguity. Imagine that you’ve got two interfaces that derive from the same base interface:

1interface IGlobe : ISphere {};
2interface IPlanet : ISphere {};

If you’ve got a class that derives from both of these interfaces, the compiler won’t know which base class to cast to if you use COM_INTERFACE_ENTRY for ISphere:

 1class CDesktopGlobe :
 2  public CComObjectRootEx<CDesktopGlobe>,
 3  public IGlobe,
 4  public IPlanet {
 5public:
 6  ...
 7BEGIN_COM_MAP(CDesktopGlobe)
 8  COM_INTERFACE_ENTRY(ISphere) // ambiguous
 9  COM_INTERFACE_ENTRY(IGlobe)
10  COM_INTERFACE_ENTRY(IPlanet)
11END_COM_MAP()
12  // ISphere methods
13  ...
14  // IGlobe methods
15  ...
16  // IPlanet methods
17  ...
18};

The problem is more easily seen when you look at the inheritance hierarchy in Figure 6.1.

Figure 6.1. CDesktopGlobe inheritance hierarchy

_images/06atl01.jpg

Figure6.1 shows two interfaces that CDesktopGlobe has inherited from more than once, IUnknown and ISphere. IUnknown is not a problem because ATL handles it specially by choosing the first entry in the interface map (as discussed earlier). ISphere is a problem, though, because there are two of them, IGlobe and IPlanet. Each base interface has a separate vptr that points to a separate vtbl. Even though we’ve got a shared implementation of all the methods of ISphere and, therefore, duplicate entries in both the IGlobe and the IPlanet vtbls, the compiler needs us to pick one. COM_INTERFACE_ENTRY_IID enables us to resolve this ambiguity:

 1class CDesktopGlobe :
 2  public CComObjectRootEx<CDesktopGlobe>,
 3  public IGlobe,
 4  public IPlanet {
 5public:
 6...
 7BEGIN_COM_MAP(CDesktopGlobe)
 8  COM_INTERFACE_ENTRY_IID(IID_ISphere, IGlobe) // unambiguous
 9  COM_INTERFACE_ENTRY(IGlobe)
10  COM_INTERFACE_ENTRY(IPlanet)
11END_COM_MAP()
12...
13};

In this case, because we have shared implementations of the ISphere methods in our implementation of IGlobe and IPlanet, it doesn’t really matter which one we hand out. Sometimes it matters very much. COM_INTERFACE_ENTRY_IID is often used when exposing multiple dual interfaces, each of which derives from IDispatch. Using IDispatchImpl, we provide base class implementations of IDispatch that are different based on the dual interface we’re implementing. In fact, any time you’ve got multiple implementations of the same interface in the base classes, you must decide which implementation is the “default.” Imagine another example of a base class interface implementation that has nothing to do with scripting:

1template <typename Base> class ISphereImpl : public Base {...};

Using ISphereImpl looks like this:

 1class CDesktopGlobe :
 2  public CComObjectRootEx<CDesktopGlobe>,
 3  public ISphereImpl<IGlobe>,
 4  public ISphereImpl<IPlanet> {
 5public:
 6...
 7BEGIN_COM_MAP(CDesktopGlobe)
 8  COM_INTERFACE_ENTRY_IID(IID_ISphere, IGlobe) // Default ISphere
 9  COM_INTERFACE_ENTRY(IGlobe)
10  COM_INTERFACE_ENTRY(IPlanet)
11END_COM_MAP()
12...
13};

Here’s the problem: If the client queries for IGlobe (or ISphere) and calls ISphere methods, it gets different behavior than if it were to query for IPlanet and call ISphere methods. Now the client has just the kind of order-of-query problem that the laws of COM identity were built to prohibit. Multiple implementations of the same base interface clearly violate the spirit, if not the letter, of the laws of COM identity.

Branch Casting

COM_INTERFACE_ENTRY2 and COM_INTERFACE_ENTRY2_IID

Both COM_INTERFACE_ENTRY2 and COM_INTERFACE_ENTRY2_IID are simple entries meant for use with MI:

 1 #define COM_INTERFACE_ENTRY2(x, x2)\
 2   { &_ATL_IIDOF(x),\
 3     reinterpret_cast<DWORD_PTR>( \
 4       static_cast<x*>( \
 5         static_cast<x2*>( \
 6           reinterpret_cast<_ComMapClass*>(8))))-8, \
 7     _ATL_SIMPLEMAPENTRY},
 8
 9 #define COM_INTERFACE_ENTRY2_IID(iid, x, x2)\
10   { &iid,\
11     reinterpret_cast<DWORD_PTR>( \
12       static_cast<x*>( \
13         static_cast<x2*>( \
14           reinterpret_cast<_ComMapClass*>(8))))-8, \
15     _ATL_SIMPLEMAPENTRY},

COM_INTERFACE_ENTRY2 is much like COM_INTERFACE_ENTRY_IID because it enables you to resolve the problem of multiple bases:

 1class CDesktopGlobe :
 2  public CComObjectRootEx<CDesktopGlobe>,
 3  public IGlobe,
 4  public IPlanet {
 5public:
 6...
 7BEGIN_COM_MAP(CDesktopGlobe)
 8  COM_INTERFACE_ENTRY2(ISphere, IGlobe) // Use the IGlobal branch
 9  COM_INTERFACE_ENTRY(IGlobe)
10  COM_INTERFACE_ENTRY(IPlanet)
11END_COM_MAP()
12...
13};

This macro performs its magic by enabling you to specify two things, the interface to expose (such as ISphere) and the branch of the inheritance hierarchy tofollow to get to the implementation of that interface (such as IGlobe). This macro is slightly different from COM_INTERFACE_ENTRY_IID, in that the interface is specified by name instead of by IID. If you want to be very explicit about both, use COM_INTERFACE_ENTRY2_IID:

 1class CDesktopGlobe :
 2  public CComObjectRootEx<CDesktopGlobe>,
 3  public IGlobe,
 4  public IPlanet {
 5public:
 6...
 7BEGIN_COM_MAP(CDesktopGlobe)
 8  COM_INTERFACE_ENTRY2_IID(&IID_ISphere, ISphere, IGlobe)
 9  COM_INTERFACE_ENTRY(IGlobe)
10  COM_INTERFACE_ENTRY(IPlanet)
11END_COM_MAP()
12...
13};

COM_INTERFACE_ENTRY2[_IID] provides no extra functionality beyond what COM_INTERFACE_ENTRY[_IID] provides, so I tend to always use the latter.

Handling Name Conflicts

One of the problems with MI is name collisions. Imagine the following interfaces:

1interface ICowboy : IUnknown {
2    HRESULT Draw();
3};
4
5interface IArtist : IUnknown {
6   HRESULT Draw();
7};

Because both Draw methods have the same signature, using straight MI requires a single shared implementation:

 1// Ace Powell was a cowboy/artist who lived in the western US
 2// from 1912 to his death in 1978. I'd like to thank Tim Ewald
 3// for this fabulous example, which I have used to death
 4// for years.
 5class CAcePowell :
 6    public CComObjectRootEx<CComSingleThreadModel>,
 7    public ICowboy,
 8    public IArtist
 9{
10public:
11BEGIN_COM_MAP(CAcePowell)
12  COM_INTERFACE_ENTRY(ICowboy)
13  COM_INTERFACE_ENTRY(IArtist)
14END_COM_MAP()
15...
16  STDMETHODIMP Draw() { /* Act as a cowboy or an artist? */ }
17};

The implied meaning of Draw is very different for an artist than it is for a cowboy, so we’d like to be able to provide two Draw implementations. We can deal with this predicament in a couple ways. The first solution uses a Microsoft-specific extension to the C++ language that allows a very intuitive syntax for disambiguating the Draw implementation. It’s employed as follows:

 1class CAcePowell :
 2    public CComObjectRootEx<CComSingleThreadModel>,
 3    public ICowboy,
 4    public IArtist {
 5public:
 6BEGIN_COM_MAP(CAcePowell)
 7  COM_INTERFACE_ENTRY(ICowboy)
 8  COM_INTERFACE_ENTRY(IArtist)
 9END_COM_MAP()
10...
11  STDMETHODIMP IArtist::Draw() {
12    /* Draw like an artist */
13    return S_OK;
14  }
15
16  STDMETHODIMP ICowboy::Draw() {
17    /* Draw like a cowboy */
18    return S_OK;
19  }
20};

By decorating the method with the name of the interface, the compiler can figure out which Draw method you are implementing. However, there is an important limitation with this technique imposed by what I consider a bug in the compiler: You must place the body of these methods in the class declaration in the header file. If you try to just put a declaration in the header file and put the body in the CPP file, it won’t compile.

This syntax is not Standard C++, so don’t expect to use this with non-Microsoft compilers (not that much of the rest of this book would be useful on non-Microsoft compilers anyway). If you’re implementing clashing methods from multiple interfaces, you might want to turn to alternative technique to address the problem of name collision. I call this technique, long known to the C++ community, forwarding shims. [3]

Forwarding shims rely upon the fact that although we can’t distinguish methods in the most derived class, we can certainly distinguish the methods in individual base classes:

1struct _IArtist : public IArtist {
2  STDMETHODIMP Draw() { return ArtistDraw(); }
3  STDMETHOD(ArtistDraw)() =0;
4};
5
6struct _ICowboy : public ICowboy {
7  STDMETHODIMP Draw() { return CowboyDraw(); }
8  STDMETHOD(CowboyDraw)() =0;
9};

Both _IArtist and _ICowboy are shim classes that implement the method with the conflicting name and forward to another pure virtual member function with a unique name. Because both shims derive from the interface in question, the interfaces IArtist and ICowboy can still appear in the interface map without difficulty:

 1class CAcePowell :
 2    public CComObjectRootEx<CComSingleThreadModel>,
 3    public _ICowboy,
 4    public _IArtist {
 5public:
 6BEGIN_COM_MAP(CAcePowell)
 7  COM_INTERFACE_ENTRY(ICowboy)
 8  COM_INTERFACE_ENTRY(IArtist)
 9END_COM_MAP()
10...
11  STDMETHODIMP ArtistDraw();
12  STDMETHODIMP CowboyDraw();
13};

This trick fills the vtbls for IArtist and ICowboy with _IArtist::Draw and _ICowboy::Draw. These functions, in turn, forward to the more derived class’s implementation of ArtistDraw and CowboyDraw. The forwarding shims remove the name conflict at the cost of an extra vtable per shim class, an extra entry per method per vtable, and an extra virtual function invocation per call. If this extra cost bothers you, remove it using the standard ATL tricks [4] :

 1template <typename Deriving>
 2struct ATL_NO_VTABLE _IArtist : public IArtist {
 3  STDMETHODIMP Draw() {
 4    return static_cast<Deriving*>(this)->ArtistDraw();
 5  }
 6};
 7
 8template <typename Deriving>
 9struct ATL_NO_VTABLE _ICowboy : public ICowboy {
10  STDMETHODIMP Draw() {
11    return static_cast<Deriving*>(this)->CowboyDraw();
12  }
13};
14
15class ATL_NO_VTABLE CAcePowell :
16    public CComObjectRootEx<CComSingleThreadModel>,
17    public _ICowboy<CAcePowell>,
18    public _IArtist<CAcePowell>
19{
20public:
21BEGIN_COM_MAP(CAcePowell)
22  COM_INTERFACE_ENTRY(ICowboy)
23  COM_INTERFACE_ENTRY(IArtist)
24END_COM_MAP()
25...
26  HRESULT ArtistDraw();
27  HRESULT CowboyDraw();
28};

Don’t Go Off Half-Cocked…

You might think it would be enough to change one of the names by using only one forwarding shim:

 1template <typename Deriving>
 2struct ATL_NO_VTABLE _ICowboy : public ICowboy {
 3  STDMETHODIMP Draw() {
 4    return static_cast<Deriving*>(this)->CowboyDraw();
 5  }
 6};
 7
 8class ATL_NO_VTABLE CAcePowell :
 9    public CComObjectRootEx<CComSingleThreadModel>,
10    public _ICowboy<CAcePowell>,
11    public IArtist {
12public:
13BEGIN_COM_MAP(CAcePowell)
14  COM_INTERFACE_ENTRY(ICowboy)
15  COM_INTERFACE_ENTRY(IArtist)
16END_COM_MAP()
17...
18  HRESULT Draw();       // Use for both IArtist::Draw and
19                        // ICowboy::Draw
20  HRESULT CowboyDraw(); // Never called!
21};

Don’t be tempted to try this. Remember that forwarding shims depend on overriding the behavior for the same member function name in the base classes. If you provide an implementation of the function in question with the same name as the function you’re implementing in the forwarding shim in the base, the forwarding shim function will never be called. By implementing one of the functions in the deriving class, you’ve effectively provided an implementation of both, putting you right back where you were in the first place.

Interface Coloring

In the same “sneaky C++ trick” way that forwarding shims let you fill the appropriate vtbl entries even if the compiler won’t cooperate, ATL supports another technique called interface coloring. Interface coloring is based on the idea that two classes can be layout compatible but not type compatible. Two classes are layout compatible if they have the same vtbl structure: The functions must be in exactly the same order, and the parameters must be exactly the same. The names, however, may be different. For example, the following two classes are layout compatible because they each result in a vtbl with the same number of methods, and every method at the same offset has the same signature:

 1struct ISphere : IUnknown {
 2  STDMETHOD(Rotate)(long nDegrees, long* pnOrientation) =0;
 3  STDMETHOD(Twirl)(long nVelocity) =0;
 4};
 5
 6struct IRedSphere {
 7  // Colored IUnknown methods
 8  STDMETHOD(RedQueryInterface)( REFIID riid, void** ppv) =0;
 9  STDMETHOD_(ULONG, RedAddRef)() =0;
10  STDMETHOD_(ULONG, RedRelease)() =0;
11
12  // Uncolored ISphere methods
13  STDMETHOD(Rotate)(long nDegrees, long* pnOrientation) =0;
14  STDMETHOD(Twirl)(long nVelocity) =0;
15};

However, because IRedSphere does not derive from ISphere, IRedSphere is not type compatible: The compiler won’t let you pass IRedSphere where ISphere is expected (without coercion). Cloning the layout of an interface is known as interface coloring. The layout-compatible interface is said to be colored because it is identical to the original, except for the names; that feature is not important to the runtime behavior of your object, just as a color is unimportant to the runtime behavior of your car. The names are used at compile time, though, and enable you to implement multiple versions of the same interface:

 1class CDesktopGlobe :
 2  public CComObjectRootEx<CComSingleThreadModel>,
 3  public IRedSphere,
 4  public IGlobe,
 5  public IPlanet {
 6public:
 7  ...
 8BEGIN_COM_MAP(CDesktopGlobe)
 9  // Expose IRedShere when ISphere is requested
10  COM_INTERFACE_ENTRY_IID(IID_ISphere, IRedSphere)
11  COM_INTERFACE_ENTRY(IGlobe)
12  COM_INTERFACE_ENTRY(IPlanet)
13END_COM_MAP()
14  ...
15  // Colored method implementations
16  STDMETHODIMP RedQueryInterface(REFIID riid, void** ppv)
17  { return GetUnknown()->QueryInterface(riid, ppv); }
18
19  STDMETHODIMP_(ULONG) RedAddRef() {
20    _ThreadModel::Increment(&m_cRefSphere);
21    return GetUnknown()->AddRef();
22  }
23
24  STDMETHODIMP_(ULONG) RedRelease() {
25    _ThreadModel::Decrement(&m_cRefSphere);
26    return GetUnknown()->Release();
27  }
28
29private:
30  long m_cRefSphere;
31};

By deriving from IRedSphere, we can provide an implementation of all the colored methods separately from the uncolored ones. By coloring the IUnknown methods of IRedSphere, we can handle IUnknown calls on ISphere separately from the other implementations of IUnknown by the other interfaces. In this case, we’re using RedAddRef and RedRelease to keep track of an ISphere-specific reference count. And even though we expose IRedSphere to the client when it asks for ISphere, as far as the client is concerned, it has just an ISphere interface pointer. Because IRedSphere and ISphere are layout compatible, as far as COM is concerned, the client is right.

 1void TryRotate(IUnknown* punk) {
 2  ISphere* ps = 0;
 3
 4  // Implicit AddRef really a call to RedAddRef
 5  if(SUCCEEDED(punk->QueryInterface(IID_ISphere, (void**)&ps))) {
 6    // ps actually points to an IRedSphere*
 7
 8    ps->Rotate();
 9    ps->Release(); // Really a call to RedRelease
10  }
11}

COM_INTERFACE_ENTRY_IMPL and COM_INTERFACE_ENTRY_IMPL_IID

Interface coloring is somewhat interesting in the same way that a car wreck on the side of the road is interesting: It can be disconcerting as well and can slow traffic. Beginning with ATL 3.0, the vast majority of IXxxImpl classes no longer use interface coloring. In ATL 2. x, interface coloring was used for some, but not all, of the IXxxImpl classes to perform interface-specific reference counting. These implementation classes took the following form:

1template <typename Deriving> class IXxxImpl {...};

Instead of deriving from the interface the class implemented, the implementation class used interface coloring to make itself layout compatible with the implemented interface. This enabled each class to implement its own reference counting but prohibited the use of the simple COM_INTERFACE_ENTRY macro. Additional macros were provided to make the necessary entries in the interface map:

1#define COM_INTERFACE_ENTRY_IMPL(x) \
2  COM_INTERFACE_ENTRY_IID(_ATL_IIDOF(x), x##Impl<_ComMapClass>)
3
4#define COM_INTERFACE_ENTRY_IMPL_IID(iid, x) \
5  COM_INTERFACE_ENTRY_IID(iid, x##Impl<_ComMapClass>)

The interface coloring technique was useful only if you wanted to track some of the ATL-implemented interfaces. Beginning with ATL 3.0, ATL uses a more generic mechanism [5] that tracks reference counts on all interfaces. Toward that end, all the ATL-implementation classes actually derive from the interface in question, making the use of COM_INTERFACE_ENTRY_IMPL and COM_INTERFACE_ENTRY_IMPL_IID macros unnecessary. All new and ported code should use COM_INTERFACE_ENTRY or COM_INTERFACE_ENTRY_IID instead. Old code that used the IMPL forms of the macros still compiles under the new ATL and acts appropriately.

Tear-Off Interfaces

Although multiple inheritance is preferred when implementing multiple interfaces, it’s not perfect. One of the problems is something called vptr-bloat. For each interface a class derives from, that’s another vptr per instance of that class. Beefing up our beach ball implementation can lead to some significant overhead:

 1class CBeachBall :
 2  public CComObjectRootEx<CBeachBall>,
 3  public ISphere,
 4  public IRollableObject,
 5  public IPlaything,
 6  public ILethalObject,
 7  public ITakeUpSpace,
 8  public IWishIWereMoreUseful,
 9  public ITryToBeHelpful,
10  public IAmDepressed {...};

Because each beach ball implements eight interfaces, each instance has 32 bytes of overhead on Win32 systems before the reference count or any useful state. If clients actually made heavy use of these interfaces, that wouldn’t be too high of a price to pay. However, my guess is that most clients will use beach balls for their rollable and play-thing abilities. Because the other interfaces will be used infrequently, we’d rather not pay the overhead until they are used. For this, Crispin Goswell invented the tear-off interface, which he described in the article “The COM Programmer’s Cookbook.” [6]

Standard Tear-Offs

A tear-off interface is an interface that’d you want to expose on demand but not actually inherit from in the main class. Instead, an auxiliary class inherits from the interface to be torn off, and instances of that class are created any time a client queries for that interface. For example, assuming that few clients will think to turn a beach ball into a lethal weapon, ILethalObject would make an excellent tear-off interface for the CBeachBall class. Instead of using CComObjectRootEx as the base class, ATL classes implementing tear-off interfaces use the CComTearOffObjectBase as their base class:

1template <class Owner, class ThreadModel = CComObjectThreadModel>
2class CComTearOffObjectBase
3    : public CComObjectRootEx<ThreadModel> {
4public:
5    typedef Owner _OwnerClass;
6    Owner* m_pOwner;
7    CComTearOffObjectBase() { m_pOwner = NULL; }
8};

CComTearOffObjectBase provides one additional service, which is the caching of the owner of the tear-off interface. Each tear-off belongs to an owner object that has torn it off to satisfy a client’s request. The owner is useful so that the tear-off instance can access member data or member functions of the owner class:

 1class CBeachBallLethalness :
 2  public CComTearOffObjectBase<CBeachBall,
 3    CComSingleThreadModel>,
 4  public ILethalObject {
 5public:
 6BEGIN_COM_MAP(CBeachBallLethalness)
 7  COM_INTERFACE_ENTRY(ILethalObject)
 8END_COM_MAP()
 9
10  // ILethalObject methods
11  STDMETHODIMP Kill() {
12    m_pOwner->m_gasFill = GAS_HYDROGEN;
13    m_pOwner->HoldNearOpenFlame();
14    return S_OK;
15  }
16};

COM_INTERFACE_ENTRY_TEAR_OFF

To use this tear-off implementation, the owner class uses the COM_INTERFACE_ENTRY_TEAR_OFF macro:

1#define COM_INTERFACE_ENTRY_TEAR_OFF(iid, x) \
2  { &iid, \
3    (DWORD_PTR)&ATL::_CComCreatorData< \
4        ATL::CComInternalCreator< ATL::CComTearOffObject< x > > \
5          >::data, \
6  _Creator },

_CComCreatorData is just a sneaky trick to fill in the dw entry of the interface entry with a function pointer to the appropriate creator function. The creator function is provided by CComInternalCreator, which is identical to CComCreator except that it calls _InternalQueryInterface to get the initial interface instead of QueryInterface. This is necessary because, as I show you soon, QueryInterface on a tear-off instance forwards to the owner, but we want the initial interface on a new tear-off to come from the tear-off itself. That is, after all, why we’re creating the tear-off: to expose that interface.

The pFunc entry COM_INTERFACE_ENTRY_TEAR_OFF makes is the first instance of a nonsimple entry so far in this chapter and, thus, the first macro we’ve seen that cannot be used as the first entry in the interface map. The _Creator function is a static member of the CComObjectRootBase class that simply calls the Creator function pointer held in the dw parameter:

1static HRESULT WINAPI
2CComObjectRootBase::_Creator(void* pv, REFIID iid,
3  void** ppv, DWORD_PTR dw) {
4  _ATL_CREATORDATA* pcd = (_ATL_CREATORDATA*)dw;
5  return pcd->pFunc(pv, iid, ppv);
6}

The most derived class of a tear-off implementation is not CComObject, but rather CComTearOffObject. CComTearOffObject knows about the m_pOwner member of the base and fills it during construction. Because each tear-off instance is a separate C++ object, each maintains its own lifetime. However, to live up to the laws of COM identity, each tear-off forwards requests for new interfaces to the owner:

 1template <class Base>
 2class CComTearOffObject : public Base {
 3public:
 4    CComTearOffObject(void* pv) {
 5        ATLASSERT(m_pOwner == NULL);
 6        m_pOwner = reinterpret_cast<Base::_OwnerClass*>(pv);
 7        m_pOwner->AddRef();
 8    }
 9
10    ~CComTearOffObject() {
11        m_dwRef = -(LONG_MAX/2);
12        FinalRelease();
13#ifdef _ATL_DEBUG_INTERFACES
14        _AtlDebugInterfacesModule.DeleteNonAddRefThunk(
15            _GetRawUnknown());
16#endif
17        m_pOwner->Release();
18    }
19
20    STDMETHOD_(ULONG, AddRef)() {
21        return InternalAddRef();
22    }
23
24    STDMETHOD_(ULONG, Release)() {
25        ULONG l = InternalRelease();
26        if (l == 0)
27            delete this;
28        return l;
29    }
30
31    STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) {
32        return m_pOwner->QueryInterface(iid, ppvObject);
33    }
34};

To use a tear-off, the owner class adds an entry to its interface map:

 1class CBeachBall :
 2  public CComObjectRootEx<CBeachBall>,
 3  public ISphere,
 4  public IRollableObject,
 5  public IPlaything,
 6  //public ILethalObject, // Implemented by the tear-off
 7  public ITakeUpSpace,
 8  public IWishIWereMoreUseful,
 9  public ITryToBeHelpful,
10  public IAmDepressed {
11public:
12BEGIN_COM_MAP(CBeachBall)
13  COM_INTERFACE_ENTRY(ISphere)
14  COM_INTERFACE_ENTRY(IRollableObject)
15  COM_INTERFACE_ENTRY(IPlaything)
16  COM_INTERFACE_ENTRY_TEAR_OFF(IID_ILethalObject,
17    CBeachBallLethalness)
18  COM_INTERFACE_ENTRY(ITakeUpSpace)
19  COM_INTERFACE_ENTRY(IWishIWereMoreUseful)
20  COM_INTERFACE_ENTRY(ITryToBeHelpful)
21  COM_INTERFACE_ENTRY(IAmDepressed)
22END_COM_MAP()
23...
24private:
25  GAS_TYPE m_gasFill;
26  void     HoldNearOpenFlame();
27  // Tear-offs are generally friends
28  friend class CBeachBallLethalness;
29};

Because the owner class is no longer deriving from ILethalObject, each instance is 4 bytes lighter. However, when the client queries for ILethalObject, we’re spending 4 bytes for the ILethalObject vptr in CBeachBallLethalness, 4 bytes for the CBeachBallLethalness reference count, and 4 bytes for the m_pOwner back pointer. You might wonder how spending 12 bytes to save 4 bytes actually results in a savings. I’ll tell you: volume! Or rather, the lack thereof. Because we’re paying only the 12 bytes during the lifetime of the tear-off instance and we’ve used extensive profiling to determine ILethalObject is rarely used, the overall object footprint should be smaller.

Tear-Off Caveats

Before wrapping yourself in the perceived efficiency of tear-offs, you should be aware of these cautions:

  • Tear-offs are only for rarely used interfaces. Tear-off interfaces are an implementation trick to be used to reduce vptr bloat when extensive prototyping has revealed this to be a problem. If you don’t have this problem, save yourself the trouble and avoid tear-offs.

  • Tear-offs are for intra-apartment use only. The stub caches a tear-off interface for the life of an object. In fact, the current implementation of the stub manager caches each interface twice, sending the overhead of that particular interface from 12 bytes to 24 bytes.

  • Tear-offs should contain no state of their own. If a tear-off contains its own state, there will be one copy of that state per tear-off instance, breaking the spirit, if not the laws, of COM identity. If you have per-interface state, especially large state that you want to be released when no client is using the interface, use a cached tear-off.

Cached Tear-Offs

You might have noticed that every query for ILethalObject results in a new tear-off instance, even if the client already holds an ILethalObject interface pointer. This might be fine for a single interface tear-off, but what about a related group of interfaces that will be used together? [7] For example, imagine moving the other rarely used interfaces of CBeachBall to a single tear-off implementation:

 1class CBeachBallAttitude :
 2  public CComTearOffObjectBase<CBeachBall,
 3    CComSingleThreadModel>,
 4  public ITakeUpSpace,
 5  public IWishIWereMoreUseful,
 6  public ITryToBeHelpful,
 7  public IAmDepressed {
 8public:
 9BEGIN_COM_MAP(CBeachBallAttitude)
10  COM_INTERFACE_ENTRY(ITakeUpSpace)
11  COM_INTERFACE_ENTRY(IWishIWereMoreUseful)
12  COM_INTERFACE_ENTRY(ITryToBeHelpful)
13  COM_INTERFACE_ENTRY(IAmDepressed)
14END_COM_MAP()
15...
16};

The following use of this tear-off implementation compiles and exhibits the appropriate behavior, but the overhead of even a single tear-off is exorbitant:

 1class CBeachBall :
 2  public CComObjectRootEx<CBeachBall>,
 3  public ISphere,
 4  public IRollableObject,
 5  public IPlaything
 6  // No tearoff interfaces in base class list
 7{
 8public:
 9BEGIN_COM_MAP(CBeachBall)
10  COM_INTERFACE_ENTRY(ISphere)
11  COM_INTERFACE_ENTRY(IRollableObject)
12  COM_INTERFACE_ENTRY(IPlaything)
13  // tearoffs are listed in the interface map
14  COM_INTERFACE_ENTRY_TEAR_OFF(IID_ILethalObject,
15    CBeachBallLethalness)
16  COM_INTERFACE_ENTRY_TEAR_OFF(IID_ITakeUpSpace,
17    CBeachBallAttitude)
18  COM_INTERFACE_ENTRY_TEAR_OFF(IID_IWishIWereMoreUseful,
19    CBeachBallAttitude)
20  COM_INTERFACE_ENTRY_TEAR_OFF(IID_ITryToBeHelpful,
21    CBeachBallAttitude)
22  COM_INTERFACE_ENTRY_TEAR_OFF(IID_IAmDepressed,
23    CBeachBallAttitude)
24END_COM_MAP()
25...
26};

Because we’ve grouped the “attitude” interfaces together into a single tear-off implementation, every time the client queries for any of them, it pays the overhead of all of them. To allow this kind of grouping but avoid the overhead of creating a new instance for every query, ATL provides an implementation of a cached tear-off. The owner holds a cached tear-off if there is even one outstanding interface to the tear-off. The initial query creates and caches the tear-off. Subsequent queries use the cached tear-off. The final release deletes the tear-off instance.

COM_INTERFACE_ENTRY_CACHED_TEAR_OFF

To support caching tear-offs, ATL provides another interface macro:

1#define COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(iid, x, punk) \
2  { &iid, \
3    (DWORD_PTR)&ATL::_CComCacheData< \
4    ATL::CComCreator< ATL::CComCachedTearOffObject< x > >, \
5    (DWORD_PTR)offsetof(_ComMapClass, punk) >::data, \
6    _Cache },

The _CComCacheData class is used to stuff a pointer into an _ATL_CACHEDATA structure:

1struct _ATL_CACHEDATA {
2  DWORD             dwOffsetVar;
3  _ATL_CREATORFUNC* pFunc;
4};

The use of this structure allows the dw to point to a Creator function pointer as well as another member, an offset. The offset is from the base of the owner class to the member data that is used to cache the pointer to the tear-off. The _Cache function, another static member function of CComObjectRootBase, uses the offset to calculate the address of the pointer and checks the pointer to determine whether to create a new instance of the cached tear-off:

 1static HRESULT WINAPI
 2CComObjectRootBase::_Cache(
 3    void* pv,
 4    REFIID iid,
 5    void** ppvObject,
 6    DWORD_PTR dw)
 7{
 8  HRESULT hRes = E_NOINTERFACE;
 9  _ATL_CACHEDATA* pcd = (_ATL_CACHEDATA*)dw;
10  IUnknown** pp = (IUnknown**)((DWORD_PTR)pv + pcd->dwOffsetVar);
11  if (*pp == NULL)
12    hRes = pcd->pFunc(pv, __uuidof(IUnknown), (void**)pp);
13  if (*pp != NULL)
14    hRes = (*pp)->QueryInterface(iid, ppvObject);
15  return hRes;
16}

Just as an instance of a tear-off uses CComTearOffObject instead of CComObject to provide the implementation of IUnknown, cached tear-offs use CComCachedTearOffObject. CComCachedTearOffObject is nearly identical to CComAggObject [8] because of the way that the lifetime and identity of the tear-off are subsumed by that of the owner. The only difference is that the cached tear-off, like the tear-off, initializes the m_pOwner member.

Replacing the inefficient use of COM_INTERFACE_ENTRY_TEAR_OFF with COM_INTERFACE_ENTRY_CACHED_TEAR_OFF looks like this:

 1class CBeachBall :
 2  public CComObjectRootEx<CBeachBall>,
 3  public ISphere,
 4  public IRollableObject,
 5  public IPlaything {
 6public:
 7BEGIN_COM_MAP(CBeachBall)
 8  COM_INTERFACE_ENTRY(ISphere)
 9  COM_INTERFACE_ENTRY(IRollableObject)
10  COM_INTERFACE_ENTRY(IPlaything)
11  COM_INTERFACE_ENTRY_TEAR_OFF(IID_ILethalObject,
12    CBeachBallLethalness)
13  COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(IID_ITakeUpSpace,
14    CBeachBallAttitude, m_spunkAttitude.p)
15  COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(IID_IWishIWereMoreUseful,
16    CBeachBallAttitude, m_spunkAttitude.p)
17  COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(IID_ITryToBeHelpful,
18    CBeachBallAttitude, m_spunkAttitude.p)
19  COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(IID_IAmDepressed,
20    CBeachBallAttitude, m_spunkAttitude.p)
21END_COM_MAP()
22DECLARE_GET_CONTROLLING_UNKNOWN() // See the Aggregation section
23...
24public:
25  CComPtr<IUnknown> m_spunkAttitude;
26};

Another Use for Cached Tear-Offs

Cached tear-offs have another use that is in direct opposition to standard tear-offs: caching per-interface resources. For example, imagine a dictionary object that implements a rarely used IHyphenation interface:

1interface IHyphenation : public IUnknown {
2  HRESULT Hyphenate([in] BSTR bstrUnhyphed,
3    [out, retval] BSTR* pbstrHyphed);
4};

Performing hyphenation is a matter of consulting a giant look-up table. If a CDictionary object were to implement the IHyphenation interface, it would likely doso as a cached tear-off to manage the resources associated with the look-up table. When the hyphenation cached tear-off is first created, it acquires the look-up table. Because the tear-off is cached, subsequent queries use the same look-up table. After all references to the IHyphenation interface are released, the look-up table can be released. If we had used a standard tear-off for this same functionality, a naïve implementation would have acquired the resources for the look-up table for each tear-off.

Aggregation: The Controlling Outer

As with tear-offs, aggregation enables you to separate the code for a single identity into multiple objects. However, whereas using tear-offs require shared source between the owner and the tear-off class, aggregation does not. The controlling outer and the controlling inner do not have to share the same server or even the same implementation language (although they do have to share the same apartment). If you like, you can consider an aggregated object a kind of “binary cached tear-off.” As with a cached tear-off, an aggregated instance’s lifetime and identity are subsumed by that of the controlling outer. Just like a cached tear-off, an aggregated instance must have a way to obtain the interface pointer of the controlling outer. In a tear-off, we pass the owner as a constructor argument. In aggregation, we do the same thing, but using the COM constructor that accepts a single, optional constructor argument – that is, the pUnkOuter parameter of IClassFactory::CreateInstance and its wrapper, CoCreateInstance:

 1interface IClassFactory : IUnknown {
 2HRESULT CreateInstance([in, unique]IUnknown* pUnkOuter,
 3[in] REFIID riid,
 4[out, iid_is(riid)] void **ppvObject);
 5HRESULT LockServer([in] BOOL fLock);
 6};
 7
 8WINOLEAPI CoCreateInstance([in] REFCLSID rclsid,
 9[in, unique]LPUNKNOWN pUnkOuter,
10[in] DWORD dwClsContext,
11[in] REFIID riid,
12[out, iid_is(riid)] LPVOID FAR* ppv);

In Chapter 4, “Objects in ATL,” I discussed how ATL supports aggregation as a controlled inner using CComAggObject (or CComPolyObject). In this chapter, I show you the four macros that ATL provides for the controlling outer in the aggregation relationship.

Planned Versus Blind Aggregation

After an aggregate is created, the controlling outer has two choices of how to exposed the interface(s) of the aggregate as its own. The first choice is planned aggregation. In planned aggregation, the controlling outer wants the inner to expose one of a set of interfaces known by the outer. Figure 6.2 illustrates planned aggregation.

Figure 6.2. Planned aggregation

_images/06atl02.jpg

The downside to this technique is that if the inner’s functionality grows, clients using the outer cannot gain access to the additional functionality. The upside is that this could be exactly what the outer had in mind. For example, consider the standard interface IPersist:

1interface IPersist : IUnknown {
2HRESULT GetClassID([out] CLSID *pClassID);
3}

If the outer were to blindly expose the inner’s implementation of IPersist, when the client called GetClassID, it would get the CLSID of the inner, not the outer. Because the client wants the outer object’s class identifier, we have again broken the spirit of the COM identity laws. Planned aggregation helps prevent this breach.

Blind aggregation, on the other hand, allows the outer’s functionality to grow with the inner’s, but it provides the potential for exposing identity information from the inner. For this reason, blind aggregation should be avoided. Figure 6.3 shows blind aggregation.

Figure 6.3. Blind aggregation

_images/06atl03.jpg

Manual Versus Automatic Creation

COM_INTERFACE_ENTRY_AGGREGATE and COM_INTERFACE_ENTRY_AGGREGATE_BLIND

ATL provides support for both planned and blind aggregation via the following two macros:

1#define COM_INTERFACE_ENTRY_AGGREGATE(iid, punk) \
2{ &iid, (DWORD_PTR)offsetof(_ComMapClass, punk), _Delegate },
3
4#define COM_INTERFACE_ENTRY_AGGREGATE_BLIND(punk) \
5{ NULL, (DWORD_PTR)offsetof(_ComMapClass, punk), _Delegate},

These macros assume that the aggregate has already been created manually and that the interface pointer is stored in the punk parameter to the macro. The _Delegate function forwards the QueryInterface request to that pointer:

 1static HRESULT WINAPI
 2CComObjectRootBase::
 3
 4_Delegate(
 5void* pv,
 6REFIID iid,
 7void** ppvObject,
 8DWORD dw)
 9{
10HRESULT hRes = E_NOINTERFACE;
11IUnknown* p = *(IUnknown**)((DWORD_PTR)pv + dw);
12if (p != NULL) hRes = p->QueryInterface(iid, ppvObject);
13return hRes;
14}

To use aggregation of an inner that has been manually created, the classes use COM_INTERFACE_ENTRY_AGGREGATE or COM_INTERFACE_ENTRY_AGGREGATE_BLIND in the interface map:

 1class CBeachBall :
 2  public CComObjectRootEx<CBeachBall>,
 3  public ISphere,
 4  public IRollableObject,
 5  public IPlaything {
 6public:
 7BEGIN_COM_MAP(CBeachBall)
 8  COM_INTERFACE_ENTRY(ISphere)
 9  COM_INTERFACE_ENTRY(IRollableObject)
10  COM_INTERFACE_ENTRY(IPlaything)
11COM_INTERFACE_ENTRY_AGGREGATE(IID_ILethalObject,
12m_spunkLethalness)
13COM_INTERFACE_ENTRY_AGGREGATE_BLIND(m_spunkAttitude)
14END_COM_MAP()
15DECLARE_GET_CONTROLLING_UNKNOWN()
16DECLARE_PROTECT_FINAL_CONSTRUCT()
17HRESULT FinalConstruct() {
18HESULT hr;
19hr = CoCreateInstance(CLSID_Lethalness,
20GetControllingUnknown(),
21CLSCTX_INPROC_SERVER,
22IID_IUnknown,
23(void**)&m_spunkLethalness);
24if( SUCCEEDED(hr) ) {
25hr = CoCreateInstance(CLSID_Attitude,
26GetControllingUnknown(),
27CLSCTX_INPROC_SERVER,
28IID_IUnknown,
29(void**)&m_spunkAttitude);
30}
31return hr;
32}
33void FinalRelease() {
34m_spunkLethalness.Release();
35m_spunkAttitude.Release();
36}
37...
38public:
39CComPtr<IUnknown> m_spunkLethalness;
40CComPtr<IUnknown> m_spunkAttitude;
41};

Notice that I have used the FinalConstruct method to create the aggregates so that failure stops the creation process. Notice also that because I’ve got a FinalConstruct that hands out interface pointers to the object being created, I’m using DECLARE_PROTECT_FINAL_CONSTRUCT to protect against premature destruction. I’ve also got a FinalRelease method to manually release the aggregate interface pointers to protect against double destruction. Aggregation was one of the chief motivations behind the multiphase construction of ATL-based COM objects, so it’s not surprising to see all the pieces used in this example.

However, one thing I’ve not yet mentioned is the DECLARE_GET_CONTROLLING_UNKNOWN macro. The controlling unknown is a pointer to the most controlling outer. Because aggregation can go arbitrarily deep, when aggregating, the outer needs to pass the pUnkOuter of the outermost object. To support this, ATL provides the GET_CONTROLLING_UNKNOWN macro to give an object the definition of a GetControllingUnknown function:

1#define DECLARE_GET_CONTROLLING_UNKNOWN() public: \
2virtual IUnknown* GetControllingUnknown() { \
3return GetUnknown(); }

You might question the value of this function because it simply forwards to GetUnknown, but notice that it’s virtual. If the object is actually being created as an aggregate while it is aggregating, GetControllingUnknown is overridden in CComContainedObject:

1template <class Base> class CComContainedObject : public Base {
2...
3IUnknown* GetControllingUnknown()
4{ return m_pOuterUnknown; }
5...
6};

So, if the object is standalone, GetControllingUnknown returns the IUnknown* of the object, but if the object is itself being aggregated, GetControllingUnknown returns the IUnknown* of the outermost outer.

COM_INTERFACE_ENTRY_AUTOAGGREGATE and COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND

If you’re not doing any initialization of the aggregates, sometimes it seems a waste to create them until (or unless) they’re needed. For automatic creation of aggregates on demand, ATL provides the following two macros:

 1#define COM_INTERFACE_ENTRY_AUTOAGGREGATE(iid, punk, clsid) \
 2{ &iid, \
 3(DWORD_PTR)&ATL::_CComCacheData< \
 4ATL::CComAggregateCreator<_ComMapClass, &clsid>, \
 5(DWORD_PTR)offsetof(_ComMapClass, punk)>::data, \
 6_Cache },
 7
 8#define COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND(punk, clsid) \
 9{ NULL, \
10(DWORD_PTR)&ATL::_CComCacheData< \
11ATL::CComAggregateCreator<_ComMapClass, &clsid>, \
12(DWORD_PTR)offsetof(_ComMapClass, punk)>::data, \
13_Cache },

The only new thing in these macros is the CComAggregateCreator, which simply performs the CoCreateInstance the first time the interface is requested:

 1template <class T, const CLSID* pclsid>
 2class CComAggregateCreator {
 3public:
 4static HRESULT WINAPI CreateInstance(void* pv, REFIID,
 5LPVOID* ppv) {
 6T* p = (T*)pv;
 7return CoCreateInstance(*pclsid, p->GetControllingUnknown(),
 8CLSCTX_INPROC, __uuidof(IUnknown), ppv);
 9}
10};

Using automatic creation simplifies the outer’s code somewhat:

 1class CBeachBall :
 2  public CComObjectRootEx<CBeachBall>,
 3  public ISphere,
 4  public IRollableObject,
 5  public IPlaything {
 6public:
 7BEGIN_COM_MAP(CBeachBall)
 8  COM_INTERFACE_ENTRY(ISphere)
 9  COM_INTERFACE_ENTRY(IRollableObject)
10  COM_INTERFACE_ENTRY(IPlaything)
11COM_INTERFACE_ENTRY_AUTOAGGREGATE(IID_ILethalObject,
12m_spunkLethalness, CLSID_Lethalness)
13COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND(m_spunkAttitude,
14CLSID_Attitude)
15END_COM_MAP()
16
17DECLARE_GET_CONTROLLING_UNKNOWN()
18
19  void FinalRelease() {
20    m_spunkLethalness.Release();
21    m_spunkAttitude.Release();
22  }
23...
24public:
25  CComPtr<IUnknown> m_spunkLethalness;
26  CComPtr<IUnknown> m_spunkAttitude;
27};

Although we no longer need to perform the creation in FinalConstruct, we’re still required to use DECLARE_GET_CONTROLLING_UNKNOWN and to provide storage for the aggregated interfaces. We still release the interfaces manually in FinalRelease, as well, to avoid double destruction.

Aggregating the Free Threaded Marshaler

The ATL Object Wizard directly supports one particularly interesting use of aggregation: aggregating the implementation of IMarshal provided by the Free Threaded Marshaler (FTM). Any object that aggregates the FTM is said to be an apartment-neutral object. Normally, passing an interface pointer between apartments, even in the same process, results in a proxy-stub pair. The proxy-stub pair maintains the concurrency and synchronization requirements of both the object and the client, but it also adds overhead. In-process objects that provide their ownsynchronization and prefer to snuggle up to the client without the overhead of the proxy-stub can aggregate the FTM. By aggregating the FTM, an object short-circuits the creation of the proxy-stub for in-process objects. Therefore, the object can be passed between apartments in the same address space without the overhead of a proxy-stub.

The wizard generates the following code when the Free Threaded Marshaler option is checked in the ATL Object Wizard:

 1class ATL_NO_VTABLE CBowlingBall :
 2public CComObjectRootEx<CComMultiThreadModel>,
 3public CComCoClass<CBowlingBall, &CLSID_BowlingBall>,
 4public IBowlingBall
 5{
 6public:
 7CBowlingBall() { m_pUnkMarshaler = NULL; }
 8
 9DECLARE_REGISTRY_RESOURCEID(IDR_BOWLINGBALL)
10DECLARE_GET_CONTROLLING_UNKNOWN()
11DECLARE_PROTECT_FINAL_CONSTRUCT()
12
13BEGIN_COM_MAP(CBowlingBall)
14COM_INTERFACE_ENTRY(IBowlingBall)
15COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler.p)
16END_COM_MAP()
17
18HRESULT FinalConstruct() {
19return CoCreateFreeThreadedMarshaler(GetControllingUnknown(),
20&m_pUnkMarshaler.p);
21}
22void FinalRelease() {
23m_pUnkMarshaler.Release();
24}
25
26CComPtr<IUnknown> m_pUnkMarshaler;
27...
28};

Because the CLSID of the FTM is not available, instead of using auto-creation, ATL uses CoCreateFreeThreadMarshaler to create an instance of the FTM in the FinalConstruct method.

FTM Danger, Will Robinson! Danger! Danger!

Aggregating the FTM is easy, so I should mention a couple of big responsibilities that you, the developer, accept by aggregating the FTM:

  • Apartment-neutral objects must be thread safeYou can mark your class ThreadingModel=Apartment all day long, but because your object can be passed freely between apartments in the same process and, therefore, can used simultaneously by multiple threads, you’d better use CComMultiThreadModel and at least object-level locking. Fortunately, the ATL Object Wizard makes the FTM option available for selection only if you choose a threading model of Both or Neutral.

  • Apartment-neutral objects are not aggregatable. Aggregating the FTM depends on being able to implement IMarshal. If the outer decides to implement IMarshal and doesn’t ask the inner object, the inner can no longer be apartment neutral.

  • Apartment-neutral objects cannot cache interface pointers. An apartment-neutral object is said to be apartment neutral because it doesn’t care which apartment it is accessed from. However, other objects that an apartment-neutral object uses might or might not also be apartment-neutral. Interface pointers to objects that aren’t apartment neutral can be used only in the apartment to which they belong. If you’re lucky, the apartment-neutral object attempting to cache and use an interface pointer from another apartment will have a pointer to a proxy. Proxies know when they are being accessed outside their apartments and return RPC_E_WRONG_THREAD for all such method calls. If you’re not so lucky, the apartment-neutral object obtains a raw interface pointer. Imagine the poor single-threaded object accessed simultaneously from multiple apartments as part of its duty to the apartment-neutral object. It will most likely work just fine until you need to give a demo to your biggest client.

The only safe way to cache interface pointers in an apartment-neutral object is as cookies obtained from the Global Interface Table (GIT). The GIT is a process-global object provided to map apartment-specific interface pointers to apartment-neutral cookies and back. The GIT was invented after the FTM and is provided in the third service pack to NT 4.0, the DCOM patch for Windows 95, and out of the box with Windows 98/2000/XP. If you’re aggregating the FTM and caching interface pointers, you must use the GIT. ATL provides the CComGITPtr class, discussed in Chapter 3, “ATL Smart Types,” to make dealing with the GIT easier.

For an in-depth discussion of the FTM, the GIT, and their use, read Essential COM (Addison-Wesley, 1997), by Don Box.

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

 1class CBigBeachBall :
 2  public CBeachBall,
 3  public IBigObject {
 4public:
 5BEGIN_COM_MAP(CBigBeachBall)
 6  COM_INTERFACE_ENTRY(IBigObject)
 7  // All entries from CBeachBall base?
 8END_COM_MAP()
 9...
10};

COM_INTERFACE_ENTRY_CHAIN

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:

1#define COM_INTERFACE_ENTRY_CHAIN(classname) \
2{ NULL, (DWORD_PTR)&ATL::_CComChainData<classname, \
3_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:

1static HRESULT WINAPI
2CComObjectRootBase::
3
4_Chain(void* pv, REFIID iid,
5void** ppvObject, DWORD dw) {
6_ATL_CHAINDATA* pcd = (_ATL_CHAINDATA*)dw;
7void* p = (void*)((DWORD_PTR)pv + pcd->dwOffset);
8return InternalQueryInterface(p, pcd->pFunc(), iid, ppvObject);
9}

If the _Chain function returns a failure – for example, if the base class doesn’t support the requested interface – the search continues with the next entry in the table:

 1class CBigBadBeachBall :
 2  public CBeachBall,
 3  public IBigObject,
 4  public IBadObject {
 5public:
 6BEGIN_COM_MAP(CBigBadBeachBall)
 7  COM_INTERFACE_ENTRY(IBigObject)
 8COM_INTERFACE_ENTRY_CHAIN(CBeachBall)
 9COM_INTERFACE_ENTRY(IBadObject)
10END_COM_MAP()
11...
12};

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:

1class CBetterBeachBall :
2  public CBeachBall {
3public:
4BEGIN_COM_MAP(CBetterBeachBall)
5COM_INTERFACE_ENTRY(IUnknown)
6COM_INTERFACE_ENTRY_CHAIN(CBeachBall)
7END_COM_MAP()
8...
9};

Just Say “No”

COM_INTERFACE_ENTRY_NOINTERFACE

Sometimes, you want to short-circuit the interface request by explicitly returning E_NOINTERFACE when a specific interface is requested. For this, ATL provides COM_INTERFACE_ENTRY_NOINTERFACE:

1#define COM_INTERFACE_ENTRY_NOINTERFACE(x) \
2{ &_ATL_IIDOF(x), NULL, _NoInterface },

The _NoInterface function does pretty much what you’d expect:

1static HRESULT WINAPI
2CComObjectRootBase::
3
4_NoInterface(void*, REFIID, void**,
5DWORD_PTR)
6{ return E_NOINTERFACE; }

This interface map macro is handy when you’ve got blind entries in the interface map, as in blind aggregation or chaining, and you want to remove functionality that the inner object or the base class provides. For example

 1class CBigNiceBeachBall :
 2  public CBeachBall,
 3  public IBigObject {
 4public:
 5BEGIN_COM_MAP(CBigNiceBeachBall)
 6  COM_INTERFACE_ENTRY(IBigObject)
 7COM_INTERFACE_ENTRY_NOINTERFACE(ILethalObject)
 8COM_INTERFACE_ENTRY_CHAIN(CBeachBall)
 9END_COM_MAP()
10...
11};

Debugging

COM_INTERFACE_ENTRY_BREAK

Sometimes you need to debug your QueryInterface implementation. Maybe you’re building a custom COM_MAP macro. Maybe you’re seeing weird behavior on some interfaces. Sometimes the easiest way to track down a problem is in the debugger. This is where COM_INTERFACE_ENTRY_BREAK comes in:

1#define COM_INTERFACE_ENTRY_BREAK(x) \
2{ &_ATL_IIDOF(x), NULL, _Break },

The _Break function outputs some helpful debugging information and calls DebugBreak:

1static HRESULT WINAPI
2CComObjectRootbase::
3
4_Break(void*, REFIID iid, void**, DWORD) {
5(iid);
6_ATLDUMPIID(iid, _T("Break due to QI for interface "), S_OK);
7DebugBreak();
8return S_FALSE;
9}

The call to DebugBreak is just like a breakpoint set in your debugger. It gives the active debugger the chance to take control of the process. When you’re debugging, you can set other breakpoints and continue executing.

Extensibility

COM_INTERFACE_ENTRY_FUNC and COM_INTERFACE_ENTRY_FUNC_BLIND

ATL provides two macros for putting raw entries into the interface map:

1#define COM_INTERFACE_ENTRY_FUNC(iid, dw, func) \
2{ &iid, dw, func },
3
4#define COM_INTERFACE_ENTRY_FUNC_BLIND(dw, func) \
5{ NULL, dw, func },

These macros are the universal back door to ATL’s implementation of QueryInterface. If you come up with another way of exposing COM interfaces, you can use these macros to achieve them, as long as it lives up to the laws of COM identity.

Direct Access to the this Pointer

One identity trick you can perform using COM_INTERFACE_ENTRY_FUNC was kicked around the ATL Mailing List for quite a while but was ultimately perfected by Don Box. (A slightly modified version of his solution is provided next.) In Chapter 4, “Objects in ATL,” I presented the CreateInstance static member functions of CComObject, CComAgg-Object, and CComPolyObject when using private initialization. The CreateInstance method performed the same job as a Creator would but returned a pointer to the this pointer of the object instead of to only one of the interfaces. This was useful for calling member functions or setting member data that was not exposed via interfaces. We used this technique because it was unsafe to perform a cast. However, why not make QueryInterface perform the cast safely? In other words, why not add an entry to the interface map that returns the object’s this pointer? Imagine a global function with the following implementation:

1inline
2HRESULT WINAPI _This(void* pv, REFIID iid,
3  void** ppvObject, DWORD) {
4  ATLASSERT(iid == IID_NULL);
5  *ppvObject = pv;
6  return S_OK;
7}

This function takes the first parameter, pv, which points to the object’s this pointer and hands it out directly in ppvObject. Notice also that this function does not AddRef the resultant interface pointer. Because it’s returning an object pointer, not an interface pointer, it’s not subject to the laws of COM. Remember, the this pointer is useful only within the server. To make sure that any out-of-apartment calls to QueryInterface fail, be sure to pick an interface ID without a proxy-stub, such as IID_NULL.

For example, imagine implementations of the following interfaces, creating a simple object model:

1interface IBalloonMan : IUnknown {
2    HRESULT CreateBalloon(long rgbColor, IBalloon** ppBalloon);
3    HRESULT SwitchColor(IBalloon* pBalloon, long rgbColor);
4};
5
6interface IBalloon : IUnknown {
7    [propget] HRESULT Color([out, retval] long *pVal);
8};

Notice that the balloon’s color can’t be changed via IBalloon, but the implementation of IBalloonMan can give you a balloon of the color you want. If the implementations of IBalloonMan and IBalloon share the same server, the implementation of IBalloon can expose its this pointer via the _This function like this:

 1class ATL_NO_VTABLE CBalloon :
 2  public CComObjectRootEx<CComSingleThreadModel>,
 3  public CComCoClass<CBalloon>,
 4  public IBalloon {
 5public:
 6DECLARE_REGISTRY_RESOURCEID(IDR_BALLOON)
 7DECLARE_NOT_AGGREGATABLE(CBalloon)
 8
 9DECLARE_PROTECT_FINAL_CONSTRUCT()
10
11BEGIN_COM_MAP(CBalloon)
12  COM_INTERFACE_ENTRY(IBalloon)
13COM_INTERFACE_ENTRY_FUNC(IID_NULL, 0, _This)
14END_COM_MAP()
15    // IBalloon
16public:
17  STDMETHOD(get_Color)(/*[out, retval]*/ long *pVal);
18
19private:
20  COLORREF m_rgbColor;
21  friend class CBalloonMan;
22};

Because CBalloonMan is a friend of CBalloon, CBalloonMan can set the private color data member of CBalloon, assuming that it can obtain the object’s this pointer. CBalloon’s special entry for IID_NULL lets CBalloonMan do just that:

 1STDMETHODIMP CBalloonMan::CreateBalloon(long rgbColor,
 2  IBalloon** ppBalloon) {
 3  // Create balloon
 4  *ppBalloon = 0;
 5  HRESULT     hr = CBalloon::CreateInstance(0, ppBalloon);
 6
 7  if( SUCCEEDED(hr) ) {
 8// Use backdoor to acquire CBalloon's this pointer
 9CBalloon* pBalloonThis = 0;
10hr = (*ppBalloon)->QueryInterface(IID_NULL,
11(void**)&pBalloonThis);
12if( SUCCEEDED(hr) ) {
13       // Use CBalloon's this pointer for private initialization
14       pBalloonThis->m_rgbColor = rgbColor;
15    }
16  }
17  return hr;
18}

The benefit to this technique over the private initialization technique that the CreateInstance member function of CComObject et al exposes is that the this pointer can be obtained after creation:

 1STDMETHODIMP CBalloonMan::SwitchColor(IBalloon* pBalloon,
 2  long rgbColor) {
 3// Use backdoor to acquire CBalloon's this pointer
 4CBalloon*   pBalloonThis = 0;
 5HRESULT hr = pBalloon->QueryInterface(IID_NULL, (void**)&pBalloonThis);
 6if (SUCCEEDED(hr)) {
 7    hr = pBalloonThis->m_rgbColor = rgbColor;
 8  }
 9  return hr;
10}

Clearly, this technique is a back-door hack with limited usefulness; it should not be used to subvert the binary boundary between client and object. However, it does have its own special charm. For objects that share the same apartment in the same server, it’s a valid way to discover just who is who. If you find yourself using this technique, you might find the following macro to be a useful shortcut:

1#define COM_INTERFACE_ENTRY_THIS() \
2        COM_INTERFACE_ENTRY_FUNC(IID_NULL, 0, _This)

Per-Object Interfaces

Sometimes it’s useful to handle interfaces on a per-object basis instead of a per-class basis. Another friend of mine, Martin Gudgin, provided the following example. If you want to implement something known as a “smart proxy,” you have to keep track of the list of interfaces each object supports, and you might not know what those are until runtime. Each smart proxy object has its own list of interfaces, which can easily be managed by a member function. Unfortunately, interface maps can’t hold member functions (believe me, I tried). However, you can use a combination of COM_INTERFACE_ENTRY_FUNC_BLIND and a static member function to perform the forwarding to a member function:

 1class ATL_NO_VTABLE CSmartProxy :
 2  public CComObjectRootEx<CComSingleThreadModel>,
 3  public CComCoClass<CSmartProxy, &CLSID_SmartProxy>,
 4  public IUnknown
 5{
 6public:
 7DECLARE_REGISTRY_RESOURCEID(IDR_SMARTPROXY)
 8DECLARE_PROTECT_FINAL_CONSTRUCT()
 9
10BEGIN_COM_MAP(CSmartProxy)
11  COM_INTERFACE_ENTRY(IUnknown)
12COM_INTERFACE_ENTRY_FUNC_BLIND(0, _QI)
13END_COM_MAP()
14
15public:
16static HRESULT WINAPI _QI(void* pv, REFIID iid,
17void** ppvObject, DWORD) {
18// Forward to QI member function
19return ((CSmartProxy*)pv)->QI(iid, ppvObject);
20}
21// Per-object implementation of QI
22HRESULT QI(REFIID riid, void** ppv);
23};

Of course, you might wonder why you’d go to all this trouble to get back to the per-object implementation of QueryInterface that you’ve come to know and love, but that’s ATL.

Summary

Even when you understand and commit to the laws of COM identity, you’ll find that they aren’t very restrictive. Prefer multiple inheritance, but do not feel that ATL limits you to that technique. You can extend the interface map to support any identity trick that ATL doesn’t support directly.