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

 


 

原创粉丝点击