COM 概念:组合






4.09/5 (10投票s)
2003 年 11 月 13 日
4分钟阅读

116885

1026
COM 概念:组合
引言
可重用性是编程领域任何技术成功的关键。 C++ 通过提供继承的概念来支持可重用性。 继承是子类从其基类(父类)继承功能的方式。 子类可以选择修改基类提供的功能(重写),或继续使用与基类相同的功能。 继承使程序员的生活变得更轻松,但它存在一个问题。 C++ 支持的实现继承会在基类和子类之间建立牢固的绑定或契约。 对基类的任何更改都可能对子类产生巨大影响,并可能导致子类的客户端中断。 实现继承不是创建可重用组件的适当技术,这些组件可以随时随地被任何人使用,而无需担心组件的内部实现。
COM 不支持实现继承,因为它违反了 COM 技术的座右铭,即创建可重用组件。 COM 支持接口继承,其中子类继承基类的接口,而不是实现。 接口继承可以保护组件的客户端免受更改。 可以使用组件包含的概念在 COM 中模拟实现继承。 在 COM 中,可重用性是通过使用包含和聚合来实现的。 本文将详细介绍包含技术,其中外部组件使用内部组件。 聚合将在另一篇文章中介绍。
包含
COM 中的一切都与接口相关。 包含也是在接口级别实现的。 COM 包含与 C++ 包含相同,其中外部组件是内部组件的客户端。 外部组件具有指向内部组件上接口的指针。 内部组件不会直接暴露给客户端,因此只有外部组件的 IUnknown 会暴露给客户端。 在包含中,外部组件将调用转发(委托)到内部组件。
可以实现包含的两种情况。 第一种情况是外部组件实现自己的接口并使用内部组件的接口。 第二种情况可能是外部组件重新实现内部组件支持的接口,并将调用转发到内部组件的接口。
在包含中,外部组件充当客户端并使用内部组件的接口。 在实现包含时,内部组件和客户端不知道他们正在成为包含实现的一部分。 必须修改外部组件以支持包含。
示例代码 - 说明
本文将探讨第一种场景来解释包含技术。 在此示例代码中,外部组件利用了内部组件提供的功能。 外部组件需要进行一些修改才能将内部组件作为包含对象容纳。 客户端和内部组件不会受到影响,也不会意识到他们正在参与包含实现。 此示例代码将演示客户端不熟悉外部组件正在使用内部组件的服务这一事实。
外部组件,即 CMath
将具有一个新的成员变量 m_pISqaure
,该变量是指向内部组件上的 ISquare
接口的指针。
// Code Snippet for CMath class. class CMath : public IMath { public: // Implementing IUnknown Interface. virtual HRESULT __stdcall QueryInterface(const IID& iid,void **ppv); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); //Implementing IMath Interface. virtual void _STDCALL SumSquare(int Val1,int Val2,int* pResult); // Constructor CMath(); // Destructor ~CMath(); // Pointer to ISquare interface on the inner component. ISquare* m_pISquare; private: long m_cRef; };
由于此 COM 服务器 (ContainmentSample.dll) 支持两个 COM 组件,即 CMath
和 CSquare
,因此 DllGetClassObject
应该具有对这两个 ClassID 的验证。
// Code Snippet for DllGetClassObject. // COM SCM creates a class object only when the request has come for // CLSID_CMath and CLSID_CSquare. After creating the // class object, the IClassFactory interface pointer on the // class object is returned back to the client. STDAPI DllGetClassObject(const CLSID & clsid,const IID& iid,void **ppv) { // This server supports two COM Components and hence // validation is performed. if((clsid == CLSID_CMath) || (clsid == CLSID_CSquare)) { cout<<"The requested component is supported by " "COM server (ContainmentSample.dll)" << endl; } else { return CLASS_E_CLASSNOTAVAILABLE; } CFactory *pFactory = new CFactory(); if (pFactory == NULL) { return E_OUTOFMEMORY; } HRESULT hResult = pFactory->QueryInterface(iid,ppv); static_cast<< IUnknown* >>(pFactory)->Release(); return hResult; }
必须修改外部组件的 CreateInstance
以容纳内部组件的创建并将内部组件上的 ISquare
接口存储在其成员变量 m_pISqaure
中。 外部组件使用 CLSID 参数 CLSID_CSquare
调用 CoCreateInstance
并查询内部组件 (CSquare
) 上的 ISquare
接口,如果调用成功,它会将接口指针存储在 m_pISqaure
中。
//Code Snippet for CreateInstance of Class Object. //This snippet shows the part of the code which is //executed during the creation of an outer //component. //This is executed when the client calls the CreateInstance, //after getting the IClassFactory //interface pointer on CMath's instance. //The client gets the IClassFactory interface pointer by //calling CoGetClassObject. if ((iid == IID_IMath) || (iid == IID_IUnknown)) { CMath* pMath = new CMath(); if(pMath == NULL) { return E_OUTOFMEMORY; } // Here, the Outer Component initializes the inner component. // The CoCreateInstance is called by // the outer component during its creation and // it queries for the ISquare interface on the inner // component, and if the calls succeeds it stores the pointer // in its variable m_pISqaure. cout<<"Call to Create the Inner Component" << endl; hResult = CoCreateInstance(CLSID_CSquare,NULL,CLSCTX_INPROC_SERVER, IID_ISquare,(void**)&pMath->m_pISquare); cout<<"CoCreateInstance for CSquare has been called"<< endl; hResult = pMath->QueryInterface(iid,ppv); if(SUCCEEDED(hResult)) { pMath->Release(); } }
在执行客户端应用程序之前,需要使用 regsvr32 实用程序注册 COM 服务器 (ContainmentSample.dll)。 下一篇文章将介绍聚合技术,它是包含的一种特殊情况。