65.9K
CodeProject 正在变化。 阅读更多。
Home

从零开始的 COM - 第三部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (33投票s)

2004年4月18日

3分钟阅读

viewsIcon

112950

downloadIcon

4242

一篇关于 COM 包含的文章。

Component three from the demo application

引言

第一部分中,解释了一些关于 COM 技术的背景信息,并通过一个简单的例子创建了一个 COM 组件。在第二部分中,示例中的代码被优化,使得该组件不再绑定到其客户端,并且可以通过 COM 库创建。本部分是关于包含机制的,我将引导读者在另一个组件中重用第二部分中创建的组件。

第三部分 - COM 包含

COM 提供了两种代码重用机制。这些机制被称为包含或委托和聚合。在包含机制中,一个对象(“外”对象)成为另一个对象的客户端,内部使用第二个对象(“内”对象)作为服务提供者,而这些服务对外对象在其自身的实现中发现有用。COM 包含类似于 C++ 包含;然而,它是在接口级别。外组件包含指向内组件上接口的指针。外组件使用内组件的接口来实现自己的接口,它还可以通过将调用转发给内组件并在内组件的代码之前和之后添加代码来重新实现内组件的接口。包含重用了属于内组件的接口的实现。演示应用程序(如上图所示)中的一个组件(CComponent3)使用了这种机制。到目前为止,已经创建了一个组件,并且它的接口支持 Print 方法。现在,为了使用包含机制,我们可以在一个新组件中使用这个接口。在下文中,将创建一个新组件,它重用以前组件的接口以构建自己的接口。这个新接口将支持两个新的数学函数,并将被称为 IMath 接口。

步骤 1:组件的定义及其服务器的实现

使用与第二部分相同的方法,创建一个空项目来制作组件的服务器 (Component2.dll)。创建 DLL 的步骤在第二部分中有详细说明。

  • 新接口的定义

    您可能知道,每个 COM 接口都应该直接或间接派生自 IUnknown 接口,因此新接口可以从以前的部分中实现的接口派生,该接口是直接从 IUnknown 接口派生的

    interface IMath: IComponent; 
    {
      // desired functionality for the new component      
    
            
      virtual int  __stdcall Sum(int a,int b)=0;
      virtual int  __stdcall Subtract(int a,int b)=0;
    
    };
  • 组件类的定义

    您可能知道,组件的类可以通过从新创建的 COM 接口派生一个类来定义,并且由于将使用包含机制,因此需要一个创建内组件的方法(可以从类工厂调用)以及一个指向内部接口的指针

    class CComponent2:public IMath
    {
    private:
    long m_cRef;// Referance count
    IComponent* m_pInnerInterface; 
    // pointer to the interface of the inner component
    
    public:
    //IUnknown
    virtual HRESULT __stdcall QueryInterface(const IID & iid,void** ppv);
    virtual ULONG __stdcall AddRef();
    virtual ULONG __stdcall Release();
    //IComponent (interface of the inner component)
    virtual void __stdcall Print (const char* msg);   
    //IMath
    virtual int __stdcall Sum(int a,int b){return(a+b);}
    virtual int __stdcall Subtract(int a,int b){return(a-b);} 
    // A method to create the inner component
    HRESULT __stdcall CreateInnerComponenet();
    //Constructor
    CComponent2();
    //Destructor
    ~CComponent2();
    
    };
  • CComponent2::CreateInnerComponent的实现

    Implementation of CreateInnerComponent

  • CFactory::CreateInstance()的实现
    ////////////////////////////////////////////////////////////////////////
    HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnkOuter, 
                                               const IID& iid,void** ppv)
    
    {
        // Cannot aggregate
        if (pUnkOuter != NULL)
              return CLASS_E_NOAGGREGATION ;
    
        // Create component2(the outer component).
             CComponent2* pComponent2 = new CComponent2 ;
    
        if (pComponent2 == NULL)
            return E_OUTOFMEMORY ;
    
        // Create the inner component. 
        HRESULT hr = pComponent2->CreateInnerComponent() ;
        if (FAILED(hr))
        {
            pComponent2->Release() ;
            return hr ;
        }
    
        // Get the requested interface.
            hr = pComponent2->QueryInterface(iid,(void**) ppv) ;
        //if Query faild the component will delete itself
      
        if (FAILED(hr))
           pComponent2->Release() ;
    
      return hr ;   
    }
  • 将调用转发给内组件
    ///////////////////////////////////
    void __stdcall CComponent2::Print(const char* msg)
    {
      m_pInnerInterface->Print(msg);
    }
  • 使用GUIDGEN.EXE为组件类提供 GUID,并为IMath接口提供接口标识符
    //CLSID for component2's class
    // {0CDD7D97-DD93-450a-BBCF-6B22894FAFF5}
    extern "C" const GUID CLSID_Component2 = 
    { 0xcdd7d97, 0xdd93, 0x450a, 
      { 0xbb, 0xcf, 0x6b, 0x22, 0x89, 0x4f, 0xaf, 0xf5 } };
    
    //IID for component2's IMath interface
    // {C8ACE8CC-0480-4f1a-8A62-89717E1D5705}
    extern "C" const IID IID_IMath = 
    { 0xc8ace8cc, 0x480, 0x4f1a, 
      { 0x8a, 0x62, 0x89, 0x71, 0x7e, 0x1d, 0x57, 0x5 } };

步骤 2:客户端

The Client

下图显示了客户端程序的输出窗口

The output window

包含的一种用途是通过向现有接口添加代码来扩展接口。例如,演示应用程序中的内部 Component2 有一个黄色窗口,尽管 Component2 的背景色最初是白色的。另一个变化是蝴蝶在创建内组件后开始飞行,尽管最初它不会飞。这些变化是通过包含机制进行的。以下代码段和图显示了如何专门化 Component2 的一种方法。

/////////////////////////////////////////////////////////////////////
void __stdcall CComponent3::ShowWndBackground_Com2(COLORREF bgcolor) 
// default background color is white
{
//--------------------------------------------------------------------------------//
//The outer component (Component3) can reimplement an interface supported
//by the inner component by forwarding calls to the inner component.The outer
//component can specialize the interface by adding code before and after the code
//for the inner component. As an example the background color of component2's window
//will be changed to yellow 
//--------------------------------------------------------------------------------//
    bgcolor=RGB(255,255,0);  //bgcolor is changed to yellow color
//--------------------------------------------------------------------------------//
    m_pIComponent2->StartFlying();//The butterfly should fly 
    m_pIComponent2->ShowWndBackground_Com2(bgcolor);
    
}

Extending an interface by COM Containment

演示应用程序的代码与文章中解释的示例非常相似,并且仅使用一个窗口来可视化每个组件,每个窗口的菜单充当组件的接口。我希望这些文章对您有所帮助,并且可以作为您从零开始学习 "COM" 的指南。

© . All rights reserved.