可以聚合的com需要在内部实现一个代理INondelegatingUnknown接口,并在类厂创建第一个接口的时候,检查
// iid must be IID_IUnknown for aggregating
if ( ( pUnknownOuter != NULL ) && ( iid != IID_IUnknown ) )
{
return CLASS_E_NOAGGREGATION;
}
如果是被聚合的,则查询的第一个接口必须是IUnknown,然后不管是否是被聚合状态还是非被聚合状态(判断pUnknownOuter是否为空)工厂都会返回代理INondelegatingUnknown的接口指针,而不是平常的IUnknown,可以聚合的组件内部接口的查询和实现都是通过INondelegatingUnknown来进行的。
CA *pObj=new CA (pUnknownOuter);
if (NULL==pObj) return hr;
//Obtain the first interface pointer (which does an AddRef)
hr = pObj->NondelegationQueryInterface(iid, ppv);
类厂创建完毕后通过方法NondelegationQueryInterface来查询所需要的接口对象指针。没有被聚合时都是通过INondelegatingUnknown来进行查询的接口对象指针的,这一点和普通com组件几乎是一样的,但也有一部分不同。如果查询的接口是IUnknown接口指针则返回INondelegatingUnknown的指针,因为IUnknown与INondelegatingUnknown的虚表接口是一样的,因此强转后通过IUnknown的查询都会转到INondelegatingUnknown上,但通过INondelegatingUnknown查询到的其他接口指针再进行查询的时候,(比如IX->QueryInterface())他们会调用虚表中真正的IUnknown的方法,而不是代理的,这时候就需要通过判断自己当前是否是被聚合的(判断pUnknownOuter)如果是被聚合的,则调用pUnknownOuter->QueryInteface发送给上层进行查询,如果没有聚合,则继续跳转到INondelegatingUnknown的接口上进行查询。
这里COM原理与COM技术内幕的聚合com的QueryInteface是有点区别的
com原理是这样实现的
HRESULT CA::QueryInterface(const IID& iid, void **ppv)
{
if ( m_pUnknownOuter != NULL )
return m_pUnknownOuter->QueryInterface(iid, ppv);
else
return NondelegationQueryInterface(iid, ppv);
}
通过m_pUnknownOuter来判断似乎否是被聚合状态 。
而com技术内幕实现是:
HRESULT CA::QueryInterface(const IID& iid, void **ppv)
{
return m_pUnknownOuter->QueryInterface(iid, ppv);
}
这里我们就奇怪了,他为什么没有判断呢
原因就在构造函数给 m_pUnknownOuter进行复制的时候,采用的一点小技巧。在构造函数中会判断传入的pUnknownOuter是否为空,如果pUnknownOuter是空的话,则
m_pUnknownOuter = reinterpret_cast<IUnknown*>(static_cast<INondelegationUnknown*>(this));
由于IUnKnown与INondelegationUnknown虚表是一样的,因此通过reinterpret_cast直接强转使m_pUnknownOuter直接指向INondelegationUnknown的虚表。这样之后所有调用m_pUnknownOuter就不用进行判断是否是被聚合的了。
为什么要使用这层代理呢?
当我们通过外部的com组件聚合一个内部的com组件的时候,会把外部的IUnknown指针传递给内部保存,外部查询一个内部的接口插叙不到的时候,就会调用内部的组件进行查询,内部组件查询到接口会,会返回接口的指针。根据com规范,如果IA可以查到IB,则IB也可以查询到IA,而被聚合的IB不知道IA的存在这时候就需要调用之前外部传入的m_pUnknownOuter进行查询这里就用到了我们上面所说的判断是否被聚合,如果被聚合的话则调用m_pUnknownOuter进行查询。
问题也就在这里
情况一,如果外部组件通过接口IA查询IB查询不到,则-》》》调用就会调用内部聚合的组件进行查询-》》》聚合组件接口的QueryInterface-》》》实际调用到聚合组件接口的NondelegationQueryInterface-》》》查询到IB直接返回接口指针,查询不到返回没有接口。
情况二,如果通过IB来查找IXXX通过真正的IUnknown的QueryInterface-》》》,发现是被聚合的则调用m_pUnknownOuter进行查询,这时候就按情况一继续进行了。
也就是说,搜有查询最后都会汇集到NondelegationQueryInterface
IA->QueryInterface() --------> INondelegationUnknown->NondelegationQueryInterface
IB->QueryInterface() --------> IA->QueryInterface() --------> INondelegationUnknown->NondelegationQueryInterface
如果不存在INondelegationUnknown接口的话,查询一个不存在的接口,机会陷入接口查询的死循环了,外部查不到,调用内部,内部又调用外部。
对于使用代理来进行接口查询主要还是为了统一聚合和非聚合接口的查询,全部采用自外而内的查询方式。