包容和聚合实际上是一个组件使用另一个组件的技术。对于这两个组件,可以把第一个组件称为外部组件,被使用组件称为内部组件。
1、包容简介
COM包容同C++包容是相似的。但是在COM中,同其他内容类似,包容也是在接口级完成的。外部组件包含指向内部组件接口的指针。此时外部组件只是内部组件的一个客户,它将使用内部组件的接口来实现自己的接口。
外边组件也可以通过将调用转发给内部组件的方法重新实现内部组件所支持的某个接口(适配器模式?很像啊)。并且外部组件还可以在内部组件代码的前后加上一些代码以对接口进行改造。

2、聚合简介
聚合是包容的一个特例。当一个外部组件聚合了某个内部组件的的一个接口时,它并没有像包容那样重新实现此接口并明确地将调用请求转发给内部组件。相反,外部组件将直接把内部组件的接口返回给客户。使用此方法,外部组件将无需重现实现并转发接口中的所有函数。
但是使用此方式,外部组件将无法对接口中的函数进行任何改造。当外部组件将内部组件的接口返回给客户之后,客户就直接同内部组件打交道了。但是此时客户不应该知道他是在同两个不同的组件交互,否则将无法满足封装的要求。这种使得外部组件和内部组件看起来像一个组件的做法是成功的进行聚合的关键。
3、聚合的实现
首先简要描述一下聚合是如何实现的。假定客户向外部组件请求接口IY。此时外边组件可以不实现IY接口,而只需将向部组件请求查询此IY接口并将此接口指针返回给客户。客户可以直接使用此指针调用内部组件所实现的那些IY成员函数。此时就IY接口来说,外边组件相当于被架空了:它放弃了对IY接口的控制而将此控制交给了内部组件。
聚合的目标就是使得客户确信内部组件所实现的某个接口是由外部组件实现的。此时需要从内部组件中将一个指针直接传给客户并使得客户相信此接口是属于外部组件的。若将内部组件的按通常方式实现的接口指针传给客户,那么客户得到的是对于组件的一个分裂视图,也就是说内部组件的接口将调用内部组件所实现的QueryInterface,而外部组件则有其自己的QueryInterface。当客户查询属于内部组件的接口时,它所获得的关于组件的功能视图和查询外部组件接口的时候是不同的。

3.1 聚合的IUnknown接口
一、外部IUnknown接口
在CoCreateInstance和IClassFactory::CreateInstance函数中,有个pUnkOuter指针。
WINOLEAPI CoCreateInstance(__in REFCLSID rclsid,
__in_opt LPUNKNOWN pUnkOuter, //out component
__in DWORD dwClsContext,
__in REFIID riid,
__deref_out LPVOID FAR* ppv);
HRESULT STDMETHODCALLTYPE CreateInstance(
IUnknown *pUnkOuter,
REFIID riid,
void **ppvObject) = 0;
外部接口可以使用pUnkOuter参数给内部组件传递其IUnknown接口。若此外部IUnknown接口不为空,说明我们想
进行组件聚合。使用传给CreateInstance的IUnknown接口,被创建的组件将知道它是被聚合的,并且可以知道是谁在
聚合它。对于未被聚合的组件,它可以使用自己的IUnknown接口,否则它将相应的接口调用转发给外部IUnknown接口。
二、代理和非代理IUnknown接口
为了支持聚合,内部组件实际上将实现两个IUnknown接口。其中的非代理接口将按照通常的方式实现。而代理IUnknown接口将调用转发给外部的IUnknown接口或非代理IUnknown接口。若内部组件没有被聚合,那么代理IUnknown接口将这些调用转发给非代理IUnknown接口,否则将转发给外部的IUnknown接口。聚合组件的客户将调用代理IUnknown接口,而外部组件将通过非代理IUnknown接口来操作内部组件。(本质啊!!!!)


三、非代理IUnknown接口
内部组件需要两个不同的IUnknown实现,但是的c++中不允许在一个类中完成同一个接口的两个不同实现。因此需要修改某一个IUnknown接口的名字以便两个接口的名字不会冲突。我们声明了
struct INondelegatingUnknown
{
virtual HRESULT __stdcall
NondelegatingQueryInterface(const IID&, void**) = 0 ;
virtual ULONG __stdcall NondelegatingAddRef() = 0 ;
virtual ULONG __stdcall NondelegatingRelease() = 0 ;
} ;
COM不关心接口的名字是什么,而只关心vtbl的结构,我们的INondelegatingUnknown结构和IUnknown完全相同。其中的NondelegatingAddRef和NondelegatingRelease的实现也和通常的实现一样。不同的是
NondelegatingQueryInterface:
HRESULT __stdcall CB::NondelegatingQueryInterface(const IID& iid,
void** ppv)
{
if (iid == IID_IUnknown)
{
// !!! CAST IS VERY IMPORTANT !!!
*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 ;
}
注意,在上面的代码中我们将内部组件的this指针转化成一个INondelegatingUnknown指针,这种转化是非常重要
的。通过这一转化,我们可以保证返回的是一个非代理的IUnknown指针。当向非代理指针查询IID_IUnknown接口时,
它返回的将总是一个指向其自身的指针。如果不进行这种转化,则返回的指针是指向代理IUnknown接口的,当此组件被聚合时,代理IUnknown接口将所有的QueryInterface调用转发给外部IUnknown。
被聚合组件的客户永远也不会获得内部组件的非代理IUnknown接口。当客户请求IUnknown接口指针的时候,它得到的将是外部组件的IUnknown接口。内部组件的非代理IUnknown接口只能由外部组件获取。
四、代理未知接口的实现
class CB : public IY,
public INondelegatingUnknown
{
public:
// Delegating 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() ;
}
// Nondelegating IUnknown
virtual HRESULT __stdcall
NondelegatingQueryInterface(const IID& iid, void** ppv) ;
virtual ULONG __stdcall NondelegatingAddRef() ;
virtual ULONG __stdcall NondelegatingRelease() ;
// Interface IY
virtual void __stdcall Fy() { std::cout << "Fy" << std::endl ;}
// Constructor
CB(IUnknown* m_pUnknownOuter) ;
// Destructor
~CB() ;
private:
long m_cRef ;
IUnknown* m_pUnknownOuter ;
} ;
3.2 内部组件的实现
一、外部的init函数
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, // Outer component's IUnknown @N
CLSCTX_INPROC_SERVER,
IID_IUnknown, // IUnknown when aggregating @N
(void**)&m_pUnknownInner) ;
if (FAILED(hr))
{
trace("Could not create contained component.") ;
return E_FAIL ;
}
// This call will increment the reference count on the outer component.
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 ;
}
// We need to release the reference count added to the
// outer component in the above call. So call Release
// on the pointer you passed to CoCreateInstance.
pUnknownOuter->Release() ; //@N
return S_OK ;
}
二、内部组件的IClassFactory::CreateInstance函数
HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,
const IID& iid,
void** ppv)
{
// Aggregate only if the requested iid is IID_IUnknown.
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 ;
}
// Get the requested interface.
HRESULT hr = pB->NondelegatingQueryInterface(iid, ppv) ; //@N
pB->NondelegatingRelease() ;
return hr ;
}
三、内部组件的构造函数
CB::CB(IUnknown* pUnknownOuter) : m_cRef(1)
{
::InterlockedIncrement(&g_cComponents) ;
if (pUnknownOuter == NULL)
{
trace("Not aggregating; delegate to nondelegating IUnknown.") ;
m_pUnknownOuter = reinterpret_cast<IUnknown*>
(static_cast<INondelegatingUnknown*>
(this)) ;
}
else
{
trace("Aggregating; delegate to outer IUnknown.") ;
m_pUnknownOuter = pUnknownOuter ;
}
}
分享到:
相关推荐
COM包容与聚合是COM技术中的两个核心概念,它们是实现对象间交互和组合的关键机制。 **COM包容**,也称为嵌入或包含,指的是一个COM对象可以嵌入到另一个COM对象之中,形成一个复合对象。这种包容关系允许一个对象...
本文将深入探讨在进程内COM组件的两种复用方式:包容(Aggregation)和聚合(Containment)。 1. 包容(Aggregation) 包容是一种特殊形式的COM组件关系,其中一个COM对象包含另一个COM对象,作为其内部成员。在...
标题“Windows COM组件包容、聚合实例”主要涉及的是COM组件的两个关键概念:包容(Containment)和聚合(Aggregation)。这两个概念都是COM组件间交互的重要方式。 1. **包容(Containment)**: 包容是指一个COM...
com组件示例,包含继承,包容以及聚合三种Com组件组织方法
com 聚合
首先,复习COM包容和聚合模型。在COM中,包容允许一个对象包含另一个对象,而聚合则是一种特殊的包容形式,其中内部对象的`IUnknown`接口通过外部对象暴露。在非聚合模式下,每个对象有自己的`IUnknown`接口;而在...
在COM中,有两大重用模型:包容和聚合。这两种模型都是为了提高代码复用,减少重复工作,从而提高开发效率和系统性能。 1. 包容:包容是一种包含关系,其中一个COM组件(例如对象BB)包含并使用另一个已存在的组件...
首先,让我们复习一下COM的包容和聚合模型。包容模型是指一个COM对象可以包含另一个COM对象,使得它们看起来像是单一的对象。在非聚合模式下,客户程序通过`QueryInterface`、`AddRef`和`Release`来访问接口。而在...
包容和聚合是COM中用于复用组件的高级特性,允许开发者在一个组件内部使用另一个组件的功能。 COM的实现也涉及到了一些复杂的问题,例如线程模型。COM支持单线程单元(Single-threaded Apartment, STA)和多线程...
在实验中,你可以深入理解COM的包容聚合,即一个组件可以包含其他组件,形成更复杂的组件结构。 EJB是Java平台上的企业级应用组件模型,主要用于构建可部署在服务器端的业务逻辑。EJB组件分为三种类型:会话bean...
8.5 现实世界中的聚合和包容 163 8.5.1 组件的内部状态信息 163 8.5.2 虚拟函数的模拟 165 8.6 本章小结 166 第9章 编程工作的简化 167 9.1 客户端的简化 167 9.1.1 智能接口指针 168 9.1.2 C++包装类 179 ...
课程目标是使学员能深入理解组件及其相关概念,如IUnknown和IDispatch接口,以及它们与组件的关系,同时掌握组件的生命周期管理和重用策略,如包容和聚合。此外,学员还将学习如何应对分布式环境中的通信问题,如...
COM的可重用性体现在包容和聚合,允许组件嵌入到其他组件中或者多个组件共享同一组件功能。 COM库的实现涉及到组件注册、类工厂(Class Factory)、DllGetObjectClass函数、CoGetClassObject函数、CoCreateInstance...
3.5.1 可重用机制:包容和聚合 3.5.2 进程透明性 3.5.3 安全性机制 小结 第4章 COM扩展技术 4.1 可连接对象机制 4.1.1 客户、接收器与可连接对象 4.1.1.1 接收器 4.1.1.2 可连接对象 4.1.1.3 客户 4.1.2 实现可...
《COM技术内幕》作为一本权威的介绍COM技术的书籍,其内容涵盖了COM的基本概念、架构、接口定义、类工厂、注册机制、包容和聚合模型等关键部分。它不仅为初学者提供了入门知识,也向经验丰富的开发者深入介绍了COM的...
【第10章 开发ADO数据库组件】 在本章中,我们将探讨如何开发使用ActiveX Data Objects (ADO)的...通过合理利用包容和聚合,我们可以构建出高效且灵活的数据访问组件,使得应用程序能够轻松地与各种数据源进行通信。
此外,COM还支持组件的可重用性,通过包容和聚合机制,一个组件可以包含或使用其他组件,增强了代码复用性。 接口描述语言(IDL)是定义COM接口的关键工具,它允许开发者描述接口的方法和属性。MIDL工具会将IDL文件...
此外,COM技术通过包容和聚合的方式来支持组件对象的复用,这两种方式允许一个对象使用另一个对象的功能,同时保持了接口和实现的分离。 为了实现统一数据接口,文章还提出了技术实现方案,这涉及到如何对CAT测量...
COM技术内幕一书的源代码,该书全面系统的介绍COM技术的著作,主要介绍了组建,接口,QueryInterface函数,引用计数,动态链接,HRESULT、GUID、注册表及其他细节,类厂,组建复用:包容与聚合,编程工作简化,EXE中...