代码改变世界

ATL COM类之激活

2012-09-09 00:06  Clingingboy  阅读(1763)  评论(0编辑  收藏  举报

 

参考:

ATL 核心COM继承类之CComObjectRootEx及CComObjectLock

背景

继承自CComObjectRootEx的类实际上并未实现IUnknown的三个方法,那么就得手动编写这三个方法

class CPenguin :
    public CComObjectRootEx<CComMultiThreadModel>,
    public IBird,
    public ISnappyDresser {
public:
    CPengin() { ServerLock(); }
    ~CPenguin() { ServerUnlock(); }
    BEGIN_COM_MAP(CPenguin)
        COM_INTERFACE_ENTRY(IBird)
        COM_INTERFACE_ENTRY(ISnappyDresser)
    END_COM_MAP()
    // IBird and ISnappyDresser methods...
    // IUnknown methods for standalone, heap-based objects
    STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
    { return _InternalQueryInterface(riid, ppv); }

    STDMETHODIMP_(ULONG) AddRef()
    { return InternalAddRef(); }

    STDMETHODIMP_(ULONG) Release() {
        ULONG l = InternalRelease();
        if( l == 0 ) delete this;
        return l;
    }
};

激活COM对象

这里所指COM对象可以暂时理解为继承自IUnknown接口的对象(这很重要),激活可以理解为创建对象,那我们的目的就是简化创建对象的步骤(即激活).

1.独立激活

//Base is the user's class that derives from CComObjectRoot and whatever
//interfaces the user wants to support on the object
template <class Base>
class CComObject : 
    public Base
{
public:
    typedef Base _BaseClass;
    CComObject(_In_opt_ void* = NULL) throw()
    {
        _pAtlModule->Lock();
    }
    // Set refcount to -(LONG_MAX/2) to protect destruction and 
    // also catch mismatched Release in debug builds
    virtual ~CComObject() throw()
    {
        m_dwRef = -(LONG_MAX/2);
        FinalRelease();
#ifdef _ATL_DEBUG_INTERFACES
        _AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
#endif
        _pAtlModule->Unlock();
    }
    //If InternalAddRef or InternalRelease is undefined then your class
    //doesn't derive from CComObjectRoot
    STDMETHOD_(ULONG, AddRef)() 
    {
        return InternalAddRef();
    }
    STDMETHOD_(ULONG, Release)()
    {
        ULONG l = InternalRelease();
        if (l == 0)
            delete this;
        return l;
    }
    //if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
    STDMETHOD(QueryInterface)(
        _In_ REFIID iid, 
        _Deref_out_ void** ppvObject) throw()
    {
        return _InternalQueryInterface(iid, ppvObject);
    }
    template <class Q>
    HRESULT STDMETHODCALLTYPE QueryInterface(
        _Deref_out_ Q** pp) throw()
    {
        return QueryInterface(__uuidof(Q), (void**)pp);
    }

    static HRESULT WINAPI CreateInstance(_Deref_out_ CComObject<Base>** pp) throw();
};

那么激活对象的方式可以这样:

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>;
        if( pobj ) {
            pobj->AddRef();
            HRESULT hr = pobj->QueryInterface(riid, ppv);
            pobj->Release();
            return hr;
        }
        return E_OUTOFMEMORY;
}

2.聚合激活

1

template <class Base> //Base must be derived from CComObjectRoot
class CComContainedObject : 
    public Base
{
public:
    typedef Base _BaseClass;
    CComContainedObject(_In_opt_ void* pv) 
    {
        m_pOuterUnknown = (IUnknown*)pv;
    }
#ifdef _ATL_DEBUG_INTERFACES
    virtual ~CComContainedObject()
    {
        _AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
        _AtlDebugInterfacesModule.DeleteNonAddRefThunk(m_pOuterUnknown);
    }
#endif

    STDMETHOD_(ULONG, AddRef)() throw() 
    {
        return OuterAddRef();
    }
    STDMETHOD_(ULONG, Release)() throw() 
    {
        return OuterRelease();
    }
    STDMETHOD(QueryInterface)(
        _In_ REFIID iid, 
        _Deref_out_ void** ppvObject) throw()
    {
        return OuterQueryInterface(iid, ppvObject);
    }
    template <class Q>
    HRESULT STDMETHODCALLTYPE QueryInterface(
        _Deref_out_ Q** pp)
    {
        return QueryInterface(__uuidof(Q), (void**)pp);
    }
    //GetControllingUnknown may be virtual if the Base class has declared
    //DECLARE_GET_CONTROLLING_UNKNOWN()
    IUnknown* GetControllingUnknown() throw()
    {
#ifdef _ATL_DEBUG_INTERFACES
        IUnknown* p;
        _AtlDebugInterfacesModule.AddNonAddRefThunk(m_pOuterUnknown, _T("CComContainedObject"), &p);
        return p;
#else
        return m_pOuterUnknown;
#endif
    }
};

//contained is the user's class that derives from CComObjectRoot and whatever
//interfaces the user wants to support on the object
template <class contained>
class CComAggObject :
    public IUnknown,
    public CComObjectRootEx< typename contained::_ThreadModel::ThreadModelNoCS >
{
public:
    typedef contained _BaseClass;
    CComAggObject(_In_opt_ void* pv) : 
        m_contained(pv)
    {
        _pAtlModule->Lock();
    }
    HRESULT _AtlInitialConstruct()
    {
        HRESULT hr = m_contained._AtlInitialConstruct();
        if (SUCCEEDED(hr))
        {
            hr = CComObjectRootEx< typename contained::_ThreadModel::ThreadModelNoCS >::_AtlInitialConstruct();
        }
        return hr;
    }
    //If you get a message that this call is ambiguous then you need to
    // override it in your class and call each base class' version of this
    HRESULT FinalConstruct()
    {
        CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalConstruct();
        return m_contained.FinalConstruct();
    }
    void FinalRelease()
    {
        CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalRelease();
        m_contained.FinalRelease();
    }
    // Set refcount to -(LONG_MAX/2) to protect destruction and 
    // also catch mismatched Release in debug builds
    virtual ~CComAggObject()
    {
        m_dwRef = -(LONG_MAX/2);
        FinalRelease();
#ifdef _ATL_DEBUG_INTERFACES
        _AtlDebugInterfacesModule.DeleteNonAddRefThunk(this);
#endif
        _pAtlModule->Unlock();
    }

    STDMETHOD_(ULONG, AddRef)() 
    {
        return InternalAddRef();
    }
    STDMETHOD_(ULONG, Release)()
    {
        ULONG l = InternalRelease();
        if (l == 0)
            delete this;
        return l;
    }
    STDMETHOD(QueryInterface)(
        _In_ REFIID iid, 
        _Deref_out_ 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();
#ifdef _ATL_DEBUG_INTERFACES
            _AtlDebugInterfacesModule.AddThunk((IUnknown**)ppvObject, (LPCTSTR)contained::_GetEntries()[-1].dw, iid);
#endif // _ATL_DEBUG_INTERFACES
        }
        else
            hRes = m_contained._InternalQueryInterface(iid, ppvObject);
        return hRes;
    }
    template <class Q>
    HRESULT STDMETHODCALLTYPE QueryInterface(_Deref_out_ Q** pp)
    {
        return QueryInterface(__uuidof(Q), (void**)pp);
    }
    static HRESULT WINAPI CreateInstance(
        _Inout_opt_ LPUNKNOWN pUnkOuter, 
        _Deref_out_ CComAggObject<contained>** pp)
    {
        ATLASSERT(pp != NULL);
        if (pp == NULL)
            return E_POINTER;
        *pp = NULL;

        HRESULT hRes = E_OUTOFMEMORY;
        CComAggObject<contained>* p = NULL;
        ATLTRY(p = new CComAggObject<contained>(pUnkOuter))
        if (p != NULL)
        {
            p->SetVoid(NULL);
            p->InternalFinalConstructAddRef();
            hRes = p->_AtlInitialConstruct();
            if (SUCCEEDED(hRes))
                hRes = p->FinalConstruct();
            if (SUCCEEDED(hRes))
                hRes = p->_AtlFinalConstruct();
            p->InternalFinalConstructRelease();
            if (hRes != S_OK)
            {
                delete p;
                p = NULL;
            }
        }
        *pp = p;
        return hRes;
    }

    CComContainedObject<contained> m_contained;
};

支持聚合的激活创建

STDMETHODIMP
    CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid,
    void** ppv) {
        *ppv = 0;
        if( pUnkOuter ) {
            CComAggObject<CPenguin>* pobj =
                new CComAggObject<CPenguin>(pUnkOuter);
            ...
        }
        else {
            CComObject<CPenguin>* pobj = new CComObject<CPenguin>;
            ...
        }
}

 

CComPolyObject

这个类封装了CComAggObject和CComObject的逻辑,介绍了一个类的虚表

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)

COM锁的知识背景:当一个对象在DLL中被创建出来时候为不影响这个对象的正常使用,不可卸载相关的DLL,通过锁的机制来防止这种问题(至于锁怎么实现,可以不管,一般都是全局引用计数)。如CComObject对象的构造和析构函数调用了Lock和Unlock方法,如果想缓存这个对象的话,即保存为全局对象

//Base is the user's class that derives from CComObjectRoot and whatever
//interfaces the user wants to support on the object
// CComObjectCached is used primarily for class factories in DLL's
// but it is useful anytime you want to cache an object
template <class Base>
class CComObjectCached : 
    public Base
{
public:
    typedef Base _BaseClass;
    CComObjectCached(_In_opt_ void* = NULL)
    {
    }
    // Set refcount to -(LONG_MAX/2) to protect destruction and 
    // also catch mismatched Release in debug builds
    virtual ~CComObjectCached()
    {
        m_dwRef = -(LONG_MAX/2);
        FinalRelease();
#ifdef _ATL_DEBUG_INTERFACES
        _AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
#endif
    }
    //If InternalAddRef or InternalRelease is undefined then your class
    //doesn't derive from CComObjectRoot
    STDMETHOD_(ULONG, AddRef)() throw()
    {
        ULONG l = InternalAddRef();
        if (l == 2)
            _pAtlModule->Lock();
        return l;
    }
    STDMETHOD_(ULONG, Release)() throw()
    {
        ULONG l = InternalRelease();
        if (l == 0)
            delete this;
        else if (l == 1)
            _pAtlModule->Unlock();
        return l;
    }
    //if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
    STDMETHOD(QueryInterface)(
        _In_ REFIID iid, 
        _Deref_out_ void** ppvObject) throw()
    {
        return _InternalQueryInterface(iid, ppvObject);
    }
    static HRESULT WINAPI CreateInstance(
        _Deref_out_ CComObjectCached<Base>** pp) throw();
};

那么现在你可以如下使用这个对象,避免重复创建对象

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

上面讲过CComObjectCached是对于DLL相关的对象,如果是进程外的对象可以不用锁,不会影响程序的生命周期

1

//Base is the user's class that derives from CComObjectRoot and whatever
//interfaces the user wants to support on the object
template <class Base>
class CComObjectNoLock :
    public Base
{
public:
    typedef Base _BaseClass;
    CComObjectNoLock(_In_opt_ void* = NULL)
    {
    }
    // Set refcount to -(LONG_MAX/2) to protect destruction and 
    // also catch mismatched Release in debug builds

    virtual ~CComObjectNoLock()
    {
        m_dwRef = -(LONG_MAX/2);
        FinalRelease();
#ifdef _ATL_DEBUG_INTERFACES
        _AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
#endif
    }

    //If InternalAddRef or InternalRelease is undefined then your class
    //doesn't derive from CComObjectRoot
    STDMETHOD_(ULONG, AddRef)() throw() 
    {
        return InternalAddRef();
    }
    STDMETHOD_(ULONG, Release)() throw()
    {
        ULONG l = InternalRelease();
        if (l == 0)
            delete this;
        return l;
    }
    //if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
    STDMETHOD(QueryInterface)(
        _In_ REFIID iid, 
        _Deref_out_ void** ppvObject) throw()
    {
        return _InternalQueryInterface(iid, ppvObject);
    }
};

如下调用:

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

这个对象激活了将于服务器周期一样,即结束后才释放(因为其本身就没释放过)

// It is possible for Base not to derive from CComObjectRoot
// However, you will need to provide _InternalQueryInterface
template <class Base>
class CComObjectGlobal :
    public Base
{
public:
    typedef Base _BaseClass;
    CComObjectGlobal(_In_opt_ void* = NULL)
    {
        m_hResFinalConstruct = S_OK;
        __if_exists(FinalConstruct)
        {
            __if_exists(InternalFinalConstructAddRef)
            {
                InternalFinalConstructAddRef();
            }
            m_hResFinalConstruct = _AtlInitialConstruct();
            if (SUCCEEDED(m_hResFinalConstruct))
                m_hResFinalConstruct = FinalConstruct();
            __if_exists(InternalFinalConstructRelease)
            {
                InternalFinalConstructRelease();
            }
        }
    }
    virtual ~CComObjectGlobal()
    {
        __if_exists(FinalRelease)
        {
            FinalRelease();
        }
#ifdef _ATL_DEBUG_INTERFACES
        _AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
#endif
    }

    STDMETHOD_(ULONG, AddRef)() throw()
    {
        return _pAtlModule->Lock();
    }
    STDMETHOD_(ULONG, Release)() throw()
    {
        return _pAtlModule->Unlock();
    }
    STDMETHOD(QueryInterface)(
        _In_ REFIID iid, 
        _Deref_out_ void** ppvObject) throw()
    {
        return _InternalQueryInterface(iid, ppvObject);
    }
    HRESULT m_hResFinalConstruct;
};

在栈上创建对象

// 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;
}