COM包容和聚合

   包容和聚合实际上是一个组件使用另一个组件的技术。对于这两个组件,可以把第一个组件称为外部组件,被使用组件称为内部组件。

     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 ;
	}
}
 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值