ATL Internals 2ed复习.chapter 4.IUnknown

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

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值