COM---组件复用:包容与聚合

包容和 聚合为实现组件复用和定制提供了一种极鲁棒性的机制。使得COM框架下不需要实现继承,客户通组件的实现完全隔离开。
如果希望给组件增加新的接口,可以使用聚合。聚合是包容的一个特例。

以下代码只列出了关键部分,其它部分省略。

包容

外部组件包含指向内部组件接口的指针,此时外部组件只是内部组件的一个客户。外部组件可通过将调用转发给内部组件来重新实现内部组件的某个接口,还可在内部组件代码前后加上一些代码对接口改造。

组件1中的新成员m_pIY保存了指向所包容的组件2中IY接口的指针

外部组件

//
// Component A
//
class CA : public IX,
           public IY //@N
{
public:
    // IUnknown
    virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;
    virtual ULONG   __stdcall AddRef() ;
    virtual ULONG   __stdcall Release() ;

    // Interface IX
    virtual void __stdcall Fx() { cout << "Fx" << endl ;}

    // Interface IY
    virtual void __stdcall Fy() { m_pIY->Fy() ;} //@N

    // Constructor
    CA() ;

    // Destructor
    ~CA() ;

    // Initialization function called by the class factory
    // to create the contained component
    HRESULT __stdcall Init() ;     //@N

private:
    // Reference count
    long m_cRef ;

    // 被包容的组件
    IY* m_pIY ;
} ;


//
// Constructor
//
CA::CA()
: m_cRef(1),
  m_pIY(NULL) //@N  内部组件初始化
{
    ::InterlockedIncrement(&g_cComponents) ;
}

//
// Destructor
//
CA::~CA() 
{ 
    ::InterlockedDecrement(&g_cComponents) ; 
    trace("Destroy self.") ;

    // 释放内部组件 @N
    if (m_pIY != NULL)
    {
        m_pIY->Release() ;   
    }
}


// 创建内部组件
HRESULT __stdcall CA::Init()
{
    trace("Create contained component.") ;
    //外部组件请求了内部组件IY接口的指针,若调用成功,被保存在m_pIY中
    HRESULT hr = ::CoCreateInstance(CLSID_Component2, 
                                    NULL, 
                                    CLSCTX_INPROC_SERVER,
                                    IID_IY,
                                    (void**)&m_pIY) ;
    if (FAILED(hr))
    {
        trace("Could not create contained component.") ;
        return E_FAIL ;
    }
    else
    {
        return S_OK ;
    }
}

外部组件的类厂

//
// IClassFactory implementation
//
HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,
                                           const IID& iid,
                                           void** ppv) 
{
    // Cannot aggregate
    if (pUnknownOuter != NULL)
    {
        return CLASS_E_NOAGGREGATION ;
    }
    // 创建外部组件
    CA* pA = new CA ;
    if (pA == NULL)
    {
        return E_OUTOFMEMORY ;
    }
    // 创建内部组件 @N
    HRESULT hr = pA->Init() ;
    if (FAILED(hr))
    {
        // 如何失败,则删除
        pA->Release() ;
        return hr ;
    }

    // Get the requested interface.
    hr = pA->QueryInterface(iid, ppv) ;
    pA->Release() ;
    return hr ;   
}

聚合

为了实现 聚合,内部组件需要实现两个不同的接口,其中一个是非代理IUknown接口:按通常的方式实现内部组件的IUknown接口。另一个是代理接口:未被聚合时将函数调用转发给非代理接口,被聚合时转发给外部组件的IUknown接口。

聚合情况下,代理IUknown接口将调用外部组件的IUknown实现。外部组件也可以内部组件的非代理IUknown接口来控制组件的生命期。

外部组件

//
// Component A
//
class CA : public IX
        // public IY @N
{
public:
    // IUnknown
    virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;
    virtual ULONG   __stdcall AddRef() ;
    virtual ULONG   __stdcall Release() ;

    // Interface IX
    virtual void __stdcall Fx() { cout << "Fx" << endl ;}

    /* @N Component1 aggregates instead of implementing interface IY.
    // Interface IY
    virtual void __stdcall Fy() { m_pIY->Fy() ;}
    */

    // Constructor
    CA() ;

    // Destructor
    ~CA() ;

    // Initialization function called by the class factory
    // to create the contained component
    HRESULT __stdcall Init() ;  // @N

private:
    // Reference count
    long m_cRef ;

    // Pointer to the aggregated component's IY interface
    // (We do not have to retain an IY pointer. However, we
    // can use it in QueryInterface.)
    IY* m_pIY ;                 // @N

    // Pointer to inner component's IUnknown
    IUnknown* m_pUnknownInner ; // @N
} ;

//
// Destructor
// 在外部组件的析构中释放内部组件接口
CA::~CA() 
{ 
    ::InterlockedDecrement(&g_cComponents) ; 
    trace("Destroy self.") ;

    // 恢复引用计数,保证组件不会试图再次将自己释放掉
    m_cRef = 1 ;  

    // 因为对内部组件的Release将会导致对外部组件的Release调用
    IUnknown* pUnknownOuter = this ;
    pUnknownOuter->AddRef() ;

    // 将外部组件释放掉
    m_pIY->Release() ;

    // 将内部组件释放掉
    if (m_pUnknownInner != NULL)   // @N
    {
        m_pUnknownInner->Release() ;
    }
}

// 创建内部组件
HRESULT __stdcall CA::Init()
{
    // Get the pointer to the outer unknown.
    // Since this component is not aggregated, the outer unknown
    // is the same as the this pointer. 
    IUnknown* pUnknownOuter = this ;

    trace("Create inner component.") ;
    HRESULT hr =
        ::CoCreateInstance(CLSID_Component2,         
                           pUnknownOuter, // 外部组件IUnknown接口 @N
                           CLSCTX_INPROC_SERVER,
                           IID_IUnknown,  // IUnknown,当聚合时  @N
                           (void**)&m_pUnknownInner) ; 
    if (FAILED(hr))
    {
        trace("Could not create contained component.") ;
        return E_FAIL ;
    }

    //需要在创建了内部组件之后再请求其IID_IY接口
    //QueryInterface会调用AddRef,由于内部组件是被聚合的,会被转发给外部组件的AddRef
    //因此,会导致外部组件的引用计数增加,而内部组件引用计数不变
    trace("Get the IY interface from the inner component.") ;
    hr = m_pUnknownInner->QueryInterface(IID_IY, (void**)&m_pIY) ; //@N
    if (FAILED(hr))
    {
        trace("Inner component does not support interface IY.") ;
        m_pUnknownInner->Release() ;
        return E_FAIL ;
    }

    //  为了实现正确计数,因为创建内部组件会导致外部组件计数增大
    pUnknownOuter->Release() ; //@N
    return S_OK ;
}

//
// IUnknown implementation
//
HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
{   
    if (iid == IID_IUnknown)
    {
        *ppv = static_cast<IUnknown*>(this) ; 
    }
    else if (iid == IID_IX)
    {
        *ppv = static_cast<IX*>(this) ;
    }
    else if (iid == IID_IY)
    {
        trace("Return inner component's IY interface.") ;
#if 1
        // 得到指向内部接口的指针
        return m_pUnknownInner->QueryInterface(iid,ppv) ; //@N
#else
        // Or you can return a cached pointer.
        *ppv = m_pIY ;   //@N
        // Fall through so it will get AddRef'ed
#endif
    }
    else
    {
        *ppv = NULL ;
        return E_NOINTERFACE ;
    }
    reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
    return S_OK ;
}

外部组件的类厂

//
// IClassFactory implementation
//
HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,
                                           const IID& iid,
                                           void** ppv) 
{
    // Cannot aggregate
    if (pUnknownOuter != NULL)
    {
        return CLASS_E_NOAGGREGATION ;
    }

    // Create component.
    CA* pA = new CA ;
    if (pA == NULL)
    {
        return E_OUTOFMEMORY ;
    }

    // Initialize the component. @N
    HRESULT hr = pA->Init() ;
    if (FAILED(hr))
    {
        // Initialization failed. Delete component.
        pA->Release() ;
        return hr ;
    }

    // Get the requested interface.
    hr = pA->QueryInterface(iid, ppv) ;
    pA->Release() ;
    return hr ;   
}

内部组件需要实现的接口—非代理IUknown接口

//
// 非代理 IUnknown 接口  @N
//
struct INondelegatingUnknown
{
    virtual HRESULT __stdcall
        NondelegatingQueryInterface(const IID&, void**) = 0 ;
    virtual ULONG __stdcall NondelegatingAddRef() = 0 ;
    virtual ULONG __stdcall NondelegatingRelease() = 0 ;
} ;

//
// IUnknown implementation
//
HRESULT __stdcall CB::NondelegatingQueryInterface(const IID& iid,
                                                  void** ppv)
{   
    if (iid == IID_IUnknown)
    {
        //将this指针换成了INondelegatingUnknown指针,重要!
        //保证返回的是一个非代理IUknown指针
        //当非代理接口指针查询IID_IUknown接口时,返回的将总是一个指向
        //其自身的指针。若不进行转换,返回的将是指向代理IUknown接口的指针
        *ppv = static_cast<INondelegatingUnknown*>(this) ;  // @N
    }
    else if (iid == IID_IY)
    {
        *ppv = static_cast<IY*>(this) ;
    }
    else
    {
        *ppv = NULL ;
        return E_NOINTERFACE ;
    }
    reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
    return S_OK ;
}

内部组件

//
// Component 
//
class CB : public IY,
           public INondelegatingUnknown
{
public:
    // 代理 IUnknown
    // 以内联方式实现
    // 只要将调用请求转发给外部IUknown接口或非代理IUnknown接口即可
    virtual HRESULT __stdcall
        QueryInterface(const IID& iid, void** ppv)
    {
        trace("Delegate QueryInterface.") ;
        return m_pUnknownOuter->QueryInterface(iid, ppv) ; 
    }

    virtual ULONG __stdcall AddRef() 
    {
        trace("Delegate AddRef.") ;
        return m_pUnknownOuter->AddRef() ; 
    }

    virtual ULONG __stdcall Release() 
    {
        trace("Delegate Release.") ;
        return m_pUnknownOuter->Release() ; 
    }

    // 非代理 IUnknown
    virtual HRESULT __stdcall
        NondelegatingQueryInterface(const IID& iid, void** ppv) ;
    virtual ULONG   __stdcall NondelegatingAddRef() ;
    virtual ULONG   __stdcall NondelegatingRelease() ;

    // Interface IY
    virtual void __stdcall Fy() { cout << "Fy" << endl ;}

    // Constructor
    CB(IUnknown* m_pUnknownOuter) ;

    // Destructor
    ~CB() ;

private:
    long m_cRef ;  //引用计数

    //当此组件被聚合时,指向外部IUknown接口
    IUnknown* m_pUnknownOuter ;
} ;

//
// Constructor
// 将外部IUknown的指针传给内部组件的构造函数,初始化m_pUnknownOuter
CB::CB(IUnknown* pUnknownOuter) 
: m_cRef(1)
{ 
    ::InterlockedIncrement(&g_cComponents) ; 

    //若此组件未被聚合,将m_pUnknownOuter设为指向非代理IUknown接口
    if (pUnknownOuter == NULL)
    {
        trace("Not aggregating; delegate to nondelegating IUnknown.") ;
        m_pUnknownOuter = reinterpret_cast<IUnknown*>
                          (static_cast<INondelegatingUnknown*>
                          (this)) ;
    }
    //若此组件被聚合,指向外部IUKnown接口
    else
    {
        trace("Aggregating; delegate to outer IUnknown.") ;
        m_pUnknownOuter = pUnknownOuter ;
    }
}

内部组件的类厂

//
// IClassFactory implementation
//
HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,
                                       const IID& iid,
                                       void** ppv)
{
    //当pUnknownOuter非空,表示外部组件想进行聚合
    //iid必须为IID_IUnknown,因为当组件被聚合时,只能返回一个IUknown接口
    if ((pUnknownOuter != NULL) && (iid != IID_IUnknown)) //@N
    {
        return CLASS_E_NOAGGREGATION ;
    }

    // Create component.
    CB* pB = new CB(pUnknownOuter) ; // @N
    if (pB == NULL)
    {
        return E_OUTOFMEMORY ;
    }

    //获取新创建的内部组件中客户所请求的接口
    //并非调用QueryInterface
    //因为类厂需要返回一个指向非代理IUknown接口的指针
    HRESULT hr = pB->NondelegatingQueryInterface(iid, ppv) ; //@N
    pB->NondelegatingRelease() ; 
    return hr ;   
}

客户

//
// Client.cpp - client implementation
//
#include <iostream.h>
#include <objbase.h>

#include "Iface.h"

void trace(const char* msg) { cout << "Client: \t" << msg << endl ;}

//
// main function
//
int main()
{
    // Initialize COM Library
    CoInitialize(NULL) ;

    trace("Get interface IX from Component 1.") ;
    IX* pIX = NULL ; 
    HRESULT hr = ::CoCreateInstance(CLSID_Component1,
                                    NULL, 
                                    CLSCTX_INPROC_SERVER,
                                    IID_IX, 
                                    (void**)&pIX) ;
    if (SUCCEEDED(hr))
    {
        trace("Succeeded creating component.") ;
        pIX->Fx() ;

        trace("Get interface IY from IX.") ;
        IY* pIY = NULL ;
        hr = pIX->QueryInterface(IID_IY, (void**)&pIY) ;
        if (SUCCEEDED(hr))
        {
            trace("Succeeded getting interface IY from IX.") ;
            pIY->Fy() ;

            trace("Get interface IX from IY.") ;
            IX* pIX2 = NULL ;
            hr = pIY->QueryInterface(IID_IX, (void**)&pIX2);
            if (SUCCEEDED(hr))
            {
                trace("Succeeded getting interface IX from IY.") ;
                pIX2->Release() ;
            }
            else
            {
                trace("Error! Should have gotten interface IX.") ;
            }

            pIY->Release() ;
        }
        else
        {
            trace("Could not get interface IY.") ;
        }

        pIX->Release() ;
    }
    else
    {
        cout << "Could not create component: " << hex << hr << endl ;
    }

    // Uninitialize COM Library
    CoUninitialize() ;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值