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

源代码下载

    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函数 
******************************* 
*/ 

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java 技术分享

MVC 小案例 -- 信息管理

892
来自专栏爱撒谎的男孩

地址管理之省市区三级联动菜单

8413
来自专栏Android 开发学习

JsBridge 源码分析

1583
来自专栏软件开发

MyBatis学习总结(四)——MyBatis缓存与代码生成

缓存可以提高系统性能,可以加快访问速度,减轻服务器压力,带来更好的用户体验。缓存用空间换时间,好的缓存是缓存命中率高的且数据量小的。缓存是一种非常重要的技术。

1913
来自专栏菩提树下的杨过

[转自JeffreyZhao]在LINQ to SQL中使用Translate方法以及修改查询用SQL

目前LINQ to SQL的资料不多——老赵的意思是,目前能找到的资料都难以摆脱“官方用法”的“阴影”。LINQ to SQL最权威的资料自然是MSDN,但是M...

2065
来自专栏码农分享

4.1、苏宁百万级商品爬取 代码讲解 索引建立

Lucene是一款高性能的、可扩展的信息检索(IR)工具库。信息检索是指文档搜索、文档内信息搜索或者文档相关的元数据搜索等操作。

1333
来自专栏Pythonista

Django之ORM数据库

            django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqli...

1171
来自专栏wannshan(javaer,RPC)

dubbo监控机制之监控中心实现分析

这里的监控中心以dubbo-ops\dubbo-monitor-simple项目说 总的来说是监控中心启动一个sevlet容器,通过web页面向用户多维度的展...

1.1K6
来自专栏影子

SpringBoot中关于Mybatis使用的三个问题

945
来自专栏Java 技术分享

MVC 小案例 -- 信息管理

2895

扫码关注云+社区