设为首页 加入收藏

TOP

ATL接口映射宏详解(四)
2012-11-04 15:22:38 来源: 作者: 【 】 浏览:1238
Tags:ATL 接口 映射 详解
  四.COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(iid, x, punk) 参ATL例程COMMAP

  这个宏与上一节所讲的COM_INTERFACE_ENTRY_TEAR_OFF宏最主要的不同就在于,当查询分割对象中其他接口时,不会再新建新的对象。下面还是先看看它的典型用法:

class CTearOff2:
public IDispatchImpl,
public CComTearOffObjectBase
{
public:
CTearOff2(){}
~CTearOff2(){}
BEGIN_COM_MAP(CTearOff2)
COM_INTERFACE_ENTRY(ITearOff2)
END_COM_MAP()
HRESULT STDMETHODCALLTYPE get_Name(BSTR* pbstrName)
{
*pbstrName = ::SysAllocString(L"ITearOff2");
return S_OK;
}
};

class COuter : public ....
{
public:
BEGIN_COM_MAP(COuter)
COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(IID_ITearOff2,
CTearOff2, m_pUnkTearOff2.p)
......
END_COM_MAP()
CComPtr m_pUnkTearOff2;
.....
};

  CTearOff2实现了分割接口ITearOff2,它的类定义与上一节所看见的CTearOff1一模一样可见不管是哪种分割接口,实现都是一样的,不同的地方在于COuter。在COuter中增加了一个成员变量m_pUnkTearOff2作为宏的一个参数。

  我们继续用老办法跟踪它的内部执行过程,假设pOuter是已经获得的组件COuter有接口IOuter指针。

  执行pOuter->QueryInterface(IID_ITearOff2, (void **)&pTear1);

  函数堆栈一:

9.CTearOff2::_InternalQueryInterface(...)
8.ATL::CComCachedTearOffObject::QueryInterface(...)(第二次调用)
9.ATL::CComCachedTearOffObject::QueryInterface(...)
8.ATL::CComCreator>::CreateInstance()
7.ATL::CComObjectRootBase::_Cache(...)
6.COuter::_Cache(...)
5.ATL::AtlInternalQueryInterface(...)
4.ATL::CComObjectRootBase::InternalQueryInterface(...)
3,COuter::_InternalQueryInterface(...)
2.ATL::CComObject::QueryInterface(...)
1.CTestDlg::OnButton1() line 187 + 22 bytes

  解释:

  1:pOuter->QueryInterface(IID_ITearOff2, (void **)&pTear1);

  2-5:这段代码见到很多次了,不用再讲了,现在程序执行到

HRESULT hRes = pEntries->pFunc(pThis,
iid, ppvObject, pEntries->dw);

  看来我们得看看这个宏的定义才能知道pFunc是执行的什么功能了。

#define COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(iid, x, punk)\
{&iid,\
(DWORD)&_CComCacheData<\ CComCreator< CComCachedTearOffObject< x> >,\
(DWORD)offsetof(_ComMapClass, punk)\
>::data,\
_Cache},

  与我们上一节见的宏的定义不太一样,还是先跟踪下去再说。

  6:原来在BEGIN_COM_MAP中也定义了_Cache函数:

static HRESULT WINAPI _Cache(void* pv,REFIID iid,void** ppvObject,DWORD dw
)\
{\
......
HRESULT hRes = CComObjectRootBase::_Cache(pv, iid, ppvObject, dw);\
......
}\

  7:看看CComObjectRootBase::_Cache的源码:

static HRESULT WINAPI _Cache(void* pv,REFIID iid,void** ppvObject,DWORD dw)
{
HRESULT hRes = E_NOINTERFACE;
_ATL_CACHEDATA* pcd = (_ATL_CACHEDATA*)dw;
IUnknown** pp = (IUnknown**)((DWORD)pv + pcd->dwOffsetVar);
if (*pp == NULL)
hRes = pcd->pFunc(pv, IID_IUnknown, (void**)pp);
if (*pp != NULL)
hRes = (*pp)->QueryInterface(iid, ppvObject);
return hRes;
}

  现在问题的关键是dw了,dw是从pEntries->dw传过来的。我们得看一下宏定义中的:

(DWORD)&_CComCacheData<\ CComCreator< CComCachedTearOffObject< x> >,\
(DWORD)offsetof(_ComMapClass, punk)\
>::data,\

  是什么意思。

template
_ATL_CACHEDATA _CComCacheData::data ={dwVar, Creator::CreateInstance};

  CComCreator我们在前面已经见过它的定义了,它只有一个成员函数CreateInstance.

template
class CComCachedTearOffObject :
public IUnknown,
public CComObjectRootEx
{
public:
typedef contained _BaseClass;
CComCachedTearOffObject(void* pv) :
m_contained(((contained::_OwnerClass*)pv)->GetControllingUnknown())
{
m_contained.m_pOwner =
reinterpret_cast*>(pv);
}
CComContainedObject m_contained;
};

  CComCachedTearOffObject是这个宏与上一节所讲宏不同的关键所在,因为它包含了一个CComContainedObject的对象。这个对象的作用在查询的时候再讲。

  我们再来看看offsetof的定义:

#define offsetof(s,m) (size_t)&(((s *)0)->m)

  对(DWORD)offsetof(_ComMapClass, punk)来说,就是punk在_ComMapClass类中的偏移值。
现在来看看_ATL_CACHEDDATA是什么东西。

struct _ATL_CACHEDATA
{
DWORD dwOffsetVar;
_ATL_CREATORFUNC* pFunc;
};
typedef HRESULT (WINAPI _ATL_CREATORFUNC)(void* pv, REFIID riid,
LPVOID* ppv);

  要注意的是从_Cached()函数传进来的参数dw就是_ATL_CACHEDATA结构的变量,所以可知道(DWORD)pv + pcd->dwOffsetVar)得到的就是在类COuter中定义的m_pUnkTearOff2的偏移中,所以IUnknown** pp就是指向m_pUnkTearOff2的一个指向指针的指针。而pdc->pFunc()则会调用CComCreator>::CreateInstance

  8:下面将调用CComCreator::CreateInstance,将创建一个CComCachedTearOffObject<> 的对象实例。其构造函数定义如下:

CComCachedTearOffObject(void* pv) :
m_contained(((contained::_OwnerClass*)pv)->GetControllingUnknown())
{
ATLASSERT(m_contained.m_pOwner == NULL);
m_contained.m_pOwner =
reinterpret_cast*>(pv);
}

  这里contained就是CTearOff2,contained::_OwnerClass就是COuter,可见m_contained 保存了外部对象的指针。

  9:创建完对象后,将查询接口ITearOff2

STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
//如果是IUnknown,...,返回IUnknwon接口指针
else
hRes = m_contained._InternalQueryInterface(iid, ppvObject);
.....
}
}

  注意,这里把查询工作交给了m_contained,也就是一个CComContainedObject对象。不过现在查询的是IUnknown指针,别忘了,我们在COuter中还定义了一个IUnknown指针呢,现在查询的就是它!!

  8:经过一系列退栈,退到_Cache()中,现在还要继续查询ITearOff2接口。是根据我们刚刚查询到的IUnknown指针查询ITearOff2。所以再一次进入ATL::CComCachedTearOffObject::QueryInterface(...),不过这回将调用的是m_contained._InternalQueryInterface(...)了。

  9:因为CComContainedObject m_contained的基类是CTearOff2,所以将调用 CTearOff2::_InternalQueryInterface(...) 剩下的操作就没什么特别之处了,仅仅一般的查询操作。

  执行pTear1->QueryInterface(ITearOff2, (void **)&pTear2);

  函数堆栈二:

  12.ATL::AtlInternalQueryInterface(...)

  11.ATL::CComObjectRootBase::InternalQueryInterface(...)

  10.CTearOff2::_InternalQueryInterface(...)

  9.ATL::CComCachedTearOffObject::QueryInterface(...)

  8.ATL::CComObjectRootBase::_Cache(...)

  7.COuter::_Cache(...)

  6.ATL::AtlInternalQueryInterface(...)

  5.ATL::CComObjectRootBase::InternalQueryInterface(...)

  4.COuter::_InternalQueryInterface(...)

  3.ATL::CComObject::QueryInterface(...)

  2.ATL::CComObjectRootBase::OuterQueryInterface(...)

  1.ATL::CComContainedObject::QueryInterface(...)

  解释:

  1:第一步就可能使我们迷惑了,为什么执行的是CComContainedObject::QueryInterface在上一节中,执行的是ATL::CComTearOffObject::QueryInterface(...) ,所以我们也自然而然的猜想,这里应该执行的是CComCachedTearOffObject的函数。但是来看看CComCachedTearOffObject的定义:

template
class CComCachedTearOffObject :
public IUnknown,
public CComObjectRootEx
{ ... };

  原来CComCachedTearOffObject没有从contained类(在这里就是CTearOff2)中继承,而 CComTearOffObject却是从CTearOff1继承的!所以我们刚才得到的pTear1就不可能是 CComCachedTearOffObject的对象。而实际上,CComContainedObject是从CTearOff2继承的,在上面的函数堆栈中第9步查询ITearOff2接口时,把工作交给了m_contained, 这是个CComContainedObject对象,所以实际上最后查询得到的ITearOff2指向的是CComContainedObject对象。所以现在执行的会是

CComContainedObject::QueryInterface(...)!!!
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
HRESULT hr = OuterQueryInterface(iid, ppvObject);
if (FAILED(hr) && _GetRawUnknown() != m_pOuterUnknown)
hr = _InternalQueryInterface(iid, ppvObject);
return hr;
}
// m_pOuterUnknown

  2:HRESULT OuterQueryInterface(REFIID iid, void ** ppvObject)
{
return m_pOuterUnknown->QueryInterface(iid, ppvObject);
}

  把查询工作交给外部对象完成,也就是COuter。第一、二步的功能与上一节中所讲的一样,都是交给外部对象去处理,不同之处在下面3-8:COuter中的查询过程与上例中无异,我们可以直接跳到第8步中

static HRESULT WINAPI _Cache(void* pv, REFIID iid, void** ppvObject,DWORD dw)
{
....
if (*pp == NULL)
hRes = pcd->pFunc(pv, IID_IUnknown, (void**)pp);
if (*pp != NULL)
hRes = (*pp)->QueryInterface(iid, ppvObject);
return hRes;
}

  还记得我们在COuter中定义了一个IUnknown指针m_pUnkTearOff2吧,我们在第一次查询ITearOff2接口时,创建了CTearOff2对象,并查询一个IUnknown指针给了它.现在它就发挥作用了,在_Cache中将判断如果m_pUnkTearOff2不等于空,则表明CTearOff2已经创建就不会再创建它了,而是直接用它去查询接口。

  9:所以现在将调用CComCachedTearOffObject::QueryInterface(...),在上一个函数堆栈中的第9步中我们已经看到了这个QueryInterface(...)的代码,它把查询工作交给m_contained._InternalQueryInterface(.),其实因为CComContainedObject中没有定义BEGIN_COM_MAP宏,所以也没有定义_InternalQueryInterface(),所以实际上调用的是它包含的类的函数,即CTearOff2::_InternalQueryInterface(...) 。

  10-12:以下的工作就很简单了,不再赘述。

首页 上一页 1 2 3 4 5 6 7 下一页 尾页 4/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇基于VC实现Java和Matlab的通信 下一篇用ATL实现VC插件

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: