<com原理>与<com内幕>的聚合实现部分

转载 2013年02月16日 12:36:46

原文出自: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接口的话,查询一个不存在的接口,机会陷入接口查询的死循环了,外部查不到,调用内部,内部又调用外部。

对于使用代理来进行接口查询主要还是为了统一聚合和非聚合接口的查询,全部采用自外而内的查询方式。

相关文章推荐

COM技术内幕(第二部分)

  • 2007年09月26日 10:54
  • 3.22MB
  • 下载

一个使用MFC实现Com聚合的例子

在网上搜Com聚合的例子,发现都比较少,大多偏于原理性的介绍。由于模拟MFC 和真正使用MFC时,在查询IUnknown接口时,流程上有所不同,所以模拟MFC的方式与直接从CCmdTarget派生时,...

COM接口聚合实现解惑(C++虚表)

本文转自http://blog.csdn.net/nobugtodebug/archive/2009/10/12/4659351.aspx              最近看潘爱民的《COM原理...
  • arau_sh
  • arau_sh
  • 2011年01月05日 22:59
  • 729

COM原理及应用 第六部分

  • 2007年07月11日 15:11
  • 8.58MB
  • 下载

COM原理及应用 第三部分

  • 2007年07月11日 15:18
  • 8.58MB
  • 下载

COM技术内幕--调试接口与自动化

同每一个EXE都有其自己的进程不同,DLL将被映射到链接它们的EXE文件的进程空间中。由于这个原因,DLL也被称作是进程服务器,而EXE则被称作是进程外服务器。 不同进程空间中的相同的内存地址将指向不...

COM原理及应用 第四部分

  • 2007年07月11日 15:07
  • 8.58MB
  • 下载

COM原理及应用 第五部分

  • 2007年07月11日 15:09
  • 8.58MB
  • 下载

COM技术内幕学习笔记---第八章---组件复用:包容和聚合

COM 不支持实现继承的原因在于这种继承方式将 使得一个对象的实现同另外一个对象的实现紧紧地关联起来。 在这种情况下,当基类的实现被修改之后,派生类将无法正常运行而必须被修改。对于一个中等规模的C+...

COM原理及应用 第七部分

  • 2007年07月11日 15:14
  • 5.03MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:<com原理>与<com内幕>的聚合实现部分
举报原因:
原因补充:

(最多只允许输入30个字)