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 |
本文详细解析了COM对象在不同激活场景下的管理与激活机制,包括单例模式、堆栈对象、全局对象等,并通过实例展示了如何正确使用这些技术来确保对象的生命周期与服务器的稳定运行。

586

被折叠的 条评论
为什么被折叠?



