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