发布时间:2011-3-12 11:40
分类名称:COM
对象A是已经被实现好的COM对象。
包容:
B实现ISomeInterface和IOtherInterface接口,然后在ISomeInterface的实现中调用A对象提供的服务。一般的,A的生存期在B生存期内。
聚合:
B只实现了IOtherInterface,当客户要求B对象提供ISomeInterface时,由于A被聚合到了B中,B可以提供此服务。一般的,A的生存期在B生存期内。
聚合的复杂性在于,B虽然知道A实现了什么接口,但A不知道B实现了什么接口。这样就导致通过B获取到A的实例指针后,通过此指针调用的QueryInterface是A的,也就无法Query出B中实现的接口来。而且A和B的接口继承于俩个不同的IUnknown,获取回的指针也不一样。这样就违背了QueryInterface需要遵循的原则。
解决聚合复杂性的办法,在A中留下一个IUnknown 指针,通过外部(例如B)将其IUnknown传入给A,A就可以利用此接口来代理B 来做Query Interface了。
实现:
class CB : public IOtherInterface
{
......
private:
IUnknown *m_pUnknownInner; // Point to A's IUnknown
......
}
CB::QueryInterface(...)
{
......
if (iid == IID_SomeInterface)
return m_pUnknownInner->QueryInterface(iid, ppv);
......
}
A 分被聚合和未被聚合俩种情况。
如果被聚合,A就使用内部这个指针(即B传入的自己实例指针)来Query Interface,相当于是B在Query Interface,获取的IUnknown都是B的。而且不存在A找不到B的Interface。
如果未被聚合,那么就什么都不做,不使用这个指针。
CoCreateInstance有一个参数,叫IUnknown *pUnknownOuter,这个指针,就是A中要保存的B实例指针。
A内部,为了区分聚合和非聚合,定了了俩个IUnknown(delegating unknown, undelegating unknown).这个俩个委托和非委托名词,曾经困扰了我很久,后来发现是因为这俩个词语本身的语义对我的误导。如果要以我自己的方式区分,我将其区分为一个IUnknown是Proxy(代理),一个IUnknown是正常的IUnknown。代理只是个中转站,负责分发,其内部其实就是一个判断,如果A的pUnknownOuter为NULL,那么就转发给正常的IUnknown。如果pUnknownOuter不为NULL,那么就调用它自身的接口函数。
class INondelegationUnknown
{
public:
virtual HRESULT _stdcall NondelegationQueryInterface(...) = 0;
virtual ULONG _stdcall NondelegationAddRef() = 0;
virtual ULONG _stdcall NondelegationRelease() = 0;
};
class CA : public ISomeInterface, public INondelegationUnknown
{
......
public:
NondelegationQueryInterface(...);
NondelegationAddRef();
NondelegationRelease();
QueryInterface(...);
AddRef();
Release();
......
private:
IUnknown *m_pUnknownOuter;
......
};
CA::NondelegationQueryInterface(...)
{ 正常Query,就像什么都没发生过 }
CA::NondelegationAddRef()
{ 正常AddRef,就像什么都没发生过 }
CA::NondelegationRelease()
{ 正常Release,就像什么都没发生过 }
CA::QueryInterface(...){
if (NULL == m_pUnknownOuter) // 转发
Call NondelegationQueryInterface
else
Call m_pUnknownOuter->QueryInterface
}
CA::AddRef(){
if (NULL == m_pUnknownOuter) // 转发
Call NondelegationAddRef
else
Call m_pUnknownOuter->AddRef
}
CA::Release(){
if (NULL == m_pUnknownOuter) // 转发
Call NondelegationRelease
else
Call m_pUnknownOuter->AddRef
}
再看看B何时将它自己的指针传递给A:
HRESULT CB::Init()
{
IUnknown *pUnknownOuter = (IUnknown *) this;
HRESULT result = ::CoCreateInstance(CLSID_ComponetA, pUnknownOuter, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&m_pUnknownInner);
......
传入pUnknownOuter(既B的this),而且得到m_pUnknownInner(A的this)。
}