ATL Internals 2ed复习.chapter 4.IUnknown
来源:互联网 发布:如何注销淘宝网账号 编辑:程序博客网 时间:2024/06/06 08:45
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,如:
STDMETHODIMPCPenguinCO::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.cppSTDMETHODIMP 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 availablepenguin.fly// Since ISnappyDresser is not the default, its operations// aren't availablepenguin.straightenTie // runtime error
CComObject Et Al
由于CComObjectRootEx并未定义IUnknown的3个方法,所以下面的代码是会导致编译错误的:
STDMETHODIMPCPenguinCO::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) ;};
例子:
STDMETHODIMPCPenguinCO::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) {...} ... };
用于选择性判断是否内聚,例子:
STDMETHODIMPCPenguinCO::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 alivestatic 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
- ATL Internals 2ed复习.chapter 4.IUnknown
- ATL Internals 2ed复习.chapter 2
- ATL Internals 2ed复习.chapter 4.Thread
- ATL Internals 2ed复习.chapter 4.Creators
- ATL Internals 2ed复习.chapter 4.Debugging
- ATL Internals 2ed复习.chapter 3.ATL Memory Managers
- ATL Internals 2ed复习.chapter 7.ATL Persistence implements
- ATL Internals 2ed复习.chapter 3.CComVariant
- ATL Internals 2ed复习.chapter 3.SAFEARRAY
- ATL Internals 2ed复习.chapter 3.CComSafeArray
- ATL Internals 2ed复习.chapter 3.CComPtr CComQIPtr
- ATL Internals 2ed复习.chapter 3.CComGITPtr
- ATL Internals 2ed复习.chapter 3.CAutoPtr
- ATL Internals 2ed复习.chapter 5.COM Server review
- ATL Internals 2ed复习.chapter 5.Object Map
- ATL Internals 2ed复习.chapter 5.CAtlModule
- ATL Internals 2ed复习.chapter 6.Table driven QueryInterface
- ATL Internals 2ed复习.chapter 6.interface map tricks
- hdu 4296 Buildings
- Linux 关中断 与 开中断
- 类与对象
- android SurfaceView初次使用错误解决办法
- CKEditor 上传文件后返回的内容
- ATL Internals 2ed复习.chapter 4.IUnknown
- AngryMailer - Free Mass Mail Sender - 愤怒的邮递员 - 免费邮件群发器
- Regionals 2011, Asia - Phuket
- C语言读取配置文件的另类写法
- java程序员的迷茫?
- 程序员,你的一千万在哪里?
- Go语言编程--读后感
- 大学毕业后拉开差距的原因
- 俄罗斯方块Windows版本的实现!