这两天学习com,正在看《com技术内幕》这本书,在看到com聚合时有一个问题不懂啊,望各位前辈不吝赐教。先贴一哈源代码,然后提问:在书的148页有这样一个例子,组件CA支持接口IX和IY,其中IY接口从另外一个组件聚合得到:书上是这么写的:class CA : public IX
{
   virtual HRESULT __stdcall QueryInterface(const IID &iid, void ** ppv)
   {
       if(iid == IID_IUnknown)
       {
          略;
        }
    else if(iid == IID_IX)
        {
          略:
         }
    else if(iid == IID_IY)   //看不懂了
        {
           略
         }
         else ...
    } 其它函数略:
private:
   IUnknow *m_pInnerUnknown;   //此指针用于指向内部组件
};我的问题是:从代码可以看出,组件CA只实现了接口IX,那就是说使用此组件的客户只知道此组件支持接口IX,客户根本就不知道组件支持聚合得来的接口IY,那客户就不会作查询接口IY的操作了,那这个聚合在这里似乎就没有任何作用,不知是不是组件在发布的时候要告诉客户它支持什么样的接口而不是用头文件的形式发布的?不好意思,我目前只看到了这里,有些不懂了,上来问一下各位高人,指点指点:)

解决方案 »

  1.   

    所谓聚合是指一个类中包括了另一个类的指针,而外部类不需要实现内部类的方法,外部类可以直接向用户返回内部类的指针.在COM中,外部类返回内部类的时候,不能让用户知道他是在使用两个接口.
    那个:
    class CA:public IX
    {
    private: 
      IUnknown *m_pInnerUnknown;  //此指针用于指向内部组件 
    是内部类的指针,它代表被聚合的组件.指的是内部组件的非代理接口
      IY*       m_pIY         //此接口是内部类的代理指针,
    }class CB:public IY,public INondelegateIUnknown
    {
    public:
       CB(IUnknown* pOuterIUnknown):m_pOuterIUnknown(pOuterIUnknown);
    private:
       IUnknown*  m_pOuterIUnknown;//外部未知接口
        HRESULT    QueryInterface(const IID& refiid,void** ppv)//外部未知接口的查询方法
       HRESULT     NondelegateIUnknown(const IID& refiid,void** ppv);//内部接口自身的查询方法,它实现了INondelegateIUnknown中的虚函数
    }
    当CB构造函数无参数传递时,m_pOuterIUnknown即为自已的地址,此时CB不被聚合,当CB构造函数有参数传递时,m_pOuterIUnknown即为外部类的接口指针。CoCreateInstance第二个参数指定了外部组件接口指针,因此当创建一个外部组件时,IClassFactory将会返回一个CA指针,然后CA还需要调用CoCreateInstance将第二参数指定为内部组件的指针,通过这种方式来创建内部组件。
      

  2.   

    class CA:public IX 

    private: 
      IUnknown *m_pInnerUnknown;  //用于管理内部类的引用计数
      IY*      m_pIY        //用于调用内部类的方法, 

      

  3.   

    楼上的,你说的我们都知道,我们的问题是:客户怎么知道可以从CA得到IY接口?
    只能是通过简单的QueryInterface来查询吗?
      

  4.   

    楼上的,你说的我们都知道。
    我们的问题是,客户怎么知道能从CA中得到IY接口?
    只能通过简单的QueryInterface查询??
      

  5.   

    楼上的,你说的我们都知道。
    我们的问题是,客户怎么知道能从CA中得到IY接口?
    只能通过简单的QueryInterface查询??
      

  6.   

    我的感觉是有点儿类似c++里使用其他自定义类b作为类a公有成员变量的意思;而这个自定义类a和b的父类是Iunknown或者Idispatch。
      

  7.   

    组件CA只实现了IX,但聚合了IY,但组件CA是会对外公布IY的。
    例如在ATL聚合时,IDL文件里,需要对外公布IY。
      

  8.   

    楼上的,你说的我们都知道。 
    我们的问题是,客户怎么知道能从CA中得到IY接口? 
    只能通过简单的QueryInterface查询??
    ____________________________________________
    是的,你注意下,这段代码:
    1.class CA:public IX 

    private: 
      IUnknown *m_pInnerUnknown;  //此指针用于指向内部组件 
    是内部类的指针,它代表被聚合的组件.指的是内部组件的非代理接口 
      IY*      m_pIY        //此接口是内部类的代理指针, 
    } 2.然后CA的QueryInterface是这样的:
    virtual HRESULT __stdcall QueryInterface(const IID &iid, void ** ppv) 

           if(iid == IID_IY)  //看不懂了 
            { 
                *ppv=m_pIY;  //这里直接返回成员变量m_pIY
           } 
           static_cast<IUnknown*>(this)->AddRef();
           return S_OK;
            
    } 3.在创建聚合组件时,CA在自身初始化后,调用Init方法,以下是Init方法:
    IUnknown* pIUnknonw=this;
    HRESULT hr;
    hr=CoCreateInstance(CLSID_CB,pIUnknonw,CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&m_pIUnknownInner);
    if(FAILED(hr))
    {
    return E_FAIL;
    }
    hr=m_pIUnknownInner->QueryInterface(IID_IY,(void**)&m_pIY);//在这里CB调用了NondelegateQueryInterface,因为m_pIUnknownInner指向了INondelegateUnknown指针。因为INondelegateUnknown与IUnknown的结构相同,所以INondelegateUnknown可以转化成IUnknown
    if(SUCCEEDED(hr))
    {
    m_pIUnknownInner->Release();
    return S_OK;
    }
    m_pIUnknownInner->Release();
    return E_FAIL;
    可以看出Init方法用于创建被聚合组件CB,因为CoCreateInstance第二个参数为CA组件的实例,那么pIUnknonw将作为CB的外部组件。4.而CoCreateInstance(CLSID_CB,pIUnknonw,CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&m_pIUnknownInner)又会调用类厂的CreateInstance方法:
    if(pUnkOuter!=NULL && riid!=IID_IUnknown)
    {
    return CLASS_E_NOAGGREGATION;
    }
    CB* pCB=new CB(pUnkOuter);
    if(pCB==NULL)
    {
    return E_OUTOFMEMORY;
    }
    hr=pCB->NonDelegateQueryInterface(riid,ppvObject);
    pCB->NonDelegateRelease();
    就在这里,CB的INonedelegateUnknown指针返回给了CA的Init中的m_pIUnknownInner。就这样从4->1的逆序方式,CB在创建自身实例之后 ,将IY指针提供出来,然后传给CA组件用于供客户查询。