Standalone Reference Counting
template <class ThreadModel> class CComObjectRootEx : public CComObjectRootBase
中的类型定义:
typedef ThreadModel _ThreadModel;
typedef typename _ThreadModel::AutoCriticalSection _CritSec;
typedef typename _ThreadModel::AutoDeleteCriticalSection _AutoDelCritSec;
typedef CComObjectLockT<_ThreadModel> ObjectLock;
数据部分:
_AutoDelCritSec m_critsec;(在CComObjectRootEx<CComSingleThreadModel>该部分未使用)
其中:
template <class ThreadModel> class CcomObjectLockT { public: CComObjectLockT(CComObjectRootEx<ThreadModel>* p) { if (p) p->Lock(); m_p = p; } ~CComObjectLockT() { if (m_p) m_p->Unlock(); } CComObjectRootEx<ThreadModel>* m_p; }; template <> class CComObjectLockT<CComSingleThreadModel> { public: CComObjectLockT(CComObjectRootEx<CComSingleThreadModel>*) {} ~CComObjectLockT() {} };
InternalAddRef()调用_ThreadModel::Increment(&m_dwRef)
InternalRelease()调用return _ThreadModel::Decrement(&m_dwRef)
CComObjectRootBase
class CComObjectRootBase { public: CComObjectRootBase() { m_dwRef = 0L; } ... ULONG OuterAddRef() { return m_pOuterUnknown->AddRef(); } ULONG OuterRelease() { return m_pOuterUnknown->Release(); } HRESULT OuterQueryInterface(REFIID iid, void ** ppvObject) { return m_pOuterUnknown->QueryInterface(iid, ppvObject); } ... union { long m_dwRef; //stand alone IUnknown* m_pOuterUnknown; //Aggregate }; };
FinalConstruct和FinalRelease
由于COM创建对象的复杂性,该类还提供了空的函数,派生类可以override:
HRESULT FinalConstruct();
void FinalRelease();
CComObject::~CComObject() { m_dwRef = -(LONG_MAX/2); FinalRelease(); _AtlModule->Unlock(); }
在创建对象时,有时会发生提前Release现象,所以需要提前AddRef,如:
STDMETHODIMP CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv) { *ppv = 0; if( !pUnkOuter ) { CComObject<CPenguin>* pobj = new CComObject<CPenguin>; if( FAILED(hr) ) return E_OUTOFMEMORY; // Protect object from pre-mature destruction (maybe) pobj->InternalFinalConstructAddRef(); hr = pobj->FinalConstruct(); pobj->InternalFinalConstructRelease(); if( SUCCEEDED(hr) ) ... return hr; } ... }
DECLARE_PROTECT_FINAL_CONSTRUCT宏
CComObjectRootBase已经定义了2个空函数:
class CComObjectRootBase { public: ... void InternalFinalConstructAddRef() {} void InternalFinalConstructRelease() { ATLASSERT(m_dwRef == 0); } ... };
ATL则定义了宏加以扩展:
#define DECLARE_PROTECT_FINAL_CONSTRUCT() \ void InternalFinalConstructAddRef() { InternalAddRef(); } \ void InternalFinalConstructRelease() { InternalRelease(); }
用户可以这样使用:
class CPenguin : ... {
public:
HRESULT FinalConstruct();
DECLARE_PROTECT_FINAL_CONSTRUCT()
...
};
注意:为了安全,每一个重新定义FinalConstruct的类都应该加这个宏
ATL_NO_VTABLE
如果使用了这个宏,在构造函数中禁止使用虚函数;的确要使用,应该放到FinalConstruct()中
Table-Driven QueryInterface
static HRESULT WINAPI CComObjectRootBase::InternalQueryInterface( void* pThis, //object's this,注意该函数为static const _ATL_INTMAP_ENTRY* pEntries, //表头 REFIID iid, void** ppvObject); //[out]vptr
pEntry使用一个表结构,每一个entry都对应一个interface:
struct _ATL_INTMAP_ENTRY { const IID* piid; DWORD dw; //编译时确定的interface和pThis的offset _ATL_CREATORARGFUNC* pFunc; //_ATL_SIMPLEMAPENTRY等等 };
该结构还提供一个GetUnknown(),可以得到object的IUnknown*,可以消除多重继承的二义性,例如:
HRESULT FlyInAnAirplane(IUnknown* punkPassenger); // Penguin.cpp STDMETHODIMP CPenguin::Fly() { return FlyInAnAirplane(this); // ambiguous }
STDMETHODIMP CPenguin::Fly() { return FlyInAnAirplane(this->GetUnknown()); // unambiguous }
Your Class
支持IDispatch:IDispatchImpl<IPenguin, &IID_IPenguin>
支持多重继承:COM_INTERFACE_ENTRY2(itf, branch),
但是注意下面代码,在custom环境下没有问题,但是在script环境下编程语言不支持QueryInterface,所以ISnappyDresser并不能被访问到:
class CPenguin : public CComObjectRootEx<CComSingleThreadModel>, public IDispatchImpl<IBird, &IID_IBird>, public IDispatchImpl<ISnappyDresser, &IID_ISnappyDresser> { public: BEGIN_COM_MAP(CPenguin) COM_INTERFACE_ENTRY(IBird) COM_INTERFACE_ENTRY(ISnappyDresser) COM_INTERFACE_ENTRY2(IDispatch, IBird) // Compiles // (unfortunately) END_COM_MAP() ... };
// Since IBird is the default, its operations are available
penguin.fly
// Since ISnappyDresser is not the default, its operations
// aren't available
penguin.straightenTie // runtime error
CComObject Et Al
由于CComObjectRootEx并未定义IUnknown的3个方法,所以下面的代码是会导致编译错误的:
STDMETHODIMP
CPenguinCO::CreateInstance(IUnknown* pUnkOuter,
REFIID riid, void** ppv) {
...
CPenguin* pobj = new CPenguin; // IUnknown not implemented
...
}
Standalone Activation
ATL定义了CComObject用于standalone
template <class Base> class CComObject : public Base { public: typedef Base _BaseClass; CComObject(void* = NULL) { _pAtlModule->Lock(); } // Keeps server loaded // Set refcount to -(LONG_MAX/2) to protect destruction and // also catch mismatched Release in debug builds ~CComObject() { m_dwRef = -(LONG_MAX/2); FinalRelease(); #ifdef _ATL_DEBUG_INTERFACES _AtlDebugInterfacesModule.DeleteNonAddRefThunk( _GetRawUnknown()); #endif _pAtlModule->Unlock(); // Allows server to unload } 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 Q> HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp) { return QueryInterface(__uuidof(Q), (void**)pp); } static HRESULT WINAPI CreateInstance(CComObject<Base>** pp) ; };
例子:
STDMETHODIMP CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv) { *ppv = 0; if( pUnkOuter ) return CLASS_E_NOAGGREGATION; // Read on for why not to use new like this! CComObject<CPenguin>* pobj = new CComObject<CPenguin>; //注意这也是不对的,后面将会讲到为什么不使用new if( pobj ) { pobj->AddRef(); HRESULT hr = pobj->QueryInterface(riid, ppv); pobj->Release(); return hr; } return E_OUTOFMEMORY; }
Aggregated Activation
template <class contained> class CComAggObject : public IUnknown, public CComObjectRootEx< contained::_ThreadModel::ThreadModelNoCS> { public: typedef contained _BaseClass; CComAggObject(void* pv) : m_contained(pv) { _pAtlModule->Lock(); } ~CComAggObject() { m_dwRef = -(LONG_MAX/2); FinalRelease(); _pAtlModule->Unlock(); } STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) { ATLASSERT(ppvObject != NULL); if (ppvObject == NULL) return E_POINTER; *ppvObject = NULL; HRESULT hRes = S_OK; if (InlineIsEqualUnknown(iid)) { *ppvObject = (void*)(IUnknown*)this; AddRef(); } else hRes = m_contained._InternalQueryInterface(iid, ppvObject); return hRes; } STDMETHOD_(ULONG, AddRef)() { return InternalAddRef(); } STDMETHOD_(ULONG, Release)() { ULONG l = InternalRelease(); if (l == 0) delete this; return l; } template <class Q> HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp) { return QueryInterface(__uuidof(Q), (void**)pp); } static HRESULT WINAPI CreateInstance(LPUNKNOWN pUnkOuter, CComAggObject<contained>** pp); CComContainedObject<contained> m_contained; };
template <class Base> class CComContainedObject : public Base { public: typedef Base _BaseClass; CComContainedObject(void* pv) { m_pOuterUnknown = (IUnknown*)pv; } STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) { return OuterQueryInterface(iid, ppvObject); } STDMETHOD_(ULONG, AddRef)() { return OuterAddRef(); } STDMETHOD_(ULONG, Release)() { return OuterRelease(); } template <class Q> HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp) { return QueryInterface(__uuidof(Q), (void**)pp); } IUnknown* GetControllingUnknown() { return m_pOuterUnknown; } };
例子:
CComAggObject<CPenguin>* pobj = new CComAggObject<CPenguin>(pUnkOuter);
CComPolyObject
class CComPolyObject : public IUnknown, public CComObjectRootEx< contained::_ThreadModel::ThreadModelNoCS> { public: ... CComPolyObject(void* pv) : m_contained(pv ? pv : this) {...} ... };
用于选择性判断是否内聚,例子:
STDMETHODIMP CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv) { *ppv = 0; CComPolyObject<CPenguin>* pobj = new CComPolyObject<CPenguin>(pUnkOuter); ... }
CComObjectCached
template <class Base> class CComObjectCached : public Base { public: ... STDMETHOD_(ULONG, AddRef)() { ULONG l = InternalAddRef(); if (l == 2) _pAtlModule->Lock(); return l; } STDMETHOD_(ULONG, Release)() { ULONG l = InternalRelease(); if (l == 0) delete this; else if (l == 1) _pAtlModule->Unlock(); return l; } ... };
一旦建立,该对象能够在server中一直存在下去,例子:
static CComObjectCached<CPenguinCO>* g_pPenguinCO = 0; BOOL WINAPI DllMain(HINSTANCE, DWORD dwReason, void*) { switch( dwReason ) { case DLL_PROCESS_ATTACH: g_pPenguinCO = new CComObjectCached<CPenguinCO>(); // 1st ref. doesn't keep server alive if( g_pPenguinCO ) g_pPenguinCO->AddRef(); break; case DLL_PROCESS_DETACH: if( g_pPenguinCO ) g_pPenguinCO->Release(); break; } return TRUE; } STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void** ppv) { // Subsequent references do keep server alive if( clsid == CLSID_Penguin && g_pPenguinCO ) return g_pPenguinCO->QueryInterface(riid, ppv); return CLASS_E_CLASSNOTAVAILABLE; }
CComObjectNoLock
template <class Base> class CComObjectNoLock : public Base { public: ... STDMETHOD_(ULONG, AddRef)() { return InternalAddRef(); } STDMETHOD_(ULONG, Release)() { ULONG l = InternalRelease(); if (l == 0) delete this; return l; } ... };
有时,server的生命并不受到object的消亡的影响,例如在out-process中,例子:
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { CoInitialize(0); CComObjectNoLock<CPenguinCO>* pPenguinCO = new CComObjectNoLock<CPenguinCO>(); if( !pPenguinCO ) return E_OUTOFMEMORY; pPenguinCO->AddRef(); DWORD dwReg; HRESULT hr; // Reference(s) cached by ole32.dll won't keep server // from shutting down hr = CoRegisterClassObject(CLSID_Penguin, pPenguinCO, ..., &dwReg); if( SUCCEEDED(hr) ) { MSG msg; while( GetMessage(&msg, 0, 0, 0) ) DispatchMessage(&msg); CoRevokeClassObject(dwReg); pPenguinCO->Release(); } CoUninitialize(); return hr; }
CComObjectGlobal
template <class Base> class CComObjectGlobal : public Base { public: ... STDMETHOD_(ULONG, AddRef )() { return _pAtlModule->Lock(); } STDMETHOD_(ULONG, Release)() { return _pAtlModule->Unlock(); } ... };
这类对象和server的寿命相同,例如:
// No references yet, so server not forced to stay alive static CComObjectGlobal<CPenguinCO> g_penguinCO; STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void** ppv) { // All references keep the server alive if( clsid == CLSID_Penguin ) return g_penguinCO.QueryInterface(riid, ppv); return CLASS_E_CLASSNOTAVAILABLE; }
CComObjectStack
不要在这个对象上调用IUnknown的函数,例子:
void DoABadThing(IBird** ppbird) { CComObjectStack<CPenguin> penguin; penguin.Fly(); // Using IBird method is OK penguin.StraightenTie(); // Using ISnappyDresser method // also OK // This will trigger an assert at runtime penguin.QueryInterface(IID_IBird, (void**)ppbird); }
CComObjectStackEx
可以在对象的栈有效范围内调用IUnknown函数,例子:
void PlayWithBird() { CComObjectStackEx<CPenguin> penguin; IBird* pBird = NULL; penguin.QueryInterface(IID_IBird, (void**)&pBird); // OK -> no assert DoBirdTrickes(pBird); } void DoBirdTricks(IBird* pBird) { pBird->Fly(); // IBird methods OK ISnappyDresser* pPenguin = NULL; pBird->QueryInterface(IID_ISnappyDresser, (void**)&pPenguin); // OK pPenguin->StraightenTie(); // ISnappyDresser methods OK pPenguin->Release(); // OK -> no assert }
总结:
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 |