Chapter 4. Objects in ATL

ATL’s fundamental support for COM can be split into two pieces: objects and servers. This chapter covers classes and concentrates on how IUnknown is implemented as related to threading and various COM identity issues, such as standalone versus aggregated objects. The next chapter focuses on how to expose classes from COM servers.

Implementing IUnknown

A COM object has one responsibility: to implement the methods of IUnknown. Those methods perform two services, lifetime management and runtime type discovery, as follows:

1interface IUnknown {
2  // runtime type discovery
3  HRESULT QueryInterface([in] REFIID riid,
4                         [out, iid_is(riid)] void **ppv);
5
6  // lifetime management
7  ULONG AddRef();
8  ULONG Release();
9}

COM allows every object to implement these methods as it chooses (within certain restrictions, as described in Chapter 5, “COM Servers”). The canonical implementation is as follows:

 1// Server lifetime management
 2extern void ServerLock();
 3extern void ServerUnlock();
 4
 5class CPenguin : public IBird, public ISnappyDresser {
 6public:
 7  CPenguin() : m_cRef(0) { ServerLock(); }
 8  virtual ~CPenguin()    { ServerUnlock(); }
 9
10  // IUnknown methods
11  STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {
12      if( riid == IID_IBird || riid == IID_IUnknown )
13          *ppv = static_cast<IBird*>(this);
14      else if( riid == IID_ISnappyDresser )
15          *ppv = static_cast<ISnappyDresser*>(this);
16      else *ppv = 0;
17
18      if( *ppv ) {
19          reinterpret_cast<IUnknown*>(*ppv)->AddRef();
20          return S_OK;
21      }
22
23      return E_NOINTERFACE;
24  }
25
26  ULONG AddRef()
27  { return InterlockedIncrement(&m_cRef); }
28
29  ULONG Release() {
30      ULONG l = InterlockedDecrement(&m_cRef);
31      if( l == 0 ) delete this;
32      return l;
33  }
34
35  // IBird and ISnappyDresser methods...
36private:
37    ULONG m_cRef;
38};

This implementation of IUnknown is based on several assumptions:

  1. The object is heap-based because it removes itself using the delete operator. Furthermore, the object’s outstanding references completely govern its lifetime. When it has no more references, it deletes itself.

  2. The object is capable of living in a multithread apartment because it manipulates the reference count in a thread-safe manner. Of course, the other methods must be implemented in a thread-safe manner as well for the object to be fully thread safe.

  3. The object is standalone and cannot be aggregated because it does not cache a reference to a controlling outer, nor does it forward the methods of IUnknown to a controlling outer.

  4. The object exposes its interfaces using multiple inheritance.

  5. The existence of the object keeps the server running. The constructor and the destructor are used to lock and unlock the server, respectively.

These common assumptions are not the only possibilities. Common variations include the following:

  1. An object can be global and live for the life of the server. Such objects do not need a reference count because they never delete themselves.

  2. An object might not need to be thread safe because it might be meant to live only in a single-threaded apartment.

  3. An object can choose to allow itself to be aggregated as well as, or instead of, supporting standalone activation.

  4. An object can expose interfaces using other techniques besides multiple inheritance, including nested composition, tear-offs, and aggregation.

  5. You might not want the existence of an object to force the server to keep running. This is common for global objects because their mere existence prohibits the server from unloading.

Changing any of these assumptions results in a different implementation of IUnknown, although the rest of the object’s implementation is unlikely to change much (with the notable exception of thread safety). These implementation details of IUnknown tend to take a very regular form and can be encapsulated into C++ classes. Frankly, we’d really like to use someone else’s tested code and be able to change our minds later without a great deal of effort. We’d also like this boilerplate code to be easily separated from the actual behavior of our objects so that we can focus on our domain-specific implementation. ATL was designed from the ground up to provide just this kind of functionality and flexibility.

The Layers of ATL

ATL’s support for building COM objects is separated into several layers, as shown in Figure 4.1.

Figure 4.1. The layers of ATL

_images/04atl01.jpg

These layers break down into services exposed by ATL for building objects:

  1. CComObjectRootEx uses a CComXxxThreadModel to provide “just thread-safe enough” object lifetime management and object locking.

  2. CComObjectRootBase and CComObjectRootEx provide helper functions used in implementing IUnknown.

  3. Your class, which derives from CComObjectRootEx, also derives from any interfaces it wants to implement, as well as providing the method implementations. You or one of the ATL IXxxImpl classes can provide method implementations.

  4. CComObject et al, provides the actual implementation of the methods of IUnknown in a way consistent with your desires for object and server lifetime management requirements. This final layer actually derives from your class.

Your choice of base classes and the most derived class determines the way the methods of IUnknown are implemented. If your choices change, using different classes at compile time (or runtime) will change how ATL implements IUnknown, independently of the rest of the behavior of your object. The following sections explore each layer of ATL.

Threading Model Support

Just Enough Thread Safety

The thread-safe implementation of AddRef and Release shown previously might be overkill for your COM objects. For example, if instances of a specific class will live in only a single-threaded apartment, there’s no reason to use the thread-safe Win32 functions InterlockedIncrement and InterlockedDecrement. For single-threaded objects, the following implementation of AddRef and Release is more efficient:

 1class Penquin {
 2...
 3  ULONG AddRef()
 4  { return ++m_cRef; }
 5
 6  ULONG Release() {
 7      ULONG l = m_cRef;
 8      if( l == 0 ) delete this;
 9      return l;
10  }
11...
12};

Using the thread-safe Win32 functions also works for single-threaded objects, but unnecessary thread safety requires extra overhead. For this reason, ATL provides three classes, CComSingleThreadModel, CComMultiThreadModel, and CComMultiThreadModelNoCS. These classes provide two static member functions, Increment and Decrement, for abstracting away the differences between managing an object’s lifetime count in a multithreaded manner versus a single-threaded one. The two versions of these functions are as follows (notice that both CComMultiThreadModel and CComMultiThreadModelNoCS have identical implementations of these functions):

 1class CComSingleThreadModel {
 2  static ULONG WINAPI Increment(LPLONG p) { return ++(*p); }
 3  static ULONG WINAPI Decrement(LPLONG p) { return (*p); }
 4  ...
 5};
 6
 7class CComMultiThreadModel {
 8  static ULONG WINAPI Increment(LPLONG p) { return InterlockedIncrement(p); }
 9  static ULONG WINAPI Decrement(LPLONG p) { return InterlockedDecrement(p); }
10  ...
11};
12class CComMultiThreadModelNoCS {
13  static ULONG WINAPI Increment(LPLONG p) { return InterlockedIncrement(p); }
14  static ULONG WINAPI Decrement(LPLONG p) { return InterlockedDecrement(p); }
15  ...
16};

Using these classes, you can parameterize [1] the class to give a “just thread-safe enough”AddRef and Release implementation:

 1template <typename ThreadModel>
 2class Penquin {
 3...
 4  ULONG AddRef()
 5  { return ThreadModel::Increment(&m_cRef); }
 6
 7  ULONG Release() {
 8      ULONG l = ThreadModel::Decrement(&m_cRef);
 9      if( l == 0 ) delete this;
10      return l;
11  }
12...
13};

Now, based on our requirements for the CPenguin class, we can make it just thread-safe enough by supplying the threading model class as a template parameter:

1// Let's make a thread-safe CPenguin
2CPenguin* pobj = new CPenguin<CComMultiThreadModel>( );

Instance Data Synchronization

When you create a thread-safe object, protecting the object’s reference count isn’t enough. You also have to protect the member data from multithreaded access. One popular method for protecting data that multiple threads can access is to use a Win32 critical section object, as shown here:

 1template <typename ThreadModel>
 2class CPenguin {
 3public:
 4  CPenguin() {
 5    ServerLock();
 6    InitializeCriticalSection(&m_cs);
 7  }
 8
 9  ~CPenguin() { ServerUnlock(); DeleteCriticalSection(&m_cs); }
10
11  // IBird
12  STDMETHODIMP get_Wingspan(long* pnWingspan) {
13    Lock(); // Lock out other threads during data read
14    *pnWingSpan = m_nWingspan;
15    Unlock();
16    return S_OK;
17  }
18
19  STDMETHODIMP put_Wingspan(long nWingspan) {
20    Lock(); // Lock out other threads during data write
21    m_nWingspan = nWingspan;
22    Unlock();
23    return S_OK;
24  }
25  ...
26private:
27  CRITICALSECTION m_cs;
28
29  void Lock() { EnterCriticalSection(&m_cs); }
30  void Unlock() { LeaveCriticalSection(&m_cs); }
31};

Notice that before reading or writing any member data, the CPenguin object enters the critical section, locking out access by other threads. This coarse-grained, object-level locking keeps the scheduler from swapping in another thread that could corrupt the data members during a read or a write on the original thread. However, object-level locking doesn’t give you as much concurrency as you might like. If you have only one critical section per object, one thread might be blocked trying to increment the reference count while another is updating an unrelated member variable. A greater degree of concurrency requires more critical sections, allowing one thread to access one data member while a second thread accesses another. Be careful using this kind of finer-grained synchronization – it often leads to deadlock:

 1class CZax : public IZax {
 2public:
 3  ...
 4  // IZax
 5  STDMETHODIMP GoNorth() {
 6      EnterCriticalSection(&m_cs1); // Enter cs1...
 7      EnterCriticalSection(&m_cs2); // ...then enter cs2
 8      // Go north...
 9      LeaveCriticalSection(&m_cs2);
10      LeaveCriticalSection(&m_cs1);
11  }
12
13  STDMETHODIMP GoSouth() {
14      EnterCriticalSection(&m_cs2); // Enter cs2...
15      EnterCriticalSection(&m_cs1); // ...then enter cs1
16      // Go south...
17      LeaveCriticalSection(&m_cs1);
18      LeaveCriticalSection(&m_cs2);
19  }
20  ...
21private:
22    CRITICAL_SECTION m_cs1;
23    CRITICAL_SECTION m_cs2;
24};

Imagine that the scheduler let the northbound Zax [2] thread enter the first critical section and then swapped in the southbound Zax thread to enter the second critical section. If this happened, neither Zax could enter the other critical section; therefore, neither Zax thread would be able to proceed. This would leave them deadlocked while the world went on without them. Try to avoid this. [3]

Whether you decide to use object-level locking or finer-grained locking, critical sections are handy. ATL provides four class wrappers that simplify their use: CComCriticalSection, CComAutoCriticalSection, CComSafeDeleteCriticalSection, and CComAutoDeleteCriticalSection.

 1class CComCriticalSection {
 2public:
 3    CComCriticalSection() {
 4        memset(&m_sec, 0, sizeof(CRITICAL_SECTION));
 5    }
 6    ~CComCriticalSection() { }
 7    HRESULT Lock() {
 8        EnterCriticalSection(&m_sec);
 9        return S_OK;
10    }
11
12    HRESULT Unlock() {
13        LeaveCriticalSection(&m_sec);
14        return S_OK;
15    }
16
17    HRESULT Init() {
18        HRESULT hRes = E_FAIL;
19        __try {
20            InitializeCriticalSection(&m_sec);
21            hRes = S_OK;
22        }
23        // structured exception may be raised in
24        // low memory situations
25        __except(STATUS_NO_MEMORY == GetExceptionCode()) {
26            hRes = E_OUTOFMEMORY;
27        }
28
29        return hRes;
30    }
31
32    HRESULT Term() {
33        DeleteCriticalSection(&m_sec);
34        return S_OK;
35    }
36    CRITICAL_SECTION m_sec;
37};
38
39class CComAutoCriticalSection : public CComCriticalSection {
40public:
41    CComAutoCriticalSection() {
42        HRESULT hr = CComCriticalSection::Init();
43        if (FAILED(hr))
44            AtlThrow(hr);
45    }
46    ~CComAutoCriticalSection() {
47        CComCriticalSection::Term();
48    }
49private:
50    // Not implemented. CComAutoCriticalSection::Init
51    // should never be called
52    HRESULT Init();
53    // Not implemented. CComAutoCriticalSection::Term
54    // should never be called
55    HRESULT Term();
56};
57
58class CComSafeDeleteCriticalSection
59    : public CComCriticalSection {
60public:
61    CComSafeDeleteCriticalSection(): m_bInitialized(false) { }
62
63    ~CComSafeDeleteCriticalSection() {
64        if (!m_bInitialized) { return; }
65        m_bInitialized = false;
66        CComCriticalSection::Term();
67    }
68
69    HRESULT Init() {
70        ATLASSERT( !m_bInitialized );
71        HRESULT hr = CComCriticalSection::Init();
72        if (SUCCEEDED(hr)) {
73            m_bInitialized = true;
74        }
75        return hr;
76    }
77
78    HRESULT Term() {
79        if (!m_bInitialized) { return S_OK; }
80        m_bInitialized = false;
81        return CComCriticalSection::Term();
82    }
83
84    HRESULT Lock() {
85        ATLASSUME(m_bInitialized);
86        return CComCriticalSection::Lock();
87    }
88
89private:
90    bool m_bInitialized;
91};
92
93class CComAutoDeleteCriticalSection : public CComSafeDeleteCriticalSection {
94private:
95    // CComAutoDeleteCriticalSection::Term should never be called
96    HRESULT Term() ;
97};

Notice that CComCriticalSection does not use its constructor or destructor to initialize and delete the contained critical section. Instead, it contains Init and Term functions for this purpose. CComAutoCriticalSection, on the other hand, is easier to use because it automatically creates the critical section in its constructor and destroys it in the destructor.

CComSafeDeleteCriticalSection does half that job; it doesn’t create the critical section until the Init method is called, but it always deletes the critical section (if it exists) in the destructor. You also have the option of manually calling Term if you want to explicitly delete the critical section ahead of the object’s destruction. CComAutoDeleteCriticalSection, on the other hand, blocks the Term method by simply declaring it but never defining it; calling CComAutoDeleteCriticalSection::Term gives you a linker error. These classes were useful before ATL was consistent about supporting construction for global and static variables, but these classes are largely around for historical reasons at this point; you should prefer CComAutoCriticalSection.

Using a CComAutoCriticalSection in our CPenguin class simplifies the code a bit:

 1template <typename ThreadModel>
 2class CPenguin {
 3public:
 4  // IBird methods Lock() and Unlock() as before...
 5...
 6private:
 7  CComAutoCriticalSection m_cs;
 8
 9  void Lock() { m_cs.Lock(); }
10  void Unlock() { m_cs.Unlock(); }
11};

Note that with both CComAutoCriticalSection and CComCriticalSection, the user must take care to explicitly call Unlock before leaving a section of code that has been protected by a call to Lock. In the presence of code that might throw exceptions (which a great deal of ATL framework code now does), this can be difficult to do because each piece of code that can throw an exception represents a possible exit point from the function. CComCritSecLock addresses this issue by automatically locking and unlocking in its constructor and destructor. CComCritSecLock is parameterized by the lock type so that it can serve as a wrapper for CComCriticalSection or CComAutoCriticalSection.

 1template< class TLock >
 2class CComCritSecLock {
 3public:
 4      CComCritSecLock( TLock& cs, bool bInitialLock = true );
 5      ~CComCritSecLock() ;
 6
 7      HRESULT Lock() ;
 8      void Unlock() ;
 9
10// Implementation
11private:
12      TLock& m_cs;
13      bool m_bLocked;
14...
15};
16
17template< class TLock >
18inline CComCritSecLock< TLock >::CComCritSecLock(
19    TLock& cs,bool bInitialLock )
20    : m_cs( cs ), m_bLocked( false ) {
21      if( bInitialLock ) {
22            HRESULT hr;
23            hr = Lock();
24            if( FAILED( hr ) ) { AtlThrow( hr ); }
25      }
26}
27
28template< class TLock >
29inline CComCritSecLock< TLock >::~CComCritSecLock() {
30      if( m_bLocked ) { Unlock(); }
31}
32
33template< class TLock >
34inline HRESULT CComCritSecLock< TLock >::Lock() {
35      HRESULT hr;
36      ATLASSERT( !m_bLocked );
37      hr = m_cs.Lock();
38      if( FAILED( hr ) ) { return( hr ); }
39      m_bLocked = true;
40      return( S_OK );
41}
42
43template< class TLock >
44inline void CComCritSecLock< TLock >::Unlock() {
45      ATLASSERT( m_bLocked );
46      m_cs.Unlock();
47      m_bLocked = false;
48}

If the bInitialLock parameter to the constructor is true, the contained critical section is locked upon construction. In normal use on the stack, this is exactly what you want, which is why true is the default. However, as usual with constructors, if something goes wrong, you don’t have an easy way to return the failure code. If you need to know whether the lock failed, you can pass false instead and then call Lock explicitly. Lock returns the HRESULT from the lock operation. This class ensures that the contained critical section is unlocked whenever an instance of this class leaves scope because the destructor automatically attempts to call Unlock if it detects that the instance is currently locked.

Notice that our CPenguin class is still parameterized by the threading model. There’s no sense in protecting our member variables in the single-threaded case. Instead, it would be handy to have another critical section class that could be used in place of CComCriticalSection or CComAutoCriticalSection. ATL provides the CComFakeCriticalSection class for this purpose:

1class CComFakeCriticalSection {
2public:
3    HRESULT Lock() { return S_OK; }
4    HRESULT Unlock() { return S_OK; }
5    HRESULT Init() { return S_OK; }
6    HRESULT Term() { return S_OK; }
7};

Given CComFakeCriticalSection, we could further parameterize the CPenguin class by adding another template parameter, but this is unnecessary. The ATL threading model classes already contain type definitions that map to a real or fake critical section, based on whether you’re doing single or multithreading:

 1class CcomSingleThreadModel {
 2public:
 3    static ULONG WINAPI Increment(LPLONG p) {return ++(*p);}
 4    static ULONG WINAPI Decrement(LPLONG p) {return (*p);}
 5    typedef CComFakeCriticalSection AutoCriticalSection;
 6    typedef CComFakeCriticalSection AutoDeleteCriticalSection;
 7    typedef CComFakeCriticalSection CriticalSection;
 8    typedef CComSingleThreadModel ThreadModelNoCS;
 9};
10
11class CcomMultiThreadModel {
12public:
13    static ULONG WINAPI Increment(LPLONG p) {return InterlockedIncrement(p);}
14    static ULONG WINAPI Decrement(LPLONG p) {return InterlockedDecrement(p);}
15    typedef CComAutoCriticalSection AutoCriticalSection;
16    typedef CComAutoDeleteCriticalSection
17        AutoDeleteCriticalSection;
18    typedef CComCriticalSection CriticalSection;
19    typedef CComMultiThreadModelNoCS ThreadModelNoCS;
20};
21
22class CcomMultiThreadModelNoCS {
23public:
24    static ULONG WINAPI Increment(LPLONG p) {return InterlockedIncrement(p);}
25    static ULONG WINAPI Decrement(LPLONG p) {return InterlockedDecrement(p);}
26    typedef CComFakeCriticalSection AutoCriticalSection;
27    typedef CComFakeCriticalSection AutoDeleteCriticalSection;
28    typedef CComFakeCriticalSection CriticalSection;
29    typedef CComMultiThreadModelNoCS ThreadModelNoCS;
30};

These type definitions enable us to make the CPenguin class just thread safe enough for both the object’s reference count and course-grained object synchronization:

 1template <typename ThreadingModel>
 2class CPenguin {
 3public:
 4    // IBird methods as before...
 5...
 6private:
 7    ThreadingModel::AutoCriticalSection m_cs;
 8
 9    void Lock() { m_cs.Lock(); }
10    void Unlock() { m_cs.Unlock(); }
11};

This technique enables you to provide the compiler with operations that are just thread safe enough. If the threading model is CComSingleThreadModel, the calls to Increment and Decrement resolve to operator++ and operator--, and the Lock and Unlock calls resolve to empty inline functions.

If the threading model is CComMultiThreadModel, the calls to Increment and Decrement resolve to calls to InterlockedIncrement and InterlockedDecrement. The Lock and Unlock calls resolve to calls to EnterCriticalSection and LeaveCriticalSection.

Finally, if the model is CComMultiThreadModelNoCS, the calls to Increment and Decrement are thread safe, but the critical section is fake, just as with CComSingleThreadModel. CComMultiThreadModelNoCS is designed for multithreaded objects that eschew object-level locking in favor of a more fine-grained scheme. Table4.1 shows how the code is expanded based on the threading model class you use:

Table 4.1. Expanded Code Based on Threading Model Class

CcomSingleThreadModel

CComMultiThreadModel

CComMultiThreadModelNoCS

TM::Increment

++

Interlocked-Increment

Interlocked-Increment

TM::Decrement

--

Interlocked-Decrement

Interlocked-Decrement

TM::AutoCriticalSection::Lock

(Nothing)

EnterCritical-Section

(Nothing)

TM::AutoCriticalSection::Unlock

(Nothing)

LeaveCritical-Section

(Nothing)

The Server’s Default Threading Model

ATL-based servers have a concept of a “default” threading model for things that you don’t specify directly. To set the server’s default threading model, you define one of the following symbols: _ATL_SINGLE_THREADED, _ATL_APARTMENT_THREADED, or _ATL_FREE_THREADED. If you don’t specify one of these symbols, ATL assumes _ATL_FREE_THREADED. However, the ATL Project Wizard defines _ATL_APARTMENT_THREADED in the generated stdafx.h file. ATL uses these symbols to define two type definitions:

 1#if defined(_ATL_SINGLE_THREADED)
 2...
 3    typedef CComSingleThreadModel CComObjectThreadModel;
 4    typedef CComSingleThreadModel CComGlobalsThreadModel;
 5
 6#elif defined(_ATL_APARTMENT_THREADED)
 7...
 8    typedef CComSingleThreadModel CComObjectThreadModel;
 9    typedef CComMultiThreadModel CComGlobalsThreadModel;
10
11#elif defined(_ATL_FREE_THREADED)
12...
13    typedef CComMultiThreadModel CComObjectThreadModel;
14    typedef CComMultiThreadModel CComGlobalsThreadModel;
15...
16#endif

Internally, ATL uses CComObjectThreadModel to protect instance data and CComGlobalsThreadModel to protect global and static data. Because the usage is difficult to override in some cases, you should make sure that ATL is compiled using the most protective threading model of any of the classes in your server. In practice, this means you should change the wizard-generated _ATL_APARTMENT_THREADED symbol to _ATL_FREE_THREADED if you have even one multithreaded class in your server.

The Core of IUnknown

Standalone Reference Counting

To encapsulate the Lock and Unlock methods as well as the “just thread-safe enough” reference counting, ATL provides the CComObjectRootEx base class, parameterized by the desired threading model [4] :

 1template <class ThreadModel>
 2class CComObjectRootEx : public CComObjectRootBase {
 3public:
 4    typedef ThreadModel _ThreadModel;
 5    typedef typename _ThreadModel::AutoCriticalSection _CritSec;
 6    typedef typename _ThreadModel::AutoDeleteCriticalSection _AutoDelCritSec;
 7    typedef CComObjectLockT<_ThreadModel> ObjectLock;
 8
 9    ~CComObjectRootEx() {}
10
11    ULONG InternalAddRef() {
12        ATLASSERT(m_dwRef != -1L);
13        return _ThreadModel::Increment(&m_dwRef);
14    }
15    ULONG InternalRelease() {
16#ifdef _DEBUG
17        LONG nRef = _ThreadModel::Decrement(&m_dwRef);
18        if (nRef < -(LONG_MAX / 2)) {
19            ATLASSERT(0 &&
20            _T("Release called on a pointer that has"
21               " already been released"));
22        }
23        return nRef;
24#else
25        return _ThreadModel::Decrement(&m_dwRef);
26#endif
27    }
28
29    HRESULT _AtlInitialConstruct() { return m_critsec.Init(); }
30    void Lock() {m_critsec.Lock();}
31    void Unlock() {m_critsec.Unlock();}
32private:
33    _AutoDelCritSec m_critsec;
34};
35
36template <>
37class CComObjectRootEx<CComSingleThreadModel>
38    : public CComObjectRootBase {
39public:
40    typedef CComSingleThreadModel _ThreadModel;
41    typedef _ThreadModel::AutoCriticalSection _CritSec;
42    typedef _ThreadModel::AutoDeleteCriticalSection
43        _AutoDelCritSec;
44    typedef CComObjectLockT<_ThreadModel> ObjectLock;
45
46    ~CComObjectRootEx() {}
47
48    ULONG InternalAddRef() {
49        ATLASSERT(m_dwRef != -1L);
50        return _ThreadModel::Increment(&m_dwRef);
51    }
52    ULONG InternalRelease() {
53#ifdef _DEBUG
54        long nRef = _ThreadModel::Decrement(&m_dwRef);
55        if (nRef < -(LONG_MAX / 2)) {
56            ATLASSERT(0 && _T("Release called on a pointer "
57                      "that has already been released"));
58        }
59        return nRef;
60#else
61        return _ThreadModel::Decrement(&m_dwRef);
62#endif
63    }
64
65    HRESULT _AtlInitialConstruct() { return S_OK; }
66
67    void Lock() {}
68    void Unlock() {}
69};

ATL classes derive from CComObjectRootEx and forward AddRef and Release calls to the InternalAddRef and InternalRelease methods when the object is created standalone (that is, not aggregated). Note that InternalRelease checks the decremented reference count against the somewhat odd-looking value (LONG_MAX / 2). The destructor of CComObject (or one of its alternatives, discussed a bit later) sets the reference count to this value. The ATL designers could have used a different value here, but basing the value on LONG_MAX makes it unlikely that such a reference count could be reached under normal circumstances. Dividing LONG_MAX by 2 ensures that the resulting value can’t mistakenly be reached by wrapping around from 0. InternalRelease simply checks the reference count against this value to see if you’re trying to call Release on an object that has already been destroyed. If so, an assert is issued in debug builds.

The template specialization for CComSingleThreadModel demonstrates the “just safe enough” multithreading. When used in a single-threaded object, the Lock and Unlock methods do nothing, and no critical section object is created.

With the Lock and Unlock methods so readily available in the base class, you might be tempted to write the following incorrect code:

 1class CPenguin
 2    : public CComObjectRootEx<CComMultiThreadModel>, ... {
 3    STDMETHODIMP get_Wingspan(long* pnWingspan) {
 4      Lock();
 5      if( !pnWingspan ) return E_POINTER; // Forgot to Unlock
 6      *pnWingSpan = m_nWingspan;
 7      Unlock();
 8      return S_OK;
 9    }
10    ...
11};

To help you avoid this kind of mistake, CComObjectRootEx provides a type definition for a class called ObjectLock, based on CComObjectLockT parameterized by the threading model:

 1template <class ThreadModel>
 2class CcomObjectLockT {
 3public:
 4    CComObjectLockT(CComObjectRootEx<ThreadModel>* p) {
 5        if (p)
 6            p->Lock();
 7        m_p = p;
 8    }
 9
10    ~CComObjectLockT() {
11        if (m_p)
12            m_p->Unlock();
13    }
14    CComObjectRootEx<ThreadModel>* m_p;
15};
16
17template <>
18class CComObjectLockT<CComSingleThreadModel> {
19public:
20    CComObjectLockT(CComObjectRootEx<CComSingleThreadModel>*) {}
21    ~CComObjectLockT() {}
22};

Instances of CComObjectLockT Lock the object passed to the constructor and Unlock it upon destruction. The ObjectLock type definition provides a convenient way to write code that will properly release the lock regardless of the return path:

 1class CPenguin
 2    : public CComObjectRootEx<CComMultiThreadModel>, ... {
 3    STDMETHODIMP get_Wingspan(long* pnWingspan) {
 4      ObjectLock lock(this);
 5      if( !pnWingspan ) return E_POINTER; // Unlock happens as
 6                                          // stack unwinds
 7      *pnWingSpan = m_nWingspan;
 8       return S_OK;
 9    }
10    ...
11};

Of course, the specialization for CComSingleThreadModel ensures that in the single-threaded object, no locking is done. This is useful when you’ve changed your threading model; you don’t pay a performance penalty for using an ObjectLock if you don’t actually need one.

Table-Driven QueryInterface

In addition to “just thread-safe enough” implementations of AddRef and Release for standalone COM objects, CComObjectRootEx (via its base class, CComObjectRootBase) provides a static, table-driven implementation of QueryInterface called InternalQueryInterface:

1static HRESULT WINAPI
2CComObjectRootBase::InternalQueryInterface(
3    void*                    pThis,
4    const _ATL_INTMAP_ENTRY* pEntries,
5    REFIID                   iid,
6    void**                   ppvObject);

This function’s job is to use the this pointer of the object, provided as the pThis parameter, and the requested interface to fill the ppvObject parameter with a pointer to the appropriate virtual function table pointer (vptr). It does this using the pEntries parameter, a zero-terminated array of _ATL_INTMAP_ENTRY structures:

1struct _ATL_INTMAP_ENTRY {
2    const IID*           piid;
3    DWORD                dw;
4    _ATL_CREATORARGFUNC* pFunc;
5};

Each interface exposed from a COM object is one entry in the interface map, which is a class static array of _ATL_INTMAP_ENTRY structures. Each entry consists of an interface identifier, a function pointer, and an argument for the function represented as a DWORD. This provides a flexible, extensible mechanism for implementing QueryInterface that supports multiple inheritance, aggregation, tear-offs, nested composition, debugging, chaining, and just about any other wacky COM identity tricks C++ programmers currently use. [5] However, because most interfaces are implemented using multiple inheritance, you don’t often need this much flexibility. For example, consider one possible object layout for instances of the CPenguin class, shown in Figure 4.2.

Figure 4.2. CPenguin object layout, including ``vptr`` s to ``vtbl`` s

_images/04atl02.jpg
1class CPenguin : public IBird, public ISnappyDresser {...};

The typical implementation of QueryInterface for a class using multiple inheritance consists of a series of if statements and static_cast operations; the purpose is to adjust the this pointer by some fixed offset to point to the appropriate vptr. Because the offsets are known at compile time, a table matching interface identifiers to offsets would provide an appropriate data structure for adjusting the this pointer at runtime. To support this common case, InternalQueryInterface function treats the _ATL_INTMAP_ENTRY as a simple IID/offset pair if the pFunc member has the special value _ATL_SIMPLEMAPENTRY :

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

To be able to use the InternalQueryInterface function, each implementation populates a static interface map. To facilitate populating this data structure, and to provide some other methods used internally, ATL provides the following macros (as well as others described in Chapter 6, “Interface Maps”):

1#define BEGIN_COM_MAP(class) ...
2#define COM_INTERFACE_ENTRY(itf) ...
3#define END_COM_MAP() ...

For example, our CPenguin class would declare its interface map like this:

 1class CPenguin :
 2    public CComObjectRootEx<CComMultiThreadModel>,
 3    public IBird,
 4    public ISnappyDresser {
 5...
 6public:
 7BEGIN_COM_MAP(CPenguin)
 8    COM_INTERFACE_ENTRY(IBird)
 9    COM_INTERFACE_ENTRY(ISnappyDresser)
10END_COM_MAP()
11...
12};

In an abbreviated form, this would expand to the following:

 1class CPenguin :
 2    public CComObjectRootEx<CComMultiThreadModel>,
 3    public IBird,
 4    public ISnappyDresser {
 5...
 6public:
 7  IUnknown* GetUnknown() {
 8      ATLASSERT(_GetEntries()[0].pFunc == _ATL_SIMPLEMAPENTRY);
 9      return (IUnknown*)((int)this+_GetEntries()->dw); }
10  }
11  HRESULT _InternalQueryInterface(REFIID iid, void** ppvObject) {
12      return InternalQueryInterface(this, _GetEntries(), iid, ppvObject);
13  }
14  const static _ATL_INTMAP_ENTRY* WINAPI _GetEntries() {
15     static const _ATL_INTMAP_ENTRY _entries[] = {
16         { &_ATL_IIDOF(IBird),          0, _ATL_SIMPLEMAPENTRY },
17         { &_ATL_IIDOF(ISnappyDresser), 4, _ATL_SIMPLEMAPENTRY },
18         { 0, 0, 0 }
19     };
20     return _entries;
21  }
22...
23};

The _ATL_IIDOF macro expands as follows:

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

This macro lets you choose to use __uuidof operator or the standard naming convention to specify the IID for the interface in question for the entire project.

Figure4.3 shows how this interface map relates to an instance of a CPenguin object in memory.

Figure 4.3. ``CPenguin`` interface map, ``CPenguin`` object, and vtbls

[View full size image]

_images/04atl03.jpg

Something else worth mentioning is the GetUnknown member function that the BEGIN_COM_MAP provides. Although ATL uses this internally, it’s also useful when passing your this pointer to a function that requires an IUnknown*. Because your class derives from potentially more than one interface, each of which derives from IUnknown, the compiler considers passing your own this pointer as an IUnknown* to be ambiguous.

1HRESULT FlyInAnAirplane(IUnknown* punkPassenger);
2
3// Penguin.cpp
4STDMETHODIMP CPenguin::Fly() {
5    return FlyInAnAirplane(this); // ambiguous
6}

For these situations, GetUnknown is your friend, e.g.

1STDMETHODIMP CPenguin::Fly() {
2    return FlyInAnAirplane(this->GetUnknown()); // unambiguous
3}

As you’ll see later, GetUnknown is implemented by handing out the first entry in the interface map.

Support for Aggregation: The Controlled Inner

So far, we’ve discussed the implementation of IUnknown for standalone COM objects. However, if our object is to participate in aggregation as a controlled inner,our job is not to think for ourselves, but rather to be subsumed by the thoughts and prayers of another. A controlled inner does this by blindly forwarding all calls on the publicly available implementation of IUnknown to the controlling outer’s implementation. The controlling outer’s implementation is provided as the pUnkOuter argument to the CreateInstance method of IClassFactory. If our ATL-based COM object is used as a controlled inner, it simply forwards all calls to IUnknown methods to the OuterQueryInterface, OuterAddRef, and OuterRelease functions provided in CComObjectRootBase; these, in turn, forward to the controlling outer. The relevant functions of CComObjectRootBase are shown here:

 1class CComObjectRootBase {
 2public:
 3    CComObjectRootBase() { m_dwRef = 0L; }
 4    ...
 5    ULONG OuterAddRef() {
 6        return m_pOuterUnknown->AddRef();
 7    }
 8    ULONG OuterRelease() {
 9        return m_pOuterUnknown->Release();
10    }
11    HRESULT OuterQueryInterface(REFIID iid, void ** ppvObject) {
12        return m_pOuterUnknown->QueryInterface(iid, ppvObject);
13    }
14    ...
15    union {
16        long      m_dwRef;
17        IUnknown* m_pOuterUnknown;
18    };
19};

Notice that CComObjectRootBase keeps the object’s reference count and a pointer to a controlling unknown as a union. This implies that an object can either maintain its own reference count or be aggregated, but not both at the same time. This implication is not true. If the object is being aggregated, it must maintain a reference count and a pointer to a controlling unknown. In this case, discussed more later, ATL keeps the m_pUnkOuter in one instance of the CComObjectBase and derives from CComObjectBase again to keep the object’s reference count.

More to Come

Although it’s possible to implement the methods of IUnknown directly in your class using the methods of the base class CComObjectRootEx, most ATL classes don’t. Instead, the actual implementations of the IUnknown methods are left to a class that derives from your class, as in CComObject. We discuss this after we talk about the responsibilities of your class.

Your Class

Because ATL provides the behavior for IUnknown in the CComObjectRootEx class and provides the actual implementation in the CComObject (and friends) classes, the job your class performs is pretty simple: derive from interfaces and implement their methods. Besides making sure that the interface map lists all the interfaces you’re implementing, you can pretty much leave implementing IUnknown to ATL and concentrate on your custom functionality. This is, after all, the whole point of ATL in the first place.

ATL’s Implementation Classes

Many standard interfaces have common implementations. ATL provides implementation classes of many standard interfaces. For example, IPersistImpl, IConnectionPointContainerImpl, and IViewObjectExImpl implement IPersist, IConnectionPointContainer, and IViewObjectEx, respectively. Some of these interfaces are common enough that many objects can implement them; for example, persistence, eventing, and enumeration. Some are more special purpose and related only to a particular framework, as with controls, Internet-enabled components, and Microsoft Management Console extensions. Most of the general-purpose interface implementations are discussed in Chapters 7, “Persistence in ATL”; 8, “Collections and Enumerators”; and 9, “Connection Points.” The interface implementations related to the controls framework are discussed in Chapters 10, “Windowing,” and 11, “ActiveX Controls.” One implementation is general purpose enough to discuss right here: IDispatchImpl.

Scripting Support

For a scripting environment to access functionality from a COM object, the COM object must implement IDispatch:

 1interface IDispatch : IUnknown {
 2    HRESULT GetTypeInfoCount([out] UINT * pctinfo);
 3
 4    HRESULT GetTypeInfo([in] UINT iTInfo,
 5        [in] LCID lcid,
 6        [out] ITypeInfo ** ppTInfo);
 7
 8    HRESULT GetIDsOfNames([in] REFIID riid,
 9        [in, size_is(cNames)] LPOLESTR * rgszNames,
10        [in] UINT cNames,
11        [in] LCID lcid,
12        [out, size_is(cNames)] DISPID * rgDispId);
13
14    HRESULT Invoke([in] DISPID dispIdMember,
15        [in] REFIID riid,
16        [in] LCID lcid,
17        [in] WORD wFlags,
18        [in, out] DISPPARAMS * pDispParams,
19        [out] VARIANT * pVarResult,
20        [out] EXCEPINFO * pExcepInfo,
21        [out] UINT * puArgErr);
22}

The most important methods of IDispatch are GetIDsOfNames and Invoke. Imagine the following line of scripting code:

1penguin.wingspan = 102

This translates into two calls on IDispatch. The first is GetIDsOfNames, which asks the object if it supports the wingspan property. If the answer is yes, the second call to IDispatch is to Invoke. This call includes an identifier (called a DISPID) that uniquely identifies the name of the property or method the client is interested in (as retrieved from GetIDsOfName s), the type of operation to perform (calling a method, or getting or setting a property), a list of arguments, and a place to put the result (if any). The object’s implementation of Invoke is then required to interpret the request the scripting client made. This typically involves unpacking the list of arguments (which is passed as an array of VARIANT structures), converting them to the appropriate types (if possible), pushing them onto the stack, and calling some other method implemented that deals in real data types, not VARIANT s. In theory, the object’s implementation could take any number of interesting, dynamic steps to parse and interpret the client’s request. In practice, most objects forward the request to a helper, whose job it is to build a stack and call a method on an interface implemented by the object to do the real work. The helper makes use of type information held in a type library typically bundled with the server. COM type libraries hold just enough information to allow an instance of a TypeInfo object – that is, an object that implements ITypeInfo – to perform this service. The TypeInfo object used to implement IDispatch is usually based on a dual interface, defined in IDL like this:

1[ object, dual, uuid(44EBF74E-116D-11D2-9828-00600823CFFB) ]
2interface IPenguin : IDispatch {
3    [propput] HRESULT Wingspan([in] long nWingspan);
4    [propget] HRESULT Wingspan([out, retval] long* pnWingspan);
5              HRESULT Fly();
6}

Using a TypeInfo object as a helper allows an object to implement IDispatch like this (code in bold indicates differences between one implementation and another):

 1class CPenguin :
 2    public CComObectRootEx<CComSingleThreadModel>,
 3    public IBird,
 4    public ISnappyDresser,
 5    public IPenguin {
 6public:
 7    CPenguin() : m_pTypeInfo(0) {
 8        IID*      pIID   = &IID_IPenguin;
 9        GUID*     pLIBID = &LIBID_BIRDSERVERLib;
10        WORD      wMajor = 1;
11        WORD      wMinor = 0;
12        ITypeLib* ptl = 0;
13        HRESULT hr = LoadRegTypeLib(*pLIBID, wMajor, wMinor,
14            0, &ptl);
15        if( SUCCEEDED(hr) ) {
16            hr = ptl->GetTypeInfoOfGuid(*pIID, &m_pTypeInfo);
17            ptl->Release();
18        }
19    }
20
21    virtual ~Penguin() {
22        if( m_pTypeInfo ) m_pTypeInfo->Release();
23    }
24
25BEGIN_COM_MAP(CPenguin)
26    COM_INTERFACE_ENTRY(IBird)
27    COM_INTERFACE_ENTRY(ISnappyDresser)
28    COM_INTERFACE_ENTRY(IDispatch)
29    COM_INTERFACE_ENTRY(IPenguin)
30END_COM_MAP()
31
32    // IDispatch methods
33    STDMETHODIMP GetTypeInfoCount(UINT *pctinfo) {
34        return (*pctinfo = 1), S_OK;
35    }
36    STDMETHODIMP GetTypeInfo(UINT ctinfo, LCID lcid,
37    ITypeInfo **ppti) {
38        if( ctinfo != 0 ) return (*ppti = 0), DISP_E_BADINDEX;
39        return (*ppti = m_pTypeInfo)->AddRef(), S_OK;
40    }
41
42    STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames,
43        UINT cNames, LCID lcid, DISPID *rgdispid) {
44        return m_pTypeInfo->GetIDsOfNames(rgszNames, cNames,
45            rgdispid);
46    }
47
48    STDMETHODIMP Invoke(DISPID dispidMember,
49                        REFIID riid,
50                        LCID lcid,
51                        WORD wFlags,
52                        DISPPARAMS *pdispparams,
53                        VARIANT *pvarResult,
54                        EXCEPINFO *pexcepinfo,
55                        UINT *puArgErr) {
56        return m_pTypeInfo->Invoke(static_cast<IPenguin*>(this),
57                                   dispidMember, wFlags,
58                                   pdispparams, pvarResult,
59                                   pexcepinfo, puArgErr);
60    }
61    // IBird, ISnappyDresser and IPenguin methods...
62private:
63    ITypeInfo* m_pTypeInfo;
64};

Because this implementation is so boilerplate (it varies only by the dual interface type, the interface identifier, the type library identifier, and the major and minor version numbers), it can be easily implemented in a template base class. ATL’s parameterized implementation of IDispatch is IDispatchImpl:

1template <class T,
2          const IID* piid = &__uuidof(T),
3          const GUID* plibid = &CAtlModule::m_libid,
4          WORD wMajor = 1,
5          WORD wMinor = 0,
6          class tihclass = CComTypeInfoHolder>
7class ATL_NO_VTABLE IDispatchImpl : public T {...};

Given IDispatchImpl, our IPenguin implementation gets quite a bit simpler:

 1class CPenguin :
 2    public CComObjectRootEx<CComMultiThreadModel>,
 3    public IBird,
 4    public ISnappyDresser,
 5    public IDispatchImpl<IPenguin, &IID_IPenguin> {
 6public:
 7BEGIN_COM_MAP(CPenguin)
 8    COM_INTERFACE_ENTRY(IBird)
 9    COM_INTERFACE_ENTRY(ISnappyDresser)
10    COM_INTERFACE_ENTRY(IDispatch)
11    COM_INTERFACE_ENTRY(IPenguin)
12END_COM_MAP()
13    // IBird, ISnappyDresser and IPenguin methods...
14};

Supporting Multiple Dual Interfaces

I wish it wouldn’t, but this question always comes up: “How do I support multiple dual interfaces in my COM objects?” My answer is always, “Why would you want to?”

The problem is, of the scripting environments I’m familiar with that require an object to implement IDispatch, not one supports QueryInterface. So although it’s possible to use ATL to implement multiple dual interfaces, you have to choose which implementation to hand out as the “default” – that is, the one the client gets when asking for IDispatch. For example, let’s say that instead of having a special IPenguin interface that represents the full functionality of my object to scripting clients, I decided to make all the interfaces dual interfaces.

1[ dual, uuid(...) ] interface IBird : IDispatch {...}
2[ dual, uuid(...) ] interface ISnappyDresser : IDispatch { ... };

You can implement both of these dual interfaces using ATL’s IDispatchImpl:

 1class CPenguin :
 2    public CComObjectRootEx<CComSingleThreadModel>,
 3    public IDispatchImpl<IBird, &IID_IBird>,
 4    public IDispatchImpl<ISnappyDresser, &IID_ISnappyDresser> {
 5public:
 6BEGIN_COM_MAP(CPenguin)
 7    COM_INTERFACE_ENTRY(IBird)
 8    COM_INTERFACE_ENTRY(ISnappyDresser)
 9    COM_INTERFACE_ENTRY(IDispatch) // ambiguous
10END_COM_MAP()
11...
12};

However, when you fill in the interface map in this way, the compiler gets upset. Remember that the COM_INTERFACE_ENTRY macro essentially boils down to a static_cast to the interface in question. Because two different interfaces derive from IDispatch, the compiler cannot resolve the one to which you’re trying to cast. To resolve this difficulty, ATL provides another macro:

1#define COM_INTERFACE_ENTRY2(itf, branch)

This macro enables you to tell the compiler which branch to follow up the inheritance hierarchy to the IDispatch base. Using this macro allows you to choose the default IDispatch interface:

 1class CPenguin :
 2    public CComObjectRootEx<CComSingleThreadModel>,
 3    public IDispatchImpl<IBird, &IID_IBird>,
 4    public IDispatchImpl<ISnappyDresser, &IID_ISnappyDresser> {
 5public:
 6BEGIN_COM_MAP(CPenguin)
 7    COM_INTERFACE_ENTRY(IBird)
 8    COM_INTERFACE_ENTRY(ISnappyDresser)
 9    COM_INTERFACE_ENTRY2(IDispatch, IBird) // Compiles
10                                           // (unfortunately)
11END_COM_MAP()
12...
13};

That brings me to my objection. Just because ATL and the compiler conspire to allow this usage doesn’t mean that it’s a good one. There is no good reason to support multiple dual interfaces on a single implementation. Any client that supports QueryInterface will not need to use GetIDsOfNames or Invoke. These kinds of clients are perfectly happy using a custom interface, as long as it matches their argument type requirements. On the other hand, scripting clients that don’t support QueryInterface can get to methods and properties on only the default dual interface. For example, the following will not work:

1// Since IBird is the default, its operations are available
2penguin.fly
3// Since ISnappyDresser is not the default, its operations
4// aren't available
5penguin.straightenTie // runtime error

So, here’s my advice: Don’t design your reusable, polymorphic COM interfaces as dual interfaces. Instead, if you’re going to support scripting clients, define a single dual interface that exposes the entire functionality of the class, as I did when defining IPenguin in the first place. As an added benefit, this means that you have to define only one interface that supports scripting clients instead of mandating that all of them do.

Having said that, sometimes you don’t have a choice. For example, when building Visual Studio add-ins, you need to implement two interfaces: _IDTExtensibility2 and IDTCommandTarget. Both of these are defined as dual interfaces, so the environment forces you to deal with this problem. [6] You’ll need to look at the documentation and do some experimentation to figure out which of your IDispatch implementations should be the default.

CComObject Et Al

Consider the following C++ class:

 1class CPenguin :
 2  public CComObjectRootEx<CComMultiThreadModel>,
 3  public IBird,
 4  public ISnappyDresser {
 5public:
 6BEGIN_COM_MAP(CPenguin)
 7    COM_INTERFACE_ENTRY(IBird)
 8    COM_INTERFACE_ENTRY(ISnappyDresser)
 9END_COM_MAP()
10    // IBird and ISnappyDresser methods...
11    // IUnknown methods not implemented here
12};

Because this class doesn’t implement the methods of IUnknown, the following will fail at compile time:

1STDMETHODIMP
2CPenguinCO::CreateInstance(IUnknown* pUnkOuter,
3    REFIID riid, void** ppv) {
4    ...
5    CPenguin* pobj = new CPenguin; // IUnknown not implemented
6    ...
7}

Given CComObjectRootBase, you can easily implement the methods of IUnknown:

 1// Server lifetime management
 2extern void ServerLock();
 3extern void ServerUnlock();
 4
 5class CPenguin :
 6  public CComObjectRootEx<CComMultiThreadModel>,
 7  public IBird,
 8  public ISnappyDresser {
 9public:
10    CPengin() { ServerLock(); }
11    ~CPenguin() { ServerUnlock(); }
12BEGIN_COM_MAP(CPenguin)
13    COM_INTERFACE_ENTRY(IBird)
14    COM_INTERFACE_ENTRY(ISnappyDresser)
15END_COM_MAP()
16    // IBird and ISnappyDresser methods...
17    // IUnknown methods for standalone, heap-based objects
18    STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
19    { return _InternalQueryInterface(riid, ppv); }
20
21    STDMETHODIMP_(ULONG) AddRef()
22    { return InternalAddRef(); }
23
24    STDMETHODIMP_(ULONG) Release() {
25        ULONG l = InternalRelease();
26        if( l == 0 ) delete this;
27        return l;
28    }
29};

Unfortunately, although this implementation does leverage the base class behavior, it has hard-coded assumptions about the lifetime and identity of our objects. For example, instances of this class can’t be created as an aggregate. Just as we’re able to encapsulate decisions about thread safety into the base class, we would like to encapsulate decisions about lifetime and identity. However, unlike thread-safety decisions, which are made on a per-class basis and are, therefore, safe to encode into a base class, lifetime and identity decisions can be made on a per-instance basis. Therefore, we’ll want to encapsulate lifetime and identity behavior into classes meant to derive from our class.

Standalone Activation

To encapsulate the standalone, heap-based object implementation of IUnknown I just showed you, ATL provides CComObject, shown in a slightly abbreviated form here:

 1template <class Base>
 2class CComObject : public Base {
 3public:
 4    typedef Base _BaseClass;
 5    CComObject(void* = NULL)
 6    { _pAtlModule->Lock(); }    // Keeps server loaded
 7
 8    // Set refcount to -(LONG_MAX/2) to protect destruction and
 9    // also catch mismatched Release in debug builds
10    ~CComObject() {
11        m_dwRef = -(LONG_MAX/2);
12        FinalRelease();
13#ifdef _ATL_DEBUG_INTERFACES
14        _AtlDebugInterfacesModule.DeleteNonAddRefThunk(
15            _GetRawUnknown());
16#endif
17        _pAtlModule->Unlock();   // Allows server to unload
18    }
19    STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
20    STDMETHOD_(ULONG, Release)() {
21        ULONG l = InternalRelease();
22        if (l == 0) delete this;
23        return l;
24    }
25
26    STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
27    {return _InternalQueryInterface(iid, ppvObject);}
28
29    template <class Q>
30    HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp)
31    { return QueryInterface(__uuidof(Q), (void**)pp); }
32
33    static HRESULT WINAPI CreateInstance(CComObject<Base>** pp) ;
34};

Notice that CComObject takes a template parameter called Base. This is the base class from which CComObject derives to obtain the functionality of CComObjectRootEx, as well as whatever custom functionality we’d like to include in our objects. Given the implementation of CPenguin that did not include the implementation of the IUnknown methods, the compiler would be happy with CComObject used as follows (although I describe later why new shouldn’t be used directly when creating ATL-based COM objects):

 1STDMETHODIMP
 2CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid,
 3    void** ppv) {
 4    *ppv = 0;
 5    if( pUnkOuter ) return CLASS_E_NOAGGREGATION;
 6    // Read on for why not to use new like this!
 7    CComObject<CPenguin>* pobj = new CComObject<CPenguin>;
 8    if( pobj ) {
 9        pobj->AddRef();
10        HRESULT hr = pobj->QueryInterface(riid, ppv);
11        pobj->Release();
12        return hr;
13    }
14    return E_OUTOFMEMORY;
15}

Besides the call to FinalRelease and the static member function CreateInstance (which are both described in the “Creators” section of this chapter), CComObject provides one additional item of note, the QueryInterface member function template [7] :

1template <class Q>
2HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp)
3{ return QueryInterface(__uuidof(Q), (void**)pp); }

This member function template uses the capability of the VC++ compiler to tag a type with a universally unique identifier (UUID). This capability has been available since VC++ 5.0 and takes the form of a declarative specifier (declspec s):

1struct __declspec(uuid("00000000-0000-0000-C000-000000000046") IUnknown
2{...};

These declspec specifiers are output by the Microsoft IDL compiler and are available for both standard and custom interfaces. You can retrieve the UUID of a type using the __uuidof operator, allowing the following syntax:

1void TryToFly(IUnknown* punk) {
2    IBird* pbird = 0;
3    if( SUCCEEDED(punk->QueryInterface(__uuidof(pbird),
4        (void**)&pbird) ) {
5        pbird->Fly();
6        pbird->Release();
7    }
8}

Using the QueryInterface member function template provided in CComObject offers a bit more syntactic convenience, given a CComObject-based object reference:

1void TryToFly(CComObject<CPenguin>* pPenguin) {
2    IBird* pbird = 0;
3    if( SUCCEEDED(pPenguin->QueryInterface(&pbird) ) {
4      pbird->Fly();
5      pbird->Release();
6    }
7}

Aggregated Activation

Notice that the CPenguin class object implementation shown previously disallowed aggregation by checking for a nonzero pUnkOuter and returning CLASS_E_NOAGGREGATION. If we want to support aggregation as well as – or instead of – standalone activation, we need another class to implement the forwarding behavior of aggregated instances. For this, ATL provides CComAggObject.

CComAggObject performs the chief service of being a controlled inner – that is, providing two implementations of IUnknown. One implementation forwards calls to the controlling outer, subsumed by its lifetime and identity. The other implementation is for private use of the controlling outer for actually maintaining the lifetime of and querying interfaces from the inner. To obtain the two implementations of IUnknown, CComAggObject derives from CComObjectRootEx twice, once directly and once indirectly via a contained instance of your class derived from CComContainedObject, as shown here:

 1template <class contained>
 2class CComAggObject :
 3    public IUnknown,
 4    public CComObjectRootEx<
 5        contained::_ThreadModel::ThreadModelNoCS> {
 6public:
 7    typedef contained _BaseClass;
 8    CComAggObject(void* pv) : m_contained(pv)
 9    { _pAtlModule->Lock(); }
10
11    ~CComAggObject() {
12        m_dwRef = -(LONG_MAX/2);
13        FinalRelease();
14        _pAtlModule->Unlock();
15    }
16
17    STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) {
18        ATLASSERT(ppvObject != NULL);
19        if (ppvObject == NULL)
20            return E_POINTER;
21        *ppvObject = NULL;
22
23        HRESULT hRes = S_OK;
24        if (InlineIsEqualUnknown(iid)) {
25            *ppvObject = (void*)(IUnknown*)this;
26            AddRef();
27        }
28        else
29            hRes = m_contained._InternalQueryInterface(iid,
30                ppvObject);
31        return hRes;
32    }
33
34    STDMETHOD_(ULONG, AddRef)()
35    { return InternalAddRef(); }
36
37    STDMETHOD_(ULONG, Release)() {
38        ULONG l = InternalRelease();
39        if (l == 0) delete this;
40        return l;
41    }
42
43    template <class Q>
44    HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp)
45    { return QueryInterface(__uuidof(Q), (void**)pp); }
46    static HRESULT WINAPI CreateInstance(LPUNKNOWN pUnkOuter,
47        CComAggObject<contained>** pp);
48
49    CComContainedObject<contained> m_contained;
50};

You can see that instead of deriving from your class (passed as the template argument), CComAggObject derives directly from CComObjectRootEx. Its implementation of QueryInterface relies on the interface map you’ve built in your class, but its implementation of AddRef and Release access relies on the second instance of CComObjectRootBase it gets by deriving from CComObjectRootEx. This second instance of CComObjectRootBase uses the m_dwRef member of the union.

The first instance of CComObjectRootBase, the one that manages the m_pOuterUnknown member of the union, is the one CComAggObject gets by creating an instance of your class derived from CComContainedObject as the m_contained data member. CComContainedObject implements QueryInterface, AddRef, and Release by delegating to the m_pOuterUnknown passed to the constructor:

 1template <class Base>
 2class CComContainedObject : public Base {
 3public:
 4    typedef Base _BaseClass;
 5    CComContainedObject(void* pv) {
 6        m_pOuterUnknown = (IUnknown*)pv;
 7    }
 8
 9    STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) {
10      return OuterQueryInterface(iid, ppvObject);
11    }
12
13    STDMETHOD_(ULONG, AddRef)()
14    { return OuterAddRef(); }
15
16    STDMETHOD_(ULONG, Release)()
17    { return OuterRelease(); }
18
19    template <class Q>
20    HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp)
21    { return QueryInterface(__uuidof(Q), (void**)pp); }
22
23    IUnknown* GetControllingUnknown()
24    { return m_pOuterUnknown; }
25};

Being the Controlled Inner

Using CComAggObject and its two implementations of IUnknown, our CPenguin class object implementation can support either standalone or aggregated activation without touching the CPenguin source:

 1STDMETHODIMP
 2CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid,
 3    void** ppv) {
 4    *ppv = 0;
 5    if( pUnkOuter ) {
 6        CComAggObject<CPenguin>* pobj =
 7                       new CComAggObject<CPenguin>(pUnkOuter);
 8        ...
 9    }
10    else {
11        CComObject<CPenguin>* pobj = new CComObject<CPenguin>;
12        ...
13    }
14}

This usage provides the most efficient runtime decision making. If the object is standalone, it pays the price of one reference count and one implementation of IUnknown. If it is aggregated, it pays the price of one reference count, one pointer to the controlling outer, and two implementations of IUnknown. However, one additional price we’re paying is one extra set of vtbl s. By using both CComAggObject<CPenguin> and CComObject<CPenguin>, we’ve created two classes and, therefore, two sets of vtbl s. If you’ve got a small number of instances or nearly all your instances are aggregated, you might want a single class that can handle both aggregated and standalone activation, thereby eliminating one set of vtbl s. You do this by using CComPolyObject in place of both CComObject and CComAggObject:

1STDMETHODIMP
2CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid,
3    void** ppv) {
4    *ppv = 0;
5    CComPolyObject<CPenguin>* pobj =
6        new CComPolyObject<CPenguin>(pUnkOuter);
7    ...
8}

CComPolyObject is nearly identical to CComAggObject, except that, in its constructor, if the pUnkOuter is zero, it uses its second implementation of IUnknown as the outer for the first to forward to, as shown:

1class CComPolyObject :
2    public IUnknown,
3    public CComObjectRootEx<
4        contained::_ThreadModel::ThreadModelNoCS> {
5public:
6    ...
7    CComPolyObject(void* pv) : m_contained(pv ? pv : this) {...}
8    ...
9};

The use of CComPolyObject saves a set of vtbl s, so the module size is smaller, but the price you pay for standalone objects is getting an extra implementation of IUnknown as well as an extra pointer to that implementation.

Alternative Activation Techniques

Besides standalone operation, CComObject makes certain assumptions about where the object’s memory has been allocated from (the heap) and whether the existence of the object should keep the server loaded (it does). For other needs, ATL provides five more classes meant to be the most derived class in your implementation hierarchy: CComObjectCached, CComObjectNoLock, CComObjectGlobal, CComObjectStack, and CComObjectStackEx.

CComObjectCached

CComObjectCached objects implement reference counting, assuming that you’re going to create an instance and then hold it for the life of the server, handing out references to it as requested. To avoid keeping the server running forever after the cached instance is created, the boundary for keeping the server running is a reference count of one, although the lifetime of the object is still managed on a boundary of zero:

 1template <class Base>
 2class CComObjectCached : public Base {
 3public:
 4    ...
 5    STDMETHOD_(ULONG, AddRef)() {
 6        ULONG l = InternalAddRef();
 7        if (l == 2)
 8            _pAtlModule->Lock();
 9        return l;
10    }
11    STDMETHOD_(ULONG, Release)() {
12        ULONG l = InternalRelease();
13        if (l == 0)
14            delete this;
15        else if (l == 1)
16            _pAtlModule->Unlock();
17        return l;
18    }
19    ...
20};

Cached objects are useful for in-process class objects:

 1static CComObjectCached<CPenguinCO>* g_pPenguinCO = 0;
 2
 3BOOL WINAPI DllMain(HINSTANCE, DWORD dwReason, void*) {
 4  switch( dwReason ) {
 5  case DLL_PROCESS_ATTACH:
 6      g_pPenguinCO = new CComObjectCached<CPenguinCO>();
 7
 8      // 1st ref. **doesn't** keep server alive
 9      if( g_pPenguinCO ) g_pPenguinCO->AddRef();
10  break;
11
12  case DLL_PROCESS_DETACH:
13      if( g_pPenguinCO ) g_pPenguinCO->Release();
14  break;
15  }
16  return TRUE;
17}
18
19STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid,
20    void** ppv) {
21    // Subsequent references do keep server alive
22    if( clsid == CLSID_Penguin && g_pPenguinCO )
23      return g_pPenguinCO->QueryInterface(riid, ppv);
24    return CLASS_E_CLASSNOTAVAILABLE;
25}

CComObjectNoLock

Sometimes you don’t want outstanding references on your object to keep the server alive. For example, class objects in an out-of-process server are cached in a table maintained by ole32.dll some number of times (it might not be one). For this reason, COM itself manages how the lifetime of a class object affects the lifetime of its out-of-process server using the LockServer method of the IClassFactory interface. For this use, ATL provides CComObjectNoLock, whose implementation does not affect the lifetime of the server:

 1template <class Base>
 2class CComObjectNoLock : public Base {
 3public:
 4    ...
 5    STDMETHOD_(ULONG, AddRef)()
 6    { return InternalAddRef(); }
 7
 8    STDMETHOD_(ULONG, Release)() {
 9        ULONG l = InternalRelease();
10        if (l == 0) delete this;
11        return l;
12    }
13  ...
14};

No-lock objects are useful for out-of-process class objects:

 1int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
 2    CoInitialize(0);
 3
 4    CComObjectNoLock<CPenguinCO>* pPenguinCO =
 5        new CComObjectNoLock<CPenguinCO>();
 6    if( !pPenguinCO ) return E_OUTOFMEMORY;
 7        pPenguinCO->AddRef();
 8
 9    DWORD   dwReg;
10    HRESULT hr;
11
12    // Reference(s) cached by ole32.dll won't keep server
13    // from shutting down
14    hr = CoRegisterClassObject(CLSID_Penguin, pPenguinCO, ...,
15        &dwReg);
16    if( SUCCEEDED(hr) ) {
17        MSG msg; while( GetMessage(&msg, 0, 0, 0) ) DispatchMessage(&msg);
18        CoRevokeClassObject(dwReg);
19        pPenguinCO->Release();
20    }
21
22    CoUninitialize();
23    return hr;
24}

CComObjectGlobal

Just as it’s handy to have an object whose existence or outstanding references don’t keep the server alive, sometimes it’s handy to have an object whose lifetime matches that of the server. For example, a global or static object is constructed once when the server is loaded and is not destroyed until after WinMain or DllMain has completed. Clearly, the mere existence of a global object cannot keep the server running, or the server could never be shut down. On the other hand, we’d like to be able to keep the server running if there are outstanding references to a global object. For this, we have CComObjectGlobal:

1template <class Base>
2class CComObjectGlobal : public Base {
3public:
4  ...
5  STDMETHOD_(ULONG, AddRef )() { return _pAtlModule->Lock(); }
6  STDMETHOD_(ULONG, Release)() { return _pAtlModule->Unlock(); }
7  ...
8};

Global objects can be used instead of cached objects for implementing in-process class objects, but they’re useful for any global or static object:

 1// No references yet, so server not forced to stay alive
 2static CComObjectGlobal<CPenguinCO> g_penguinCO;
 3
 4STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid,
 5    void** ppv) {
 6    // All references keep the server alive
 7    if( clsid == CLSID_Penguin )
 8        return g_penguinCO.QueryInterface(riid, ppv);
 9    return CLASS_E_CLASSNOTAVAILABLE;
10}

CComObjectStack and CComObjectStackEx

Instead of using a global or static object, you might find yourself with the urge to allocate a COM object on the stack. ATL supports this technique with CComObjectStack:

 1template <class Base>
 2class CComObjectStack : public Base {
 3public:
 4    ...
 5    STDMETHOD_(ULONG, AddRef)()
 6    { ATLASSERT(FALSE); return 0; }
 7
 8    STDMETHOD_(ULONG, Release)()
 9    { ATLASSERT(FALSE); return 0; }
10
11    STDMETHOD(QueryInterface)(REFIID iid, void** ppvObject)
12    { ATLASSERT(FALSE); return E_NOINTERFACE; }
13    ...
14};

Based on the implementation, it should be clear that you’re no longer doing COM. CComObjectStack shuts up the compiler, but you still cannot use any methods of IUnknown, which means that you cannot pass out an interface reference from an object on the stack. This is good because, as with a reference to anything on the stack, as soon as the stack goes away, the reference points at garbage. The nice thing about ATL’s implementation of CComObjectStack is that it warns you at runtime that you’re doing something bad:

1void DoABadThing(IBird** ppbird) {
2    CComObjectStack<CPenguin> penguin;
3    penguin.Fly();           // Using IBird method is OK
4    penguin.StraightenTie(); // Using ISnappyDresser method
5                             // also OK
6
7    // This will trigger an assert at runtime
8    penguin.QueryInterface(IID_IBird, (void**)ppbird);
9}

CComObjectStackEx addresses the limitations of CComObjectStack by providing a more useful implementation of IUnknown:

 1template <class Base>
 2class CComObjectStackEx : public Base {
 3public:
 4    typedef Base _BaseClass;
 5
 6    CComObjectStackEx(void* = NULL) {
 7#ifdef _DEBUG
 8        m_dwRef = 0;
 9#endif
10        m_hResFinalConstruct = _AtlInitialConstruct();
11        if (SUCCEEDED(m_hResFinalConstruct))
12            m_hResFinalConstruct = FinalConstruct();
13    }
14
15    virtual ~CComObjectStackEx() {
16        // This assert indicates mismatched ref counts.
17        //
18        // The ref count has no control over the
19        // lifetime of this object, so you must ensure
20        // by some other means that the object remains
21        // alive while clients have references to its interfaces.
22        ATLASSUME(m_dwRef == 0);
23        FinalRelease();
24#ifdef _ATL_DEBUG_INTERFACES
25        _AtlDebugInterfacesModule.DeleteNonAddRefThunk(
26            _GetRawUnknown());
27#endif
28    }
29
30    STDMETHOD_(ULONG, AddRef)() {
31#ifdef _DEBUG
32        return InternalAddRef();
33#else
34        return 0;
35#endif
36    }
37
38    STDMETHOD_(ULONG, Release)() {
39#ifdef _DEBUG
40        return InternalRelease();
41#else
42        return 0;
43#endif
44    }
45
46    STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) {
47        return _InternalQueryInterface(iid, ppvObject);
48    }
49
50    HRESULT m_hResFinalConstruct;
51};

As you can see, CComObjectStackEx permits the use of the IUnknown methods, as long as they are called within the scope of the CComObjectStackEx instance. This allows methods called from within the instance scope to treat the object as if it were a typical heap-based COM object, as in the following:

 1void PlayWithBird() {
 2    CComObjectStackEx<CPenguin> penguin;
 3    IBird* pBird = NULL;
 4    penguin.QueryInterface(IID_IBird,
 5        (void**)&pBird);          // OK -> no assert
 6    DoBirdTrickes(pBird);
 7}
 8
 9void DoBirdTricks(IBird* pBird) {
10    pBird->Fly();                 // IBird methods OK
11    ISnappyDresser* pPenguin = NULL;
12    pBird->QueryInterface(IID_ISnappyDresser,
13        (void**)&pPenguin);       // OK
14    pPenguin->StraightenTie();    // ISnappyDresser methods OK
15    pPenguin->Release();          // OK -> no assert
16}

One from Column A, Two from Column B…

Table 4.2 shows the various identity and lifetime options ATL provides.

Table 4.2. ATL’s Identity and Lifetime Options

Class
Standalone
or
Aggregated
Heap or
Stack
Existence
Keeps Server
Alive
Extent Refs
Keep Server
Alive
Useful
IUnkown
Methods

CcomObject

Standalone

Heap

Yes

Yes

Yes

CComAggObject

Aggregated

Heap

Yes

Yes

Yes

CComPolyObject

Standalone
or
aggregated

Heap

Yes

Yes

Yes

CComObjectCached

Standalone

Heap

No

Second
Reference

Yes

CComObjectNoLock

Standalone

Heap

No

No

Yes

CComObjectGlobal

Standalone

Data seg.

No

Yes

Yes

CComObjectStack

Standalone

Stack

No

No

No

CComObjectStackEx

Standalone

Stack

No

No

Yes

ATL Creators

Multiphase Construction

As I’ve mentioned, ATL servers might not necessarily link with the CRT. However, living without the CRT can be a pain. Among other things, if you don’t have the CRT, you also don’t get C++ exceptions. That doesn’t leave you much to do in the following scenario:

 1// CPenguin constructor
 2CPenguin::CPenguin() {
 3  HRESULT hr = CoCreateInstance(CLSID_EarthAtmosphere, 0,
 4    CLSCTX_ALL, IID_IAir, (void**)&m_pAir);
 5  if( FAILED(hr) ) {
 6    // Can't return an error from a ctor
 7    return hr;
 8    // Can't throw an error without the CRT
 9    throw hr;
10    // This won't help
11    OutputDebugString(__T("Help! Can't bre...\n"));
12  }
13}

The OutputDebugString isn’t going to notify the client that the object it just created doesn’t have the resources it needs to survive; there’s no way to return the failure result back to the client. This hardly seems fair because the IClassFactory method CreateInstance that’s creating our objects certainly can return an HRESULT. The problem is having a way to hand a failure from the instance to the class object so that it can be returned to the client. By convention, ATL classes provide a public member function called FinalConstruct for objects to participate in multiphase construction:

1HRESULT FinalConstruct();

An empty implementation of the FinalConstruct member function is provided in CComObjectRootBase, so all ATL objects have one. Because FinalConstruct returns an HRESULT, now you have a clean way to obtain the result of any nontrivial construction:

 1HRESULT CPenguin::FinalConstruct() {
 2    return CoCreateInstance(CLSID_EarthAtmosphere, 0, CLSCTX_ALL,
 3                            IID_IAir, (void**)&m_pAir);
 4}
 5STDMETHODIMP
 6CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid,
 7    void** ppv) {
 8    *ppv = 0;
 9    if( !pUnkOuter ) {
10        CComObject<CPenguin>* pobj = new CComObject<CPenguin>;
11        if( !pobj ) return E_OUTOFMEMORY;
12        HRESULT hr = pobj->FinalConstruct();
13        if( SUCCEEDED(hr) ) ...
14        return hr;
15    }
16    ...
17}

You do have something else to consider, though. Notice that when CreateInstance calls FinalConstruct, it has not yet increased the reference count of the object. This causes a problem if, during the FinalConstruct implementation, the object handed a reference to itself to another object. If you think this is uncommon, remember the pUnkOuter parameter to the IClassFactory method CreateInstance. However, even without aggregation, it’s possible to run into this problem. Imagine the following somewhat contrived but perfectly legal code:

 1// CPenguin implementation
 2HRESULT CPenguin::FinalConstruct() {
 3    HRESULT hr;
 4    hr = CoCreateInstance(CLSID_EarthAtmosphere, 0, CLSCTX_ALL,
 5                          IID_IAir, (void**)&m_pAir);
 6    if( SUCCEEDED(hr) ) {
 7        // Pass reference to object with reference count of 0
 8        hr = m_pAir->CheckSuitability(GetUnknown());
 9    }
10    return hr;
11}
12
13// CEarthAtmosphere implementation in separate server
14STDMETHODIMP CEarthAtmosphere::CheckSuitability(IUnknown* punk) {
15    IBreatheO2* pbo2 = 0;
16    HRESULT hr = E_FAIL;
17
18    // CPenguin's lifetime increased to 1 via QI
19    hr = punk->QueryInterface(IID_IBreatheO2, (void**)&pbo2);
20    if( SUCCEEDED(hr) ) {
21        pbo2->Release(); // During this call, lifetime decreases
22                         // to 0 and destruction sequence begins...
23    }
24
25    return (SUCCEEDED(hr) ? S_OK : E_FAIL);
26}

To avoid the problem of premature destruction, you need to artificially increase the object’s reference count before FinalConstruct is called and then decrease its reference count afterward:

 1STDMETHODIMP
 2CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid,
 3    void** ppv) {
 4    *ppv = 0;
 5    if( !pUnkOuter ) {
 6        CComObject<CPenguin>* pobj = new CComObject<CPenguin>;
 7        if( FAILED(hr) ) return E_OUTOFMEMORY;
 8
 9        // Protect object from pre-mature destruction
10        pobj->InternalAddRef();
11        hr = pobj->FinalConstruct();
12        pobj->InternalRelease();
13
14        if( SUCCEEDED(hr) ) ...
15        return hr;
16    }
17    ...
18}

Just Enough Reference Count Safety

Arguably, not all objects need their reference count artificially managed in the way just described. In fact, for multithreaded objects that don’t require this kind of protection, extra calls to InterlockedIncrement and InterlockedDecrement represent unnecessary overhead. Toward that end, CComObjectRootBase provides a pair of functions just for bracketing the call to FinalConstruct in a “just reference count safe enough” way:

 1STDMETHODIMP
 2CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid,
 3    void** ppv) {
 4    *ppv = 0;
 5    if( !pUnkOuter ) {
 6        CComObject<CPenguin>* pobj = new CComObject<CPenguin>;
 7        if( FAILED(hr) ) return E_OUTOFMEMORY;
 8
 9        // Protect object from pre-mature destruction (maybe)
10        pobj->InternalFinalConstructAddRef();
11        hr = pobj->FinalConstruct();
12        pobj->InternalFinalConstructRelease();
13
14        if( SUCCEEDED(hr) ) ...
15        return hr;
16    }
17    ...
18}

By default, InternalFinalConstructAddRef and InternalFinalConstructRelease incur no release build runtime overhead:

1class CComObjectRootBase {
2public:
3  ...
4  void InternalFinalConstructAddRef() {}
5  void InternalFinalConstructRelease() {
6    ATLASSERT(m_dwRef == 0);
7  }
8  ...
9};

To change the implementation of InternalFinalConstructAddRef and InternalFinalConstructRelease to provide reference count safety, ATL provides the following macro:

1#define DECLARE_PROTECT_FINAL_CONSTRUCT() \
2  void InternalFinalConstructAddRef() { InternalAddRef(); } \
3  void InternalFinalConstructRelease() { InternalRelease(); }

The DECLARE_PROTECT_FINAL_CONSTRUCT macro is used on a per-class basis to turn on reference count safety as required. Our CPenguin would use it like this:

1class CPenguin : ... {
2public:
3  HRESULT FinalConstruct();
4  DECLARE_PROTECT_FINAL_CONSTRUCT()
5  ...
6};

In my opinion, DECLARE_PROTECT_FINAL_CONSTRUCT is one ATL optimization too many. Using it requires not only a great deal of knowledge of COM and ATL internals, but also a great deal of knowledge of how to implement the objects you create in FinalConstruct methods. Because you often don’t have that knowledge, the only safe thing to do is to always use DECLARE_PROTECT_FINAL_CONSTRUCT if you’re handing out references to your instances in your FinalConstruct calls. And because that rule is too complicated, most folks will probably forget it. So here’s a simpler one:

Every class that implements the FinalConstruct member function should also have a DECLARE_PROTECT_FINAL_CONSTRUCT macro instantiation.

Luckily, the wizard generates DECLARE_PROTECT_FINAL_CONSTRUCT when it generates a new class, so your FinalConstruct code will be safe by default. If you decide you don’t want it, you can remove it. [8]

Another Reason for Multiphase Construction

Imagine a plain-vanilla C++ class that wants to call a virtual member function during its construction, and another C++ class that overrides that function:

 1class Base {
 2public:
 3    Base() { Init(); }
 4    virtual void Init() {}
 5};
 6
 7class Derived : public Base {
 8public:
 9    virtual void Init() {}
10};

Because it’s fairly uncommon to call virtual member functions as part of the construction sequence, it’s not widely known that the Init function during the constructor for Base will not be Derived::Init, but Base::Init. This might seem counterintuitive, but the reason it works this way is a good one: It doesn’t make sense to call a virtual member function in a derived class until the derived class has been properly constructed. However, the derived class isn’t properly constructed until after the base class has been constructed. To make sure that only functions of properly constructed classes are called during construction, the C++ compiler lays out two vtbl s, one for Base and one for Derived. The C++ runtime then adjusts the vptr to point to the appropriate vtbl during the construction sequence.

Although this is all part of the official C++ standard, it’s not exactly intuitive, especially because it is so rarely used (or maybe it’s so rarely used because it’s unintuitive). Because it’s rarely used, beginning with Visual C++ 5.0, Microsoft introduced __declspec(novtable) to turn off the adjustment of vptr s during construction. If the base class is an abstract base class, this often results in vtbl s that are generated by the compiler but not used, so the linker can remove them from the final image.

This optimization is used in ATL whenever a class is declared using the ATL_NO_VTABLE macro:

1#ifdef _ATL_DISABLE_NO_VTABLE
2#define ATL_NO_VTABLE
3#else
4#define ATL_NO_VTABLE __declspec(novtable)
5#endif

Unless the _ATL_DISABLE_NO_VTABLE is defined, a class defined using _ATL_NO_VTABLE has its constructor behavior adjusted with __declspec(novtable):

1class ATL_NO_VTABLE CPenguin ... {};

This is a good and true optimization, but classes that use it must not call virtual member functions in their constructors. [9] If virtual member functions need to be called during construction, leave them until the call to FinalConstruct, which is called after the most derived class’s constructor and after the vptr s are adjusted to the correct values.

One last thing should be mentioned about __declspec(novatble). Just as it turns off the adjustment of vptr s during construction, it turns off the adjustment of vptr s during destruction. Therefore, avoid calling virtual functions in the destructor as well; instead, call them in the object’s FinalRelease member function.

FinalRelease

ATL calls the object’s FinalRelease function after the object’s final interface reference is released and before your ATL-based object’s destructor is called:

1void FinalRelease();

The FinalRelease member function is useful for calling virtual member functions and releasing interfaces to other objects that also have pointers back to you. Because those other objects might want to query for an interface during its shutdown sequence, it’s just as important to protect the object against double destruction as it was to protect it against premature destruction in FinalConstruct. Even though the FinalRelease member function is called when the object’s reference count has been decreased to zero (which is why the object is being destroyed), the caller of FinalRelease artificially sets the reference count to -(LONG_MAX/2) to avoid double deletion. The caller of FinalRelease is the destructor of the most derived class:

1CComObject::~CComObject() {
2    m_dwRef = -(LONG_MAX/2);
3    FinalRelease();
4    _AtlModule->Unlock();
5}

Under the Hood

Just as two-phase construction applies to code you need to call to set up your objects, the ATL framework itself often needs to do operations at construction time that might fail. For example, creation of a lock object could fail for some reason. To handle this, ATL and CComObjectRootBase define a couple other entry points:

 1class CComObjectRootBase {
 2public:
 3    ...
 4    // For library initialization only
 5    HRESULT _AtlFinalConstruct() {
 6        return S_OK;
 7    }
 8    ...
 9    void _AtlFinalRelease() {}      // temp
10};

These methods exist so that ATL has a place to put framework-initialization functions that aren’t affected by your work in FinalConstruct. In addition to these methods, CComObjectRootEx defines this setup method:

1template <class ThreadModel>
2class CComObjectRootEx : public CComObjectRootBase {
3public:
4    ...
5    HRESULT _AtlInitialConstruct() {
6        return m_critsec.Init();
7    }
8};

CComAggObject, CComPolyObject, etc. all define their own implementation of _AtlInitialConstruct. At this time, nothing in the framework overrides _AtlFinalConstruct or _AtlFinalRelease. However, _AtlInitialConstruct is used; when you’re creating objects, make sure that it gets called or your objects won’t get initialized properly.

Creators

Because the extra steps to manage the multiphase construction process are easy to forget, ATL encapsulates this algorithm into several C++ classes called Creators. Each performs the appropriate multiphase construction. Each Creator class is actually just a way to wrap a scope around a single static member function called CreateInstance:

1static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv);

The name of the Creator class is used in a type definition associated with the class; this is discussed in the next section.

CComCreator

CComCreator is a Creator class that creates either standalone or aggregated instances. It is parameterized by the C++ class being created; for example, CComObject<CPenguin>. CComCreator is declared like this:

 1template <class T1>
 2class CComCreator {
 3public:
 4    static HRESULT WINAPI CreateInstance(void* pv, REFIID riid,
 5        LPVOID* ppv) {
 6        ATLASSERT(ppv != NULL);
 7        if (ppv == NULL)
 8            return E_POINTER;
 9        *ppv = NULL;
10
11        HRESULT hRes = E_OUTOFMEMORY;
12        T1* p = NULL;
13        ATLTRY(p = new T1(pv))
14        if (p != NULL) {
15            p->SetVoid(pv);
16            p->InternalFinalConstructAddRef();
17            hRes = p->_AtlInitialConstruct();
18            if (SUCCEEDED(hRes))
19                hRes = p->FinalConstruct();
20            if (SUCCEEDED(hRes))
21                hRes = p->_AtlFinalConstruct();
22            p->InternalFinalConstructRelease();
23            if (hRes == S_OK)
24                hRes = p->QueryInterface(riid, ppv);
25            if (hRes != S_OK)
26                delete p;
27        }
28        return hRes;
29    }
30};

Using CComCreator simplifies our class object implementation quite a bit:

1STDMETHODIMP
2CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid,
3    void** ppv) {
4    typedef CComCreator<
5        CComPolyObject<CPenguin> > PenguinPolyCreator;
6    return PenguinPolyCreator::CreateInstance(pUnkOuter,
7        riid, ppv);
8}

Notice the use of the type definition to define a new Creator type. If we were to create penguins other places in our server, we would have to rebuild the type definition:

1STDMETHODIMP CAviary::CreatePenguin(IBird** ppbird) {
2    typedef CComCreator< CComObject<CPenguin> > PenguinCreator;
3    return PenguinCreator::CreateInstance(0, IID_IBird, (void**)ppbird);
4}

Defining a Creator like this outside the class being created has two problems. First, it duplicates the type-definition code. Second, and more important, we’ve taken away the right of the CPenguin class to decide for itself whether it wants to support aggregation; the type definition is making this decision now. To reduce code and let the class designer make the decision about standalone versus aggregate activation, by convention in ATL, you place the type definition inside the class declaration and give it the well-known name _CreatorClass:

1class CPenguin : ... {
2public:
3    ...
4    typedef CComCreator<
5        CComPolyObject<CPenguin> > _CreatorClass;
6};

Using the Creator type definition, creating an instance and obtaining an initial interface actually involves fewer lines of code than operator new and QueryInterface:

1STDMETHODIMP CAviary::CreatePenguin(IBird** ppbird) {
2    return CPenguin::_CreatorClass::CreateInstance(0,
3        IID_IBird,
4        (void**)ppbird);
5}

Chapter5, “COM Servers,” discusses one other base class that your class will often derive from, CComCoClass.

1class CPenguin : ...,
2public CComCoClass<CPenguin, &CLSID_Penguin>, ... {...};

CComCoClass provides two static member functions, each called CreateInstance, that make use of the class’s creators:

 1template <class T, const CLSID* pclsid = &CLSID_NULL>
 2class CComCoClass {
 3public:
 4    ...
 5    template <class Q>
 6    static HRESULT CreateInstance(IUnknown* punkOuter, Q** pp) {
 7        return T::_CreatorClass::CreateInstance(punkOuter,
 8            __uuidof(Q), (void**) pp);
 9    }
10    template <class Q>
11    static HRESULT CreateInstance(Q** pp) {
12        return T::_CreatorClass::CreateInstance(NULL,
13            __uuidof(Q), (void**) pp);
14    }
15};

This simplifies the creation code still further:

1STDMETHODIMP CAviary::CreatePenguin(IBird** ppbird) {
2    return CPenguin::CreateInstance(ppbird);
3}

CComCreator2

You might like to support both standalone and aggregate activation using CComObject and CComAggObject instead of CComPolyObject because of the overhead associated with CComPolyObject in the standalone case. The decision can be made with a simple if statement, but then you lose the predefined CreateInstance code in CComCoClass. ATL provides CComCreator2 to make this logic fit within the existing Creator machinery:

1template <class T1, class T2> class CComCreator2 {
2public:
3    static HRESULT WINAPI CreateInstance(void* pv, REFIID riid,
4        LPVOID* ppv) {
5        ATLASSERT(*ppv == NULL);
6        return (pv == NULL) ? T1::CreateInstance(NULL, riid, ppv)
7                            : T2::CreateInstance(pv, riid, ppv);
8    }
9};

Notice that CComCreator2 is parameterized by the types of two other Creators. All CComCreator2 does is check for a NULL pUnkOuter and forward the call to one of two other Creators. So, if you’d like to use CComObject and CComAggObject instead of CComPolyObject, you can do so like this:

1class CPenguin : ... {
2public:
3    ...
4    typedef CComCreator2< CComCreator< CComObject<CPenguin> >,
5        CComCreator< CComAggObject<CPenguin> > >
6        _CreatorClass;
7};

Of course, the beauty of this scheme is that all the Creators have the same function, CreateInstance, and are exposed via a type definition of the same name, _CreatorClass. Thus, none of the server code that creates penguins needs to change if the designer of the class changes his mind about how penguins should be created.

CComFailCreator

One of the changes you might want to make to your creation scheme is to support either standalone or aggregate activation only, not both. To make this happen, you need a special Creator to return an error code to use in place of one of the Creators passed as template arguments to CComCreator2. That’s what CComFailCreator is for:

1template <HRESULT hr> class CComFailCreator {
2public:
3    static HRESULT WINAPI CreateInstance(void*, REFIID, LPVOID*)
4    { return hr; }
5};

If you’d like standalone activation only, you can use CComFailCreator as the aggregation creator template parameter:

1class CPenguin : ... {
2public:
3    ...
4    typedef CComCreator2< CComCreator< CComObject<CPenguin> >,
5        CComFailCreator<CLASS_E_NOAGGREGATION> >
6        _CreatorClass;
7};

If you’d like aggregate activation only, you can use CComFailCreator as the standalone creator parameter:

1class CPenguin : ... {
2public:
3    ...
4    typedef CComCreator2< CComFailCreator<E_FAIL>,
5        CComCreator< CComAggObject<CPenguin> > >
6        _CreatorClass;
7};

Convenience Macros

As a convenience, ATL provides the following macros in place of manually specifying the _CreatorClass type definition for each class:

 1#define DECLARE_POLY_AGGREGATABLE(x) public:\
 2  typedef ATL::CComCreator< \
 3  ATL::CComPolyObject< x > > _CreatorClass;
 4
 5#define DECLARE_AGGREGATABLE(x) public: \
 6  typedef ATL::CComCreator2< \
 7    ATL::CComCreator< ATL::CComObject< x > >, \
 8    ATL::CComCreator< ATL::CComAggObject< x > > > \
 9    _CreatorClass;
10
11#define DECLARE_NOT_AGGREGATABLE(x) public:\
12  typedef ATL::CComCreator2< \
13    ATL::CComCreator< ATL::CComObject< x > >, \
14    ATL::CComFailCreator<CLASS_E_NOAGGREGATION> > \
15    _CreatorClass;
16
17#define DECLARE_ONLY_AGGREGATABLE(x) public:\
18  typedef ATL::CComCreator2< \
19    ATL::CComFailCreator<E_FAIL>, \
20    ATL::CComCreator< ATL::CComAggObject< x > > > \
21    _CreatorClass;

Using these macros, you can declare that CPenguin can be activated both standalone and aggregated like this:

1class CPenguin : ... {
2public:
3    ...
4    DECLARE_AGGREGATABLE(CPenguin)
5};

Table4.3 summarizes the classes the Creators use to derive from your class.

Table 4.3. Creator Type-Definition Macros

Macro

Standalone

Aggregation

DECLARE_AGGREGATABLE

CComObject

CComAggObject

DECLARE_NOT_AGGREGATABLE

CComObject

DECLARE_ONLY_AGGREGATABLE

CComAggObject

DECLARE_POLY_AGGREGATABLE

CComPolyObject

CComPolyObject

Private Initialization

Creators are handy because they follow the multiphase construction sequence ATL-based objects use. However, Creators return only an interface pointer, not a pointer to the implementing class (as in IBird* instead of CPenguin*). This can be a problem if the class exposes public member functions or if member data is not available via a COM interface. Your first instinct as a former C programmer might be to simply cast the resultant interface pointer to the type you’d like:

 1STDMETHODIMP
 2CAviary::CreatePenguin(BSTR bstrName, long nWingspan,
 3    IBird** ppbird) {
 4    HRESULT hr;
 5    hr = CPenguin::_CreatorClass::CreateInstance(0,
 6        IID_IBird, (void**)ppbird);
 7    if( SUCCEEDED(hr) ) {
 8        // Resist this instinct!
 9        CPenguin* pPenguin = (CPenguin*)(*ppbird);
10        pPenguin->Init(bstrName, nWingspan);
11    }
12    return hr;
13}

Unfortunately, because QueryInterface allows interfaces of a single COM identity to be implemented on multiple C++ objects or even multiple COM objects, in many cases a cast won’t work. Instead, you should use the CreateInstance static member functions of CComObject, CComAggObject, and CComPolyObject:

 1static HRESULT WINAPI
 2CComObject::CreateInstance(CComObject<Base>** pp);
 3
 4static HRESULT WINAPI
 5CComAggObject::CreateInstance(IUnknown* puo,
 6    CComAggObject<contained>** pp);
 7
 8static HRESULT WINAPI
 9CComPolyObject::CreateInstance(IUnknown* puo,
10    CComPolyObject<contained>** pp);

These static member functions do not make Creators out of CComObject, CComAggObject, or CComPolyObject, but they each perform the additional work required to call the object’s FinalConstruct (and _AtlInitialConstruct, and so on) member functions. The reason to use them, however, is that each of them returns a pointer to the most derived class:

 1STDMETHODIMP
 2CAviary::CreatePenguin(BSTR bstrName, long nWingspan,
 3    IBird** ppbird) {
 4    HRESULT hr;
 5    CComObject<CPenguin>* pPenguin = 0;
 6    hr = CComObject<CPenguin>::CreateInstance(&pPenguin);
 7    if( SUCCEEDED(hr) ) {
 8        pPenguin->AddRef();
 9        pPenguin->Init(bstrName, nWingspan);
10        hr = pPenguin->QueryInterface(IID_IBird, (void**)ppbird);
11        pPenguin->Release();
12    }
13    return hr;
14}

The class you use for creation in this manner depends on the kind of activation you want. For standalone activation, use CComObject::CreateInstance. For aggregated activation, use CComAggObject::CreateInstance. For either standalone or aggregated activation that saves a set of vtbl s at the expense of per-instance overhead, use CComPolyObject::CreateInstance.

Multiphase Construction on the Stack

When creating an instance of an ATL-based COM object, you should always use a Creator (or the static CreateInstance member function of CComObject, et al) instead of the C++ operator new. However, if you’ve got a global or a static object, or an object that’s allocated on the stack, you can’t use a Creator because you’re not calling new. As discussed earlier, ATL provides two classes for creating instances that aren’t on the heap: CComObjectGlobal and CComObjectStack. However, instead of requiring you to call FinalConstruct (and FinalRelease) manually, both of these classes perform the proper initialization and shutdown in their constructors and destructors, as shown here in CComObjectGlobal:

 1template <class Base>
 2class CComObjectGlobal : public Base {
 3public:
 4    typedef Base _BaseClass;
 5    CComObjectGlobal(void* = NULL) {
 6        m_hResFinalConstruct = S_OK;
 7        __if_exists(FinalConstruct) {
 8            __if_exists(InternalFinalConstructAddRef) {
 9                InternalFinalConstructAddRef();
10        }
11        m_hResFinalConstruct = _AtlInitialConstruct();
12        if (SUCCEEDED(m_hResFinalConstruct))
13            m_hResFinalConstruct = FinalConstruct();
14        __if_exists(InternalFinalConstructRelease) {
15                InternalFinalConstructRelease();
16            }
17        }
18    }
19    ~CComObjectGlobal() {
20        __if_exists(FinalRelease) {
21            FinalRelease();
22        }
23    }
24    ...
25    HRESULT m_hResFinalConstruct;
26};

Because there is no return code from a constructor, if you’re interested in the result from FinalConstruct, you must check the cached result in the public member variable m_hResFinalConstruct.

Note in the previous code the use of the new __if_exists C++ keyword. This keyword allows for conditional compilation based on the presence of a symbol or member function. Derived classes, for instance, can check for the existence of particular members of a base class. Alternatively, the __if_not_exists keyword can be used to conditionally compile code based on the absence of specific symbol. These keywords are analogous to the #ifdef and #ifndef preprocessor directives, except that they operate on symbols that are not removed during the preprocessing stage.

Debugging

ATL provides a number of helpful debugging facilities, including both a normal and a categorized wrapper for producing debug output, a macro for making assertions, and debug output for tracing calls to QueryInterface, AddRef, and Release on an interface-by-interface basis. Of course, during a release build, all these debugging facilities fall away to produce the smallest, fastest binary image possible.

Making Assertions

Potentially the best debugging technique is to use assertions, which enable you to make assumptions in your code and, if those assumptions are invalidated, to be notified immediately. Although ATL doesn’t exactly support assertions, it does provide the ATLASSERT macro. However, it’s actually just another name for the Microsoft CRT macro _ASSERTE:

1#ifndef ATLASSERT
2#define ATLASSERT(expr) _ASSERTE(expr)
3#endif

Flexible Debug Output

OutputDebugString is handy as the Win32 equivalent of printf, but it takes only a single string argument. We want a printf that outputs to debug output instead of standard output. ATL provides the AtlTrace function to do exactly that:

1inline void _cdecl AtlTrace(LPCSTR pszFormat, ...)
2inline void _cdecl AtlTrace(LPCWSTR pszFormat, ...)

Instead of calling the function directly, use the macro ATLTRACE. The macro calls the underlying function, but also adds file and line number information to the trace output. The macro expands to either a call to AtlTrace or nothing, depending on whether the _DEBUG symbol is defined. Typical usage is as follows:

1HRESULT CPenguin::FinalConstruct() {
2  ATLTRACE(__TEXT("%d+%d= %d\n"), 2, 2, 2+2);
3}

ATLTRACE always generates output to the debug window. If you’d like to be even more selective about what makes it to debug output, ATL provides a second trace function, AtlTrace2, also with its own macro, ATLTRACE2:

1void AtlTrace2(DWORD_PTR dwCategory, UINT nLevel,
2    LPCSTR pszFormat, ...)
3void AtlTrace2(DWORD_PTR dwCategory, UINT nLevel,
4    LPCWSTR pszFormat, ...)

In addition to the format string and the variable arguments, AtlTrace2 takes a trace category and a trace level. The trace category is defined as an instance of the CTraceCategory class. ATL includes the following trace categories, already defined:

 1#ifdef _DEBUG
 2#define DECLARE_TRACE_CATEGORY( name ) \
 3    extern ATL::CTraceCategory name;
 4#else
 5#define DECLARE_TRACE_CATEGORY( name ) const DWORD_PTR name = 0;
 6#endif
 7
 8DECLARE_TRACE_CATEGORY( atlTraceGeneral )
 9DECLARE_TRACE_CATEGORY( atlTraceCOM )
10DECLARE_TRACE_CATEGORY( atlTraceQI )
11DECLARE_TRACE_CATEGORY( atlTraceRegistrar )
12DECLARE_TRACE_CATEGORY( atlTraceRefcount )
13DECLARE_TRACE_CATEGORY( atlTraceWindowing )
14DECLARE_TRACE_CATEGORY( atlTraceControls )
15DECLARE_TRACE_CATEGORY( atlTraceHosting )
16DECLARE_TRACE_CATEGORY( atlTraceDBClient )
17DECLARE_TRACE_CATEGORY( atlTraceDBProvider )
18DECLARE_TRACE_CATEGORY( atlTraceSnapin )
19DECLARE_TRACE_CATEGORY( atlTraceNotImpl )
20DECLARE_TRACE_CATEGORY( atlTraceAllocation )
21DECLARE_TRACE_CATEGORY( atlTraceException )
22DECLARE_TRACE_CATEGORY( atlTraceTime )
23DECLARE_TRACE_CATEGORY( atlTraceCache )
24DECLARE_TRACE_CATEGORY( atlTraceStencil )
25DECLARE_TRACE_CATEGORY( atlTraceString )
26DECLARE_TRACE_CATEGORY( atlTraceMap )
27DECLARE_TRACE_CATEGORY( atlTraceUtil )
28DECLARE_TRACE_CATEGORY( atlTraceSecurity )
29DECLARE_TRACE_CATEGORY( atlTraceSync )
30DECLARE_TRACE_CATEGORY( atlTraceISAPI )
31
32// atlTraceUser categories are no longer needed.
33// Just declare your own trace category using CTraceCategory.
34DECLARE_TRACE_CATEGORY( atlTraceUser )
35DECLARE_TRACE_CATEGORY( atlTraceUser2 )
36DECLARE_TRACE_CATEGORY( atlTraceUser3 )
37DECLARE_TRACE_CATEGORY( atlTraceUser4 )
38
39#pragma deprecated( atlTraceUser )
40#pragma deprecated( atlTraceUser2 )
41#pragma deprecated( atlTraceUser3 )
42#pragma deprecated( atlTraceUser4 )

The CTraceCategory class associates the category name with the underlying value so that it appears in the trace listing. The four atlTraceUserX categories exist for backward-compatibility; ATL versions 7 and earlier had no means of defining custom trace categories. For new code, you simply need to create a global instance of CTraceCategory like this:

 1CTraceCategory PenguinTraces( "CPenguin trace", 1 );
 2...
 3STDMETHODIMP CPenguin::Fly() {
 4    ATLTRACE2(PenguinTraces,   2,
 5        _T("IBird::Fly\n"));
 6    ATLTRACE2(PenguinTraces,   42,
 7        _T("Hmmm... Penguins can't fly...\n"));
 8    ATLTRACE2(atlTraceNotImpl, 0,
 9        _T("IBird::Fly not implemented!\n"));
10    return E_NOTIMPL;
11}

The trace level is a measure of severity, with 0 the most severe. ATL itself uses only levels 0 and 2. The documentation recommends that you stay between 0 and 4, but you can use any level up to 4,294,967,295 (although that might be a little too fine grained to be useful).

Also, because ATL uses atlTraceNotImpl so often, there’s even a special macro for it:

1#define ATLTRACENOTIMPL(funcname) \
2  ATLTRACE2(atlTraceNotImpl, 2, \
3    _T("ATL: %s not implemented.\n"), funcname); \
4  return E_NOTIMPL

This macro is used a lot in the implementations of the OLE interfaces:

1STDMETHOD(SetMoniker)(DWORD, IMoniker*) {
2    ATLTRACENOTIMPL(_T("IOleObjectImpl::SetMoniker"));
3}

Tracing Calls to QueryInterface

ATL’s implementation of QueryInterface is especially well instrumented for debugging. If you define the _ATL_DEBUG_QI symbol before compiling, your objects will output their class name, the interface being queried for (by name [10] , if available), and whether the query succeeded or failed. This is extremely useful for reverse engineering clients’ interface requirements. For example, here’s a sample of the _ATL_DEBUG_QI output when hosting a control in IE6:

CComClassFactory - IUnknown
CComClassFactory - IClassFactory
CComClassFactory - IClassFactory
CComClassFactory -  - failed
CPenguin - IUnknown
CPenguin -  - failed
CPenguin - IOleControl
CPenguin - IClientSecurity - failed
CPenguin - IQuickActivate
CPenguin - IOleObject
CPenguin - IViewObjectEx
CPenguin - IPointerInactive - failed
CPenguin - IProvideClassInfo2
CPenguin - IConnectionPointContainer - failed
CPenguin - IPersistPropertyBag2 - failed
CPenguin - IPersistPropertyBag - failed
CPenguin - IPersistStreamInit
CPenguin - IViewObjectEx
CPenguin - IActiveScript - failed
CPenguin -  - failed
CPenguin - IOleControl
CPenguin - IOleCommandTarget - failed
CPenguin - IDispatchEx - failed
CPenguin - IDispatch
CPenguin - IOleControl
CPenguin - IOleObject
CPenguin - IOleObject
CPenguin - IRunnableObject - failed
CPenguin - IOleObject
CPenguin - IOleInPlaceObject
CPenguin - IOleInPlaceObjectWindowless
CPenguin - IOleInPlaceActiveObject
CPenguin - IOleControl
CPenguin - IClientSecurity - failed

Tracing Calls to AddRef and Release

The only calls more heavily instrumented for debugging than QueryInterface are AddRef and Release. ATL provides an elaborate scheme for tracking calls to AddRef and Release on individual interfaces. It is elaborate because each ATL-based C++ class has a single implementation of AddRef and Release, implemented in the most derived class; for example, CComObject. To overcome this limitation, when _ATL_DEBUG_INTERFACES is defined, ATL wraps each new interface [11] handed out via QueryInterface in another C++ object that implements a single interface. Each of these “thunk objects” keeps track of the real interface pointer, as well as the name of the interface and the name of the class that has implemented the interface. The thunk objects also keep track of an interface pointer specific reference count that is managed, along with the object’s reference count, in the thunk object’s implementation of AddRef and Release. As calls to AddRef and Release are made, each thunk object knows exactly which interface is being used and dumps reference count information to debug output. For example, here’s the same interaction between a control and IE6, but using _ATL_DEBUG_INTERFACES instead of _ATL_DEBUG_QI:

QIThunk-1   AddRef:   Object=0x021c2c88   Refcount=1   CComClassFactory-IUnknown
IThunk-2    AddRef:   Object=0x021c2c88   Refcount=1   CComClassFactory-IClassFactory
QIThunk-2   AddRef:   Object=0x021c2c88   Refcount=2   CComClassFactory-IClassFactory
QIThunk-2   Release:  Object=0x021c2c88   Refcount=1   CComClassFactory-IClassFactory
QIThunk-3   AddRef:   Object=0x021c2c88   Refcount=1   CComClassFactory-IClassFactory
QIThunk-2   Release:  Object=0x021c2c88   Refcount=0   CComClassFactory-IClassFactory
QIThunk-4   AddRef:   Object=0x021c2e38   Refcount=1   CPenguin-IUnknown
QIThunk-5   AddRef:   Object=0x021c2e40   Refcount=1   CPenguin-IOleControl
QIThunk-5   Release:  Object=0x021c2e40   Refcount=0   CPenguin-IOleControl
QIThunk-6   AddRef:   Object=0x021c2e60   Refcount=1   CPenguin-IQuickActivate
QIThunk-7   AddRef:   Object=0x021c2e44   Refcount=1   CPenguin-IOleObject
QIThunk-8   AddRef:   Object=0x021c2e4c   Refcount=1   CPenguin-IViewObjectEx
QIThunk-9   AddRef:   Object=0x021c2e68   Refcount=1   CPenguin-IProvideClassInfo2
QIThunk-9   Release:  Object=0x021c2e68   Refcount=0   CPenguin-IProvideClassInfo2
QIThunk-8   Release:  Object=0x021c2e4c   Refcount=0   CPenguin-IViewObjectEx
QIThunk-7   Release:  Object=0x021c2e44   Refcount=0   CPenguin-IOleObject
QIThunk-6   Release:  Object=0x021c2e60   Refcount=0   CPenguin-IQuickActivate
QIThunk-10  AddRef:   Object=0x021c2e3c   Refcount=1   CPenguin-IPersistStreamInit
QIThunk-10  Release:  Object=0x021c2e3c   Refcount=0   CPenguin-IPersistStreamInit
QIThunk-11  AddRef:   Object=0x021c2e4c   Refcount=1   CPenguin-IViewObjectEx
QIThunk-12  AddRef:   Object=0x021c2e40   Refcount=1   CPenguin-IOleControl
QIThunk-12  Release:  Object=0x021c2e40   Refcount=0   CPenguin-IOleControl
QIThunk-13  AddRef:   Object=0x021c2e38   Refcount=1   CPenguin-IDispatch
QIThunk-14  AddRef:   Object=0x021c2e40   Refcount=1   CPenguin-IOleControl
QIThunk-14  Release:  Object=0x021c2e40   Refcount=0   CPenguin-IOleControl
QIThunk-3   Release:  Object=0x021c2c88   Refcount=0   CComClassFactory-IClassFactory
QIThunk-15  AddRef:   Object=0x021c2e44   Refcount=1   CPenguin-IOleObject
QIThunk-16  AddRef:   Object=0x021c2e44   Refcount=1   CPenguin-IOleObject
QIThunk-16  Release:  Object=0x021c2e44   Refcount=0   CPenguin-IOleObject
QIThunk-15  Release:  Object=0x021c2e44   Refcount=0   CPenguin-IOleObject
QIThunk-17  AddRef:   Object=0x021c2e44   Refcount=1   CPenguin-IOleObject
QIThunk-18  AddRef:   Object=0x021c2e50   Refcount=1   CPenguin-IOleInPlaceObject
QIThunk-19  AddRef:   Object=0x021c2e50   Refcount=1   CPenguin-IOleInPlaceObjectWindowless
QIThunk-20  AddRef:   Object=0x021c2e48   Refcount=1   CPenguin-IOleInPlaceActiveObject
QIThunk-20  Release:  Object=0x021c2e48   Refcount=0   CPenguin-IOleInPlaceActiveObject
QIThunk-18  Release:  Object=0x021c2e50   Refcount=0   CPenguin-IOleInPlaceObject
QIThunk-19  AddRef:   Object=0x021c2e50   Refcount=2   CPenguin-IOleInPlaceObjectWindowless
QIThunk-17  Release:  Object=0x021c2e44   Refcount=0   CPenguin-IOleObject
QIThunk-19  Release:  Object=0x021c2e50   Refcount=1   CPenguin-IOleInPlaceObjectWindowless
QIThunk-21  AddRef:   Object=0x021c2e40   Refcount=1   CPenguin-IOleControl
QIThunk-21  Release:  Object=0x021c2e40   Refcount=0   CPenguin-IOleControl
QIThunk-22  AddRef:   Object=0x021c2e44   Refcount=1   CPenguin-IOleObject
QIThunk-23  AddRef:   Object=0x021c2e50   Refcount=1   CPenguin-IOleInPlaceObject
QIThunk-19  Release:  Object=0x021c2e50   Refcount=0   CPenguin-IOleInPlaceObjectWindowless
QIThunk-23  Release:  Object=0x021c2e50   Refcount=0   CPenguin-IOleInPlaceObject
QIThunk-22  Release:  Object=0x021c2e44   Refcount=0   CPenguin-IOleObject
QIThunk-24  AddRef:   Object=0x021c2e44   Refcount=1   CPenguin-IOleObject
QIThunk-25  AddRef:   Object=0x021c2e50   Refcount=1   CPenguin-IOleInPlaceObject
QIThunk-25  Release:  Object=0x021c2e50   Refcount=0   CPenguin-IOleInPlaceObject
QIThunk-24  Release:  Object=0x021c2e44   Refcount=0   CPenguin-IOleObject
QIThunk-13  Release:  Object=0x021c2e38   Refcount=0   CPenguin-IDispatch
QIThunk-11  Release:  Object=0x021c2e4c   Refcount=0   CPenguin-IViewObjectEx
QIThunk-26  AddRef:   Object=0x021c2e44   Refcount=1   CPenguin-IOleObject
QIThunk-26  Release:  Object=0x021c2e44   Refcount=0   CPenguin-IOleObject
QIThunk-4   Release:  Object=0x021c2e38   Refcount=0   CPenguin-IUnknown
QIThunk-1   Release:  Object=0x021c2c88   Refcount=0   CComClassFactory-IUnknown

ATL maintains a list of outstanding thunk objects. This list is used at server shutdown to detect any leaks; that is, any interfaces that the client has not released. When using _ATL_DEBUG_INTERFACES, watch your debug output for the string LEAK, which is an indication that someone has mismanaged an interface reference:

ATL: QIThunk - 4 LEAK: Object = 0x00962920 Refcount = 4
  MaxRefCount = 4 CCalc - ICalc

The most useful part of this notification is the index of QI thunk object. You can use this to track when the leaked interface is acquired by using the CAtlDebugInterfacesModule class. This is the class that manages the thunk objects during debug builds, and a global instance of this class called _AtlDebugInterfacesModule is automatically included in your class when the _ATL_DEBUG_INTERFACES symbol is defined. You can instruct the debugger to break at the appropriate time by setting the m_nIndexBreakAt member of the CAtlDebugInterfacesModule at server start-up time.

 1extern "C"
 2BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason,
 3    LPVOID lpReserved) {
 4    hInstance;
 5    BOOL b = _AtlModule.DllMain(dwReason, lpReserved);
 6    // Trace down interface leaks
 7#ifdef _ATL_DEBUG_INTERFACES
 8    _AtlDebugInterfacesModule.m_nIndexBreakAt = 4;
 9#endif
10    return b;
11}

When that interface thunk is allocated, _AtlDebugInterfacesModule calls DebugBreak, handing control over to the debugger and allowing you to examine the call stack and plug the leak.

_ATL_DEBUG_REFCOUNT

Versions of ATL earlier than version 3 used the _ATL_DEBUG_REFCOUNT symbol to track interface reference counts for ATL IXxxImpl classes only. Because _ATL_DEBUG_INTERFACES is much more general, it has replaced _ATL_DEBUG_REFCOUNT, although _ATL_DEBUG_REFCOUNT is still supported for backward compatibility.

1#ifdef _ATL_DEBUG_REFCOUNT
2#ifndef _ATL_DEBUG_INTERFACES
3#define _ATL_DEBUG_INTERFACES
4#endif
5#endif

Summary

ATL provides a layered approach to implementing IUnknown. The top layer, represented by the CComXxxThreadModel classes, provides helper functions and type definitions for synchronization required of both STAs and MTAs. The second level, CComObjectRootEx, uses the threading model classes to support “just thread-safe enough”AddRef and Release implementations and object-level locking. CComObjectRootEx also provides a table-driven implementation of QueryInterface, using an interface map provided by your class. Your class derives from CComObjectRootEx and any number of interfaces, providing the interface member function implementations. The final level is provided by CComObject and friends, which provide the implementation of QueryInterface, AddRef, and Release based on the lifetime and identity requirements of the object.

To allow each class to define its one lifetime and identity requirements, each class defines its own _CreatorClass, which defines the appropriate Creator. The Creator is responsible for properly creating an instance of your ATL-base class and should be used in place of the C++ operator new.

Finally, to debug your objects, ATL provides a number of debugging facilities, including tracing and interface usage and leak tracking.