原文出自:http://www.cnblogs.com/sosopop/archive/2010/07/25/1784613.html
觉得写得很不错,转载下
可以聚合的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接口的话,查询一个不存在的接口,机会陷入接口查询的死循环了,外部查不到,调用内部,内部又调用外部。