| 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;
}
};