Programming 版 (精华区)
发信人: lofe ()感激生活(), 信区: Programming
标 题: ATL接口映射宏详解--(5)
发信站: 哈工大紫丁香 (Sun Sep 3 08:16:17 2000), 转信
五.COM_INTERFACE_ENTRY_AGGREGATE(iid, punk) 参ATL例程COMMAP
这一节中将介绍ATL中用于聚集对象的宏。聚集对象的概念请参阅其它参考书。
现在先看一看这个宏的典型用法:
class CAgg :
public IDispatchImpl<IAgg, &IID_IAgg, &LIBID_AGGREGLib>,
public ISupportErrorInfo,
public CComObjectRoot,
public CComCoClass<CAgg,&CLSID_CAgg>
{
.....
};
CAgg是一个聚集类,它的实现与一般的ATL组件没有区别,只是注意在它的类定义中不
要加入DECLARE_NO_AGGREGATABLE.
class COuter :
public CChainBase,
public IDispatchImpl<IOuter, &IID_IOuter, &LIBID_COMMAPLib>,
public CComCoClass<COuter,&CLSID_COuter>
{
HRESULT FinalConstruct();
void FinalRelease();
BEGIN_COM_MAP(COuter)
COM_INTERFACE_ENTRY_AGGREGATE(IID_IAgg, m_pUnkAgg.p)
END_COM_MAP()
DECLARE_GET_CONTROLLING_UNKNOWN()
CComPtr<IUnknown> m_pUnkAgg;
};
COuter包含了聚合组件CAgg,它包含了几个不同之处:
(1)加入了COM_INTERFACE_ENTRY_AGGREGATE(IID_IAgg, m_pUnkAgg.p)宏。
#define COM_INTERFACE_ENTRY_AGGREGATE(iid, punk)\
{&iid,\
(DWORD)offsetof(_ComMapClass, punk),\
_Delegate},
offsetof我们在上一节中已经见过,可以猜到它求的就是punk在类中的位置。也就
是m_pUnkAgg在COuter中的位置。
(2)加入了宏DECLARE_GET_CONTROLLING_UNKNOWN(),其定义为:
#define DECLARE_GET_CONTROLLING_UNKNOWN() public:\
virtual IUnknown* GetControllingUnknown() {return GetUnknown();}
我们也必要继续深究下去,仅从字面意思就可以看出这个函数将返回组件的IUnknown
指针。
(3)在COuter中加入一个成员变量:CComPtr<IUnknown> m_pUnkAgg;
m_pUnkAgg将用于获得被聚集组件的IUnknown指针。
(4)重载了FinalConstruct,FinalRelease
HRESULT COuter::FinalConstruct()
{
IUnknown* pUnkOuter = GetControllingUnknown();
HRESULT hRes = CoCreateInstance(CLSID_CAgg, pUnkOuter,
CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&m_pUnkAgg);
if (hRes != S_OK)
return hRes;
return S_OK;
}
void COuter::FinalRelease()
{
m_pUnkAgg.Release();
.....
}
当创建组件COuter后将会调用FinalConstruct,所以会在这里创建聚集组件。原则上
聚集组件可以仅在需要的时候才创建,但也可以随着包含它的组件一起创建。聚集组件
的创建没什么特别之处,只是要注意它将查询IUnknown指针,并返回给m_pUnkAgg.外部
组件将通过m_pUnkAgg操作聚集组件。另外注意到使用pUnkOuter作为CoCreateInstance
的参数,这将导致创建CComAggObject<COuter>对象,内部包含一个CComContainedObject
的包含对象。向上一节中的CComCachedTearOff<>类似,CComAggObject<COuter>也不是
从COuter派生的,所以真正的组件对象不是CComAggObject<COuter>对象,而是它内部包
含的CComContainedObject<COuter>对象。同样pUnkOuter得到的将是CComAggObject<>的
IUnknown指针,也同样调用它的QueryInterface会转而调用CComContainedObject的
_InternalQueryInterface函数(呵呵,现在可都还是我猜的,看我猜的对不对吧)
运行pOuter->QueryInterface(IID_IAgg, (void **)&pAgg1)
函数堆栈一:
9.ATL::AtlInternalQueryInterface(...)
8.ATL::CComObjectRootBase::InternalQueryInterface(...)
7.CAgg::_InternalQueryInterface(...)
6.ATL::CComAggObject<CAgg>::QueryInterface(...)
5.ATL::CComObjectRootBase::_Delegate(...)
4.ATL::AtlInternalQueryInterface(...)
3.ATL::CComObjectRootBase::InternalQueryInterface(...)
2.COuter::_InternalQueryInterface(...)
1.ATL::CComObject<COuter>::QueryInterface(...)
解释:
1-5:这几步函数调用我们已经见了很多次了,因为在这个宏定义使用了_Delegate,所以
将调用CComObjectRootBase::_Delegate(...).
static HRESULT _Delegate(void* pv,REFIID iid,void** ppvObject,DWORD dw)
{
HRESULT hRes = E_NOINTERFACE;
IUnknown* p = *(IUnknown**)((DWORD)pv + dw);
if (p != NULL)
hRes = p->QueryInterface(iid, ppvObject);
return hRes;
}
第二句话的含义我们在上一节中已经见过了,最后的结果p=COuter::m_pUnkAgg.
6:正如我们刚才所料,现在调用的是CComAggObject<CAgg>::QueryInterface()
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
//如果查询的是IUnknown,则....
else
hRes = m_contained._InternalQueryInterface(iid, ppvObject);
return hRes;
}
也正如我们所料,将交给它的包含对象去做.(这段代码在上一节好象也见过是吧,呵呵)
7-9:同上一节一样,将交给CAgg::_InternalQueryInterface(...),剩下的工作将由CAgg
完成了。最后返回的指针实际上将是CComContainedObject<CAgg>组件的接口指针。
运行pAgg1->QueryInterface(IID_IAgg, (void **)&pAgg2)
函数堆栈二:
9.CAgg::_InternalQueryInterface(...)
8.ATL::CComAggObject<CAgg>::QueryInterface(...)
7.ATL::CComObjectRootBase::_Delegate(...)
6.ATL::AtlInternalQueryInterface(...)
5.ATL::CComObjectRootBase::InternalQueryInterface(...)
4.COuter::_InternalQueryInterface(...)
3.ATL::CComObject<COuter>::QueryInterface(...)
2.ATL::CComObjectRootBase::OuterQueryInterface(...)
1.ATL::CComContainedObject<CAgg>::QueryInterface(...)
解释:
1-9:浏览整个堆栈,与我们上一节所见的堆栈二太相近了,这是因为都是使用了包含对
象。包含对象起了个代理的作用,他先把查询交给外部对象(COuter)去做(第1,2步),
当外部对象发现要查询的是聚集组件的接口时(IAgg),就会再把查询交还给它保留的
聚集组件的指针(m_pUnkAgg,第7步中,注意这不是真正的聚集组件),m_pUnkAgg再把查
询交给包含对象(第8步中),包含对象再把查询交给真正实现接口的类CAgg(第9步).
若外部对象发现要查询的是外部组件的接口时,那就很简单了,直接查询就行了。这
样就防止了外部组件与聚集组件查询操作的不一致性。
唉,真个过程真麻烦,不过还好,与上一节的宏很类似。相关的源码可参看上一节。
-------------未完待续-------------
--
才疏学浅,胡言乱语;不对之处,敬请指正。
路漫漫兮,其修远。
吾将上下而求索。
※ 修改:.haojs 于 Sep 3 08:13:53 修改本文.[FROM: bbs.hit.edu.cn]
--
※ 转寄:.武汉白云黄鹤站 bbs.whnet.edu.cn.[FROM: bbs.hit.edu.cn]
--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: haojs.bbs@bbs.whnet.]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:9.390毫秒