COM原理之包容与聚合

1.包容和聚合的概念

包容和聚合实际上是使一个组件使用另外一个组件的一种技术。

包容的情况下,客户只能看到外部组件的接口,而外部组件接口的实现是通过转发给内部组件实现的。

聚合的情况下,直接将内部组件的借口暴漏给客户,虽然客户不知道内部组件的存在。

 

2.包容的实现

包容的实现比较简单,例如外部组件CA(接口IX,IY,其中IY转发给内部组件),内部组件CB(接口IY)。

 

组件CA中添加一成员变量,用于保存内部组件CB的接口指针:
private:
    IY* m_pIY;

CA中Fy函数的实现
Virtual void __stdcall CA::Fy()

{

              m_pIY->Fy();

 

}

 

CA析构函数中调用m_pIY->Release();将CB从内存中释放。


CA新增一成员函数 HRESULT Init(),用于获取内部组件对象的接口指针。

HRESULT CA::Init()
{
    HRESULT hr = ::CoCreateInstance(CLSID_CB, 
                                    NULL,
                                    CLSCTX_INPROC_SERVER,
                                    IID_IY,
      (void**)&m_pIY);   
    if(FAILED(hr))
    {
        return E_FAIL;
    }
     return S_OK;
}

CA的类厂CFactory::CreateInstance函数中加入对Init函数的调用(PS:一个类厂只能创建一种COM对象,CreateInstance函数只接受接口IID,不接受CLSID):

 

HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,  const IID& iid,  void** ppv)
{
    CA* pA = new CA();
    if(pA == NULL)
   {
        return E_OUTOFMEMORY;
    }
    HRESULT hr = pA->Init();
    if(FAILED(hr))
    {
        pA->Release();
        return hr;
    }
    hr = pA->QueryInterface(iid,ppv);
    pA->Release();
    return hr;
}

以上实现了组件CA对组件CB的包容。CA自己没有实现Fy方法,而是通过转发给CB去实现,实现了组件的复用。

 

 

2.聚合的实现

 

因为客户应完全独立于组件的实现,也就是客户不应该知道所使用的组件使用了聚合,也就是永远看不到内部组件的IUnkown。由于两个不同的接口当且仅当返回相同的IUnkown指针时,才被认为是同一个组件实现的。所以外部组件要隐藏内部组件提供给客户,也就是只提供外部组件的IUnkown。

为了实现以上功能,支持被聚合CB接口要实现一组和IUnknown接口一致的非代理Unknown接口,这一组接口完成实际的QueryInterface, AddRef 和 Release的工作。而真正的IUnknow接口的工作是判断该对象是否是被聚合使用(通过一个指向IUnknown的指针成员m_pUnknownOuter),如果该对象被聚合使用(m_pUnknownOuter不等于Null),则将调用转向m_pUnknownOuter所指的外部对象CA,在CA中调用CB的非代理Unknown接口。

具体实现如下:

CA的类厂实现如下:

 

HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,  const IID& iid,  void** ppv)
{
    if(pUnknownOuter != NULL)
   return CLASS_E_NOAGGREGATION;
    CA* pA = new CA();
    if(pA == NULL)
   {
        return E_OUTOFMEMORY;
    }
    HRESULT hr = pA->Init();
    if(FAILED(hr))
    {
        pA->Release();
        return hr;
    }
    hr = pA->QueryInterface(iid,ppv);
    pA->Release();
    return hr;
}

 

CA在初始化时,聚合使用了CB。如下:

HRESULT CA::Init() 

    IUnknown *pUnknownOuter = (IUnknown *)this;
    HRESULT result = ::CoCreateInstance(CLSID_CB, pUnknownOuter, //传送给内部组件
                        CLSCTX_INPROC_SERVER, 
                        IID_IUnknown,

(void **)& m_pUnknownInner) ;//得到的是非代理IUnkown 接口

      if (FAILED(result))

              return E_FAIL; 
     
    result = m_pUnknownInner->QueryInterface(IID_IY, (void **)&m_pCAInterface);//外部组件的引用计数+1

 
    if (FAILED(result)) 
    { 
        m_pUnknownInner->Release(); 
        return E_FAIL; 
    }

    pUnknownOuter->Release(); //外部组件的引用计数-1
    return S_OK; 
}

 

CA的QueryInterface实现如下:

 

HRESULT CA::QueryInterface(const IID& iid, void **ppv) 

    if ( iid == IID_IUnknown ) 
    { 
        *ppv = (IUnknown *) this ;

   ((IUnknown *)(*ppv))->AddRef() ;

 }

    else if ( iid == IID_IX ) 
    { 
        *ppv = (IX*) this ; 
        ((IX*)(*ppv))->AddRef() ; 
    }

    else if ( iid == IID_IY) 
    { 
        return m_pUnknownInner->QueryInterface(iid, ppv) ;//调用非代理IUnkown接口的 QueryInterface
    }

    else 
    { 
        *ppv = NULL; 
        return E_NOINTERFACE ; 
    } 
    return S_OK; 
}

 

 

 

CB的类厂实现为:

 

HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,  const IID& iid,  void** ppv)
{
    if(pUnknownOuter!=NULL && iid!=IID_IUNKOWN)//当聚合情况下,只能请求IUNkown接口,因为外部组件只有这一次机会获取内部组件的非代理未知接口。
     return CLASS_E_NOAGGREGATION;
    CB* pB = new CB(pUnknownOuter);
    if(pA == NULL)
   {
        return E_OUTOFMEMORY;
    }
    HRESULT   hr = pB->   NondelegationQueryInterface (iid,ppv);
    pB->  NondelegationRelease();
    return hr;
}

 

 

客户组件的非代理IUnknown的实现等同于非聚合时的实现,代理外部接口的实现如下:

HRESULT CB::QueryInterface(const IID& iid, void **ppv) 

    if  ( m_pUnknownOuter != NULL ) //聚合情况
    { 
        return m_pUnknownOuter->QueryInterface(iid, ppv);//转交给外部组件,由外部组件调用自己的非代理IUnknown接口 
    } 
    else //非聚合情况
    { 
        return NondelegationQueryInterface(iid, ppv); 
    } 
}

 

//非代理IUnknown接口实现

class INondelegatingUnknown 

public: 
    virtual HRESULT  __stdcall  NondelegationQueryInterface(const IID& iid, void **ppv) = 0 ; 
    virtual ULONG     __stdcall  NondelegatingAddRef() = 0; 
    virtual ULONG     __stdcall  NondelegationRelease() = 0; 
};

 

 

class CB : public IY, public INondelegatingUnknown 

protected: 
     ULONG           m_Ref;

public: 
     CA(IUnknown *pUnknownOuter); 
     ~CA();

public : 
    // Delegating IUnknown 
    virtual HRESULT      __stdcall  QueryInterface(const IID& iid, void **ppv) ; 
    virtual ULONG      __stdcall  AddRef() ; 
    virtual ULONG      __stdcall  Release() ;

    // Nondelegating IUnknown 
    virtual HRESULT   __stdcall  NondelegationQueryInterface(const IID& iid, void **ppv); 
    virtual ULONG      __stdcall  NondelegatingAddRef(); 
    virtual ULONG      __stdcall  NondelegationRelease();


    private : 
        IUnknown  *m_pUnknownOuter;  // pointer to outer IUnknown 
};

 

 

 

HRESULT CB::NondelegationQueryInterface(const IID& iid, void **ppv) 

    if ( iid == IID_IUnknown ) 
    { 
       *ppv = (INondelegatingUnknown *) this ; 

        //当请求IUnknown接口时,强制转换成非代理接口。

        //(INondelegatingUnknown *) this 表达式的结果就仅仅是INondelegatingUnknown虚表的指针。而*ppv需要的正好仅仅是这个虚表的指针。所以在在CA中调用m_pUnknownInner-                     >QueryInterface,程序去找虚表中的第一项。所以实际被调用的就是CA::NondelegationQueryInterface。
         ((IUnknown *)(*ppv))->AddRef() ; 

         //调用IUnkonwn:: AddRef,所以聚合时外部组件的引用计数+1;
    } else if ( iid == IID_IY ) 
    { 
        *ppv = (IY*) this ; 
        ((IY*)(*ppv))->AddRef() ; 
    } 
    else 
    { 
        *ppv = NULL; 
        return E_NOINTERFACE ; 
    } 
    return S_OK; 
}

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值