首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ATL源码学习4---TearOff接口支持

ATL源码学习4---TearOff接口支持

作者头像
雪影
发布2018-08-02 14:29:31
4580
发布2018-08-02 14:29:31
举报
文章被收录于专栏:流媒体人生流媒体人生

源代码下载

    http://download.csdn.net/source/1687116

1.TearOff接口由来   一般COM组件实现接口支持是通过继承而获取,但是它并不完美。其中一个问题是“vptr-膨胀”(vptr-bloat)。对于一个类继承的每个接口,类的每个实例都有一个vptr。由于某些接口使用的频率很低,所以最好是在使用它们的时候才付出相应的代价。为了实现这里点,Crispin Goswell发明了tear-off接口。 2.ATL对TearOff的内部组件的支持 tear-off接口是按需暴露的接口,但主类并不真正地从它继承。相反而是通过一个辅助类从tear-off接口派生,每当客户请求接口时,再创建这个辅助类的实例。ATL类实现tear-off接口时,使用CComTearOffObjectBase作为它们的基类,而不是CComObjectRootEx,其实CComTearOffObjectBase就是从CComObjectRootEx,派生而来的。

template <class Owner, class ThreadModel = CComObjectThreadModel>  
class CComTearOffObjectBase : public CComObjectRootEx<ThreadModel>  
{  
public:  
 typedef Owner _OwnerClass;  
    CComObject<Owner>* m_pOwner;  
    CComTearOffObjectBase() {m_pOwner = NULL;}  
};  

CComTearOffObjectBase 提供了一个额外的服务,缓存tear-off接口的所有者。每个tear-off接口属于一个所有对象,通过m_pOwner变量,使得tear-off实例可以访问所有者的成员数据或者成员函数。 3. 内部组件的IUnknown接口的实现 tear-off派生类不是CComObject,而是CComTearOffObject,CComTearOffObject知道基类的m_pOwner成员,并在构造函数中初始化。为了遵循COM实体身份规则,每个Tear-off都会把新的接口请求转给拥有着。

template <class Base>  
class CComTearOffObject : public Base  
{  
public:  
    CComTearOffObject(void* pv)  
    {  
        ATLASSUME(m_pOwner == NULL);  
        m_pOwner = reinterpret_cast<Base::_OwnerClass*>(pv);  
        m_pOwner->AddRef();  
    }  
 // Set refcount to -(LONG_MAX/2) to protect destruction and  
 // also catch mismatched Release in debug builds 
 virtual ~CComTearOffObject()  
    {  
        m_dwRef = -(LONG_MAX/2);  
        FinalRelease();  
#ifdef _ATL_DEBUG_INTERFACES 
        _AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());  
#endif 
        m_pOwner->Release();  
    }  
    STDMETHOD_(ULONG, AddRef)() throw() {return InternalAddRef();}  
    STDMETHOD_(ULONG, Release)() throw()  
    {  
 ULONG l = InternalRelease();  
 if (l == 0)  
 delete this;  
 return l;  
    }  
    STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) throw()  
    {  
 return m_pOwner->QueryInterface(iid, ppvObject);  
    }  
};  

拥有缓存的tear-off组件的实现

template <class contained>  
class CComCachedTearOffObject :  
 public IUnknown,  
 public CComObjectRootEx<typename contained::_ThreadModel::ThreadModelNoCS>  
{  
public:  
    typedef contained _BaseClass;  
    CComCachedTearOffObject(void* pv) :  
        m_contained(((contained::_OwnerClass*)pv)->GetControllingUnknown())  
    {  
        ATLASSUME(m_contained.m_pOwner == NULL);  
        m_contained.m_pOwner = reinterpret_cast<contained::_OwnerClass*>(pv);  
    }  
    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 ~CComCachedTearOffObject()  
    {  
        m_dwRef = -(LONG_MAX/2);  
        FinalRelease();  
#ifdef _ATL_DEBUG_INTERFACES 
        _AtlDebugInterfacesModule.DeleteNonAddRefThunk(this);  
#endif 
    }  
    STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}  
    STDMETHOD_(ULONG, Release)()  
    {  
        ULONG l = InternalRelease();  
 if (l == 0)  
            delete this;  
 return l;  
    }  
    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();  
#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;  
    }  
    CComContainedObject<contained> m_contained;  
};  

4.ATL对TearOff的外部组件(拥有者组件)的支持 ATL通过下面两个宏实现对tear-off外部组件的支持

#define COM_INTERFACE_ENTRY_TEAR_OFF(iid, x)/ 
    {&iid,/  
    (DWORD_PTR)&ATL::_CComCreatorData</  
        ATL::CComInternalCreator< ATL::CComTearOffObject< x > >/  
        >::data,/  
    _Creator},  
#define COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(iid, x, punk)/ 
    {&iid,/  
    (DWORD_PTR)&ATL::_CComCacheData</  
        ATL::CComCreator< ATL::CComCachedTearOffObject< x > >,/  
        (DWORD_PTR)offsetof(_ComMapClass, punk)/  
        >::data,/  
    _Cache},  

     通过_Creator或_Cache函数(这两个函数的定义在CComObjectRootBase中)调用CComInternalCreator或CComCreator的CreateInstance函数,创建ATL::CComTearOffObject< x >对象,即tear-off组件对象。     两者的区别是后者能将创建的接口缓存到punk变量中,在组件的生命周期内,下次再使用时tear-off接口不需要再次创建tear-off组件。前者每次使用tear-off接口都需要创建一个tear-off组件实例。 5.tear-off接口实现例子 a.内部组件代码,即tear-off组件代码

class ATL_NO_VTABLE CB :   
 public CComTearOffObjectBase<CA,CComSingleThreadModel>,//必须从CComTearOffObjectBase继承 
 public CComCoClass<CB, &CLSID_B>,  
 public IB  
{  
public:  
    CB(){}  
DECLARE_REGISTRY_RESOURCEID(IDR_B)  
DECLARE_PROTECT_FINAL_CONSTRUCT()  
BEGIN_COM_MAP(CB)  
    COM_INTERFACE_ENTRY(IB)  
END_COM_MAP()  
// IB 
public:  
    STDMETHOD(PrintB)(){  
        cout<<"执行PrintB函数"<<endl;  
 return 0;  
    };  
};  

组件C代码同B,这里就不再贴了

b.外部组件的代码,即tear-off接口拥有者实现代码

class ATL_NO_VTABLE CA :   
 public CComObjectRootEx<CComSingleThreadModel>,  
 public CComCoClass<CA, &CLSID_A>,  
 public IA  
{  
public:  
    CA(){}  
DECLARE_REGISTRY_RESOURCEID(IDR_A)  
DECLARE_PROTECT_FINAL_CONSTRUCT()  
DECLARE_NOT_AGGREGATABLE(CA)  
DECLARE_GET_CONTROLLING_UNKNOWN()  
BEGIN_COM_MAP(CA)  
    COM_INTERFACE_ENTRY(IA)  
    COM_INTERFACE_ENTRY_TEAR_OFF(IID_IB,CB)//不缓存接口 
    COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(IID_IC,CC,m_spunk.p)//缓存tear-off组件接口 
END_COM_MAP()  
// IA 
public:  
    STDMETHOD(PrintA)(){  
        cout<<"执行PrintA函数"<<endl;  
 return 0;  
    };  
    CComPtr<IUnknown> m_spunk;  
};  

c.测试代码

#include "stdafx.h" 
#include <atlbase.h> 
#import "../../tearoff/tearoff.tlb" no_namespace named_guids raw_interfaces_only 
int main(int argc, char* argv[])  
{  
    ::CoInitialize(NULL);  
 //下面接口、函数都是通过组件A获取调用 
    CComPtr<IA> pA;  
 HRESULT hr=pA.CoCreateInstance(CLSID_A);      
    pA->PrintA();  
    CComPtr<IB> pB;  
    hr=pA->QueryInterface(&pB);  
    pB->PrintB();  
    CComPtr<IC> pC;  
    hr=pA->QueryInterface(&pC);  
    pC->PrintC();  
    pC.Release();  
    pB.Release();  
    pA.Release();  
 //下面接口、函数都是通过组件B获取调用 
    printf("/n");  
    pB.CoCreateInstance(CLSID_B);     
    pB->PrintB();  
    pB.Release();  
    ::CoUninitialize();  
 return 0;  
}  
/*  运行结果为 
******************************* 
*  执行PrintA函数 
*  执行PrintB函数 
*  执行PrintC函数 
*  执行PrintB函数 
******************************* 
*/ 
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2009年09月22日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档