ATL接口映射宏详解[6]

来源:互联网 发布:centos 7 卸载软件 编辑:程序博客网 时间:2024/05/17 08:42
五.COM_INTERFACE_ENTRY_AGGREGATE(iid, punk)

  这一节中将介绍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_ALL, IID_IUnknown, (void**)&m_pUnkAgg);
    return hRes;
}

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步). 若外部对象发现要查询的是外部组件的接口时,那就很简单了,直接查询就行了。这样就防止了外部组件与聚集组件查询操作的不一致性。唉,真个过程真麻烦,不过还好,与上一节的宏很类似。相关的源码可参看上一节。
原创粉丝点击