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

你可能感兴趣的:(cache,null,delete,Class,each,interface)