Figure 2   ATL Object Wizard Types

Object Type Supported Interfaces Notes
Simple Object None  
Add-in Object IDSAddIn Maintains a pointer to Developer Studio's IApplication interface
Internet Explorer Object IObjectWithSite Maintains a pointer to a site
ActiveX Server Component None Supports OnStartPage/OnEndPage and maintains pointers to ASP-supplied interfaces
Microsoft Transaction Server Object IObjectControl (opt) Maintains a pointer to a Microsoft Transaction Server object context
Component Registrar Object IComponentRegistrar Supports registering all CLSIDs in a module
Internet Explorer Control
IViewObject
IViewObject2
IViewObjectEx
IOleWindow
IOleInPlaceObject
IOleInPlaceObjectWindowless
IOleInPlaceActiveObject
IOleControl
IOleObject
IPersistStreamInit
Maintains pointers to site's IOleInPlaceSiteWindowless,
IOleClientSite, and IAdviseSink interfaces
Full Control All Internet Explorer control
interfaces plus
IQuickActivate
IPersistStorage
ISpecifyPropertyPages
IDataObject
IProvideClassInfo
IProvideClassInfo2
Maintains pointers to a site's IOleInPlaceSiteWindowless,
IOleClientSite, and IAdviseSink interfaces
Property Page IPropertyPage 


Figure 3   Hello

HelloATL.cpp

////////////////////////////////////////////////////
//
// HelloATL.cpp - 1997, Don Box
//
// An ATL-based implementation of a COM in-process server
//

#include <windows.h>
#include "hello.h"
#define IID_DEFINED
#include "hello_i.c"

#include <atlbase.h>
extern CComModule _Module;
#include <atlcom.h>
#include <atlimpl.cpp>

class ATL_NO_VTABLE CHelloATL 
  :   public CComObjectRootEx<CComMultiThreadModel>,
      public CComCoClass<CHelloATL, &CLSID_HelloATL>,
      public IHello
{
public:
BEGIN_COM_MAP(CHelloATL)
  COM_INTERFACE_ENTRY(IHello)
END_COM_MAP()

  DECLARE_REGISTRY_RESOURCEID(1)

  STDMETHODIMP Hello(BSTR bstr)
  {
      MessageBoxW(0, bstr ? bstr : OLESTR(""), L"Hello!", MB_SETFOREGROUND);
      return S_OK;
  }
};

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
  OBJECT_ENTRY(CLSID_HelloATL, CHelloATL)
END_OBJECT_MAP()

BOOL WINAPI DllMain(HINSTANCE h, DWORD dwReason, void *)
{
  if (dwReason == DLL_PROCESS_ATTACH)
      _Module.Init(ObjectMap, h);
  else if (dwReason == DLL_PROCESS_DETACH)
      _Module.Term();
  return TRUE;
}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{ return _Module.GetClassObject(rclsid, riid, ppv); }

STDAPI DllCanUnloadNow(void)
{ return _Module.GetLockCount() ? S_FALSE : S_OK; }

STDAPI DllRegisterServer(void)
{ return _Module.RegisterServer(TRUE); }

STDAPI DllUnregisterServer(void)
{ return _Module.UnregisterServer(); }

HelloATL.rc

1   TYPELIB "hello.tlb"
1   REGISTRY "HelloATL.rgs"

HelloATL.rgs

HKCR {
  NoRemove CLSID {
      ForceRemove {D50841E2-9AAA-11d0-8C20-0080C73925BA} = s 'HelloATL' {
          InprocServer32 = s '%MODULE%' {
              val ThreadingModel = s 'Both'
          }
      }
  }
}

HelloSDK.cpp

////////////////////////////////////////////////////
//
// HelloSDK.cpp - 1997, Don Box
//
// An SDK-based implementation of a COM in-process server
//

#include <windows.h>
#include "hello.h"
#define IID_DEFINED
#include "hello_i.c"

LONG g_cLocks = 0;
inline LONG LockModule(void) { return InterlockedIncrement(&g_cLocks); }
inline LONG UnlockModule(void) { return InterlockedDecrement(&g_cLocks); }

class CHelloSDK : public IHello {
  LONG m_dwRef;
public:
  CHelloSDK(void) : m_dwRef(0) { LockModule(); }
  virtual ~CHelloSDK(void) { UnlockModule(); }

  STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {
      if (riid == IID_IUnknown || riid == IID_IHello)
          *ppv = (IHello*)this;
      else
          return (*ppv = 0), E_NOINTERFACE;
      ((IUnknown*)*ppv)->AddRef();
      return S_OK;
  }

  STDMETHODIMP_(ULONG) AddRef(void) 
  { return InterlockedIncrement(&m_dwRef); }

  STDMETHODIMP_(ULONG) Release(void) { 
      LONG res = InterlockedIncrement(&m_dwRef); 
      if (res == 0)
          delete this;
      return res;
  }

  STDMETHODIMP Hello(BSTR bstr) {
      MessageBoxW(0, bstr ? bstr : OLESTR(""), L"Hello!", MB_SETFOREGROUND);
      return S_OK;
  }
};

HINSTANCE g_hInstance = 0;

BOOL WINAPI DllMain(HINSTANCE h, DWORD dwReason, void *) {
  if (dwReason == DLL_PROCESS_ATTACH)
      g_hInstance = h;
  return TRUE;
}

class CHelloClassObject : public IClassFactory {
public:
  STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {
      if (riid == IID_IUnknown || riid == IID_IClassFactory)
          *ppv = (IClassFactory *)this;
      else
          return (*ppv = 0), E_NOINTERFACE;
      ((IUnknown*)*ppv)->AddRef();
      return S_OK;
  }

  STDMETHODIMP_(ULONG) AddRef(void) { return LockModule(); }
  STDMETHODIMP_(ULONG) Release(void) { return UnlockModule(); }

 
  STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv) {
      *ppv = 0;
      if (pUnkOuter) return CLASS_E_NOAGGREGATION;
      CHelloSDK *p = new CHelloSDK;
      if (!p) return E_OUTOFMEMORY;
      p->AddRef();
      HRESULT hr = p->QueryInterface(riid, ppv);
      p->Release();
      return hr;
  }

  STDMETHODIMP LockServer(BOOL b) 
  { return (b ? LockModule() : UnlockModule()), S_OK; }
};

CHelloClassObject g_classObject;

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv) { 
  if (rclsid == CLSID_HelloSDK)
      return g_classObject.QueryInterface(riid, ppv);
  return (*ppv = 0), CLASS_E_CLASSNOTAVAILABLE;
}

STDAPI DllCanUnloadNow(void)
{ return g_cLocks ? S_FALSE : S_OK; }

STDAPI DllRegisterServer(void) { 
  char szFileName[MAX_PATH]; OLECHAR wszFileName[MAX_PATH];
  GetModuleFileName(g_hInstance, szFileName, MAX_PATH);
  mbstowcs(wszFileName, szFileName, MAX_PATH);

  ITypeLib *ptl = 0;
  HRESULT hr = LoadTypeLib(wszFileName, &ptl);
  if (FAILED(hr)) return hr;
  hr = RegisterTypeLib(ptl, wszFileName, 0);
  ptl->Release();
  if (FAILED(hr)) return hr;

  LONG err = RegSetValueA(HKEY_CLASSES_ROOT, 
                          "CLSID\\{D50841E3-9AAA-11d0-8C20-0080C73925BA}",
                          REG_SZ, "HelloSDK", 9);
  if (err != ERROR_SUCCESS) goto error_exit;

  HKEY hkey;
  err = RegCreateKeyA(HKEY_CLASSES_ROOT, 
                      "CLSID\\{D50841E3-9AAA-11d0-8C20-0080C73925BA}"
                      "\\InprocServer32", &hkey);
  if (err != ERROR_SUCCESS) goto error_exit;

  err = RegSetValueExA(hkey, 0, 0, REG_SZ, (BYTE*)szFileName, 
                       strlen(szFileName) + 1);
  if (err != ERROR_SUCCESS) 
      err = RegSetValueExA(hkey,"ThreadingModel",0,REG_SZ, (BYTE*)"Both", 5);
  RegCloseKey(hkey);
error_exit:
  return (err == ERROR_SUCCESS) ? S_OK :
          MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, err);
}

STDAPI DllUnregisterServer(void) { 
  LONG err = RegDeleteKeyA(HKEY_CLASSES_ROOT, 
                           "CLSID\\{D50841E3-9AAA-11d0-8C20-0080C73925BA}"
                           "\\InprocServer32");
  if (err != ERROR_SUCCESS) goto error_exit;
  
  err = RegDeleteKeyA(HKEY_CLASSES_ROOT, 
                      "CLSID\\{D50841E3-9AAA-11d0-8C20-0080C73925BA}");
error_exit:
  return (err == ERROR_SUCCESS) ? S_OK :
          MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, err);
}

HelloSDK.rc

1   TYPELIB "Hello.tlb"


Figure 4   CComPtr

template <class T>
class CComPtr {
public:
       typedef T _PtrClass;
       CComPtr() {p=NULL;}
       CComPtr(T* lp) {
               if ((p = lp) != NULL)
                       p->AddRef();
       }
       CComPtr(const CComPtr<T>& lp) {
               if ((p = lp.p) != NULL)
                       p->AddRef();
       }
       ~CComPtr() {if (p) p->Release();}
       void Release() {if (p) p->Release(); p=NULL;}
       operator T*() {return (T*)p;}
       T& operator*() {_ASSERTE(p!=NULL); return *p; }
       T** operator&() { _ASSERTE(p==NULL); return &p; }
       T* operator->() { _ASSERTE(p!=NULL); return p; }
       T* operator=(T* lp){return (T*)AtlComPtrAssign((IUnknown**)&p, lp);}
       T* operator=(const CComPtr<T>& lp) {
               return (T*)AtlComPtrAssign((IUnknown**)&p, lp.p);
       }
       bool operator!(){return (p == NULL);}
       T* p;
};

template <class T, const IID* piid>
class CComQIPtr
{
public:
       typedef T _PtrClass;
       CComQIPtr() {p=NULL;}
   CComQIPtr(T* lp) {
               if ((p = lp) != NULL)
                       p->AddRef();
       }
       CComQIPtr(const CComQIPtr<T,piid>& lp) {
               if ((p = lp.p) != NULL)
                       p->AddRef();
       }
       CComQIPtr(IUnknown* lp)        {
               p=NULL;
               if (lp != NULL)
                       lp->QueryInterface(*piid, (void **)&p);
       }
       ~CComQIPtr() {if (p) p->Release();}
       void Release() {if (p) p->Release(); p=NULL;}
       operator T*() {return p;}
       T& operator*() {_ASSERTE(p!=NULL); return *p; }
       T** operator&() { _ASSERTE(p==NULL); return &p; }
       T* operator->() {_ASSERTE(p!=NULL); return p; }
       T* operator=(T* lp){return (T*)AtlComPtrAssign((IUnknown**)&p, lp);}
   T* operator=(const CComQIPtr<T,piid>& lp) {
               return (T*)AtlComPtrAssign((IUnknown**)&p, lp.p);
       }
       T* operator=(IUnknown* lp) {
               return (T*)AtlComQIPtrAssign((IUnknown**)&p, lp, *piid);
       }
       bool operator!(){return (p == NULL);}
       T* p;
};

Figure 5   COM Operators

Operator Description
operator & Returns address of raw pointer
operator * Returns dereferenced pointer
operator T * Returns raw pointer
operator -> Returns raw pointer
operator ! Used to test nullness
operator bool Used to test nullness


Figure 6   ATL Typedef Translations

ATL Typedef
_ATL_SINGLE_THREADED Translation
_ATL_APARTMENT_THREADED Translation
_ATL_FREE_THREADED Translation
CComGlobalsThreadModel
CComSingleThreadModel
CComMultiThreadModel
CComMultiThreadModel
CComObjectThreadModel
CComSingleThreadModel
CComSingleThreadModel
CComMultiThreadModel


Figure 7   Parameterized Threading


 class CPager : public IPager {
   LONG m_dwRef;
   typedef CComObjectThreadModel _ThreadModel;
   _ThreadModel::CComAutoCriticalSection m_critsec;
     :   :   :   :
   STDMETHODIMP_(ULONG) CPager::AddRef() {
     return _ThreadModel::Increment(&m_dwRef); 
   }
   STDMETHODIMP_(ULONG) CPager::Release(){
     ULONG res = _ThreadModel::Decrement(&m_dwRef);
     if (res == 0)
       delete this;
     return res;
   }
   STDMEHTHODIMP SendUrgentMessage() {
 // ensure that we are only thread   
     m_critsec.Lock(); 
 // perform work
     this->GenerateMessage();
     this->WakeUpUser();
 // allow other threads
     m_critsec.Unlock();
     return S_OK;
   }
 };

Figure 9   CComObjectRoot


 class CComObjectRootBase {
 public:
 // C++ constuctor
   CComObjectRootBase() { m_dwRef = 0L; }
 
 // ATL psuedo-constructor and and psuedo-destructors
   HRESULT FinalConstruct() { return S_OK; } 
   void FinalRelease() {}
 
 // Inner Unknown function (InternalAddRef/Release supplied by derived class)
   static HRESULT WINAPI InternalQueryInterface(void* pThis, 
             const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject) {
     HRESULT hRes = AtlInternalQueryInterface(pThis,pEntries,iid,ppvObject);
     return _ATLDUMPIID(iid, pszClassName, hRes);
   }
 
 // Outer Unknown functions
   ULONG OuterAddRef()	 { return m_pOuterUnknown->AddRef(); }
   ULONG OuterRelease() { return m_pOuterUnknown->Release(); }
   HRESULT OuterQueryInterface(REFIID iid, void ** ppvObject) 
   { return m_pOuterUnknown->QueryInterface(iid, ppvObject); }
 
 // ATL creator hook routines
   void SetVoid(void*) {}
   void InternalFinalConstructAddRef() {}
   void InternalFinalConstructRelease() {}
 
 // ATL interface map helper functions
   static HRESULT WINAPI _Break(       void*, REFIID, void**, DWORD);
   static HRESULT WINAPI _NoInterface( void*, REFIID, void**, DWORD);
   static HRESULT WINAPI _Creator(     void*, REFIID, void**, DWORD);
   static HRESULT WINAPI _Delegate(    void*, REFIID, void**, DWORD);
   static HRESULT WINAPI _Chain(       void*, REFIID, void**, DWORD);
   static HRESULT WINAPI _Cache(       void*, REFIID, void**, DWORD);
 
 // The actual reference count OR the back pointer to the real Unknown
   union {
     long m_dwRef;
     IUnknown* m_pOuterUnknown;
   };
 };
 
 template <class ThreadModel>
 class CComObjectRootEx : public CComObjectRootBase {
 public:
   typedef ThreadModel _ThreadModel;
   typedef _ThreadModel::AutoCriticalSection _CritSec;
 
 // Inner Unknown function (InternalQueryInterface supplied by   
                           CComObjectRootBase)
   ULONG InternalAddRef()  { return _ThreadModel::Increment(&m_dwRef); }
   ULONG InternalRelease()        { return _ThreadModel::Decrement(&m_dwRef); }
 
 // Object-level lock operations
   void Lock() {m_critsec.Lock();}
   void Unlock() {m_critsec.Unlock();}
 private:
   _CritSec m_critsec;
 };

Figure 10   ATL Interface Map Macros

Macro
Raw Equivalent
Notes
COM_INTERFACE_ENTRY(X)
if (riid == IID_X)
*ppv = (X*)this;
Normal case for multiple inheritance-based interfaces
COM_INTERFACE_ENTRY2(X, Y)
if (riid == IID_X)
*ppv = (X*)(Y*)this;
Used to resolve intermediate interfaces like IDispatch for dual interfaces
COM_INTERFACE_ENTRY_
BREAK(X)
if (riid == IID_X)
DebugBreak();
Used to trigger the debugger when an interface is requested
COM_INTERFACE_ENTRY_
NOINTERFACE(X)
if (riid == IID_X)
return (*ppv = 0), E_NOINTERFACE;
Used to disable an interface that may be implemented in a base class
COM_INTERFACE_ENTRY_
IID(X, Y)
if (riid == X)
*ppv = (Y*)this;
Used to resolve intermediate interfaces like IDispatch for dual interfaces
COM_INTERFACE_ENTRY_
IMPL(X)
if (riid == IID_X)
*ppv = (XImpl<ThisClass>*)this;
Used to export ATL-based implementations that do not derive from their interfaces
COM_INTERFACE_ENTRY_
IMPL_IID(X, Y)
if (riid == X)
*ppv = (YImpl<ThisClass>*)this;
Used to export ATL-based implementations that do not derive from their interfaces
COM_INTERFACE_ENTRY2_IID(iid, X, Y)
if (riid == iid)
*ppv = (X*)(Y*)this;
Used to resolve intermediate interfaces like IDispatch for dual interfaces
COM_INTERFACE_ENTRY_
FUNC(iid,dw, func)
if (riid == iid)
return func(this, riid, ppv, dw);
Used to map an arbitrary function to an IID
COM_INTERFACE_ENTRY_FUNC_
BLIND(dw,func)
if (TRUE)
return func(this, riid, ppv, dw);
Used to map an arbitrary function to a position in the map
COM_INTERFACE_ENTRY_
TEAR_OFF(iid, X)
if (riid == iid)
*ppv = new CComTearOffObject<X>;
Used to create a new tear-off
COM_INTERFACE_ENTRY_
CACHED_TEAR_OFF(iid,X,punk)
if (riid == iid) {
if (!this->punk)
this->punk = new CComTearOffObject<X>
*ppv = this->punk;
}
Used to create a cached tear-off
COM_INTERFACE_ENTRY_
AGGREGATE(iid, punk)
if (riid == iid)
return this->punk->QueryInterface(riid,ppv);
Used to give out an aggregate that is created in a constructor for a given interface
COM_INTERFACE_ENTRY_
AGGREGATE_BLIND(punk)
return this->punk-> QueryInterface(riid,ppv);
Used to give out an aggregate that is created in a constructor
COM_INTERFACE_ENTRY_
AUTOAGGREGATE(i, punk,clsid)
if (riid == i) {
if (!this->punk)
hr = CoCreateInstance(clsid, this, CLSCTX_ALL,
IID_IUnknown, &this->punk);
if (this->punk)
return this->punk->QueryInterface(riid,ppv);
}
Used to give out an aggregate that is created on demand for a given IID
COM_INTERFACE_ENTRY_
AUTOAGGREGATE_BLIND(punk,clsid)
if (!this->punk)
hr = CoCreateInstance(clsid, this, CLSCTX_ALL,
IID_IUnknown, &this->punk);
if (this->punk)
return this->punk->QueryInterface(riid,ppv);
Used to give out an aggregate that is created on demand
COM_INTERFACE_ENTRY_
CHAIN(basename)
return basename::QueryInterface(riid, ppv);
Used to delegate to a base class's map


Figure 11   CComObject and Friends

Classname
Locks Server
Delegates IUnknown
Deletes Object
Notes
CComObject
Yes
No
Yes
The normal case
CComObjectCached
Yes (after 2nd AddRef)
No
Yes
Used for objects that are held via pointers internally
CComObjectNoLock
No
No
Yes
Used for objects that don't hold the server running
CComObjectGlobal
Yes (after 1st AddRef)
No
No
Useful for global variables
CComObjectStack
No
No
No
Useful for stack-based variables that cannot be AddRefed
CComContainedObject
No
Yes
No
Useful for MFC-style nested classes
CComAggObject
Yes
Yes
Yes
Used for aggregate-only implementations
CComPolyObject
Yes
Yes (if aggregated)
Yes
Used for aggregate/nonaggregate implementations
CComTearOffObject
No
Yes (Query-Interface only)
Yes
Used for tear-offs that are created at each request
CComCachedTearOffObject
No
Yes (through 2nd IUnknown)
Yes
Used for tear-offs that are created at the first request and cached


Figure 12   CComObject


 template <class Base>
 class CComObjectNoLock : public Base {
 public:
         typedef Base _BaseClass;
         CComObjectNoLock(void* = NULL){}
         ~CComObjectNoLock() {m_dwRef = 1L; FinalRelease();}
 
         STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
         STDMETHOD_(ULONG, Release)() {
                 ULONG l = InternalRelease();
                 if (l == 0)
                         delete this;
                 return l;
         }
         STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
         {return _InternalQueryInterface(iid, ppvObject);}
 };
 
 template <class Base>
 class CComObject : public Base {
 public:
         typedef Base _BaseClass;
         CComObject(void* = NULL) { _Module.Lock(); }
         ~CComObject() {m_dwRef = 1L; FinalRelease(); _Module.Unlock();
         }
 
         STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
         STDMETHOD_(ULONG, Release)() {
                 ULONG l = InternalRelease();
                 if (l == 0)
                         delete this;
                 return l;
         }
         STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
         {return _InternalQueryInterface(iid, ppvObject);}
         static HRESULT WINAPI CreateInstance(CComObject<Base>** pp);
 };

Figure 13   ATL Creator


 template <class T1> class CComCreator {
 public:
     static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv) {
                 HRESULT hRes = E_OUTOFMEMORY;
                 T1* p = NULL;
                 ATLTRY(p = new T1(pv))
                 if (p != NULL) {
                         p->SetVoid(pv);
                         p->InternalFinalConstructAddRef();
                         hRes = p->FinalConstruct();
                         p->InternalFinalConstructRelease();
                         if (hRes == S_OK)
                                 hRes = p->QueryInterface(riid, ppv);
                         if (hRes != S_OK)
                                 delete p;
                 }
                 return hRes;
         }
 };
 
 template <HRESULT hr> class CComFailCreator {
 public:
         static HRESULT WINAPI CreateInstance(void*, REFIID, 
                                              LPVOID*)
     { return hr; }
 };
 
 template <class T1, class T2> class CComCreator2 {
 public:
         static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, 
                                              LPVOID* ppv) {
                 HRESULT hRes = E_OUTOFMEMORY;
                 if (pv == NULL)
                         hRes = T1::CreateInstance(NULL, riid, ppv);
                 else
                         hRes = T2::CreateInstance(pv, riid, ppv);
                 return hRes;
         }
 };