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毫秒