• ATL COM类之激活


    参考:

    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;
    }
    
  • 相关阅读:
    第十八章 Nginx Rewrite重写
    第二十章 HTTPS实现LNMP全站访问
    第十九章 HTTPS协议介绍
    第二十一章 Nginx常用HTTPS配置
    第二十三章 Nginx常见问题思考
    ABC232
    1606E Arena
    st+dfs序求lca
    Servlet——HTTP状态 405 方法不允许
    借助SimpleDateFormat来谈谈java里的多线程不安全
  • 原文地址:https://www.cnblogs.com/Clingingboy/p/2677148.html
Copyright © 2020-2023  润新知