为什么QueryInterface针对同一行代码查看两个不同的COM项目?

时间:2017-04-28 20:07:44

标签: c++ casting com queryinterface

让我首先说我对COM的运作缺乏经验,但我的任务是调试其他人的问题

我有两个名为pvTaskCOM和pvFormsCOM的COM项目,每个项目都有很多接口,但我关注的两个是:

paskTaskCOM中的ITaskActPtr

pvFormsCOM中的IChartingObjectPtr

造成我问题的代码行是:

ITaskActPtr pTaskAct = m_pChartObj;

其中m_pChartObj是IChartingObjectPtr。我遇到的问题是在一个工作流程中分配后pTaskAct为NULL,但在大多数其他工作流程中都很好。我使用调试器深入研究了这里发生的事情,发现它在QueryInterface期间查看错误的COM条目。在工作正常的工作流中,QueryInterface从pvTaskCOM / pvTaskAct.h中获取条目:

BEGIN_COM_MAP(CTaskAct)
  COM_INTERFACE_ENTRY(ITaskAct)
  .
  .
  .
END_COM_MAP()

其中包含我正在尝试转换的接口,QueryInterface返回S_OK。

但是在这个其他工作流程中,m_pChartObj以同样的方式实例化,但是由于某些奇怪的原因,QueryInterface在pvFormsCOM / ChartingObject.h中查找

BEGIN_COM_MAP(CChartingObject)
  COM_INTERFACE_ENTRY(IChartingObject)
  .
  .
  .
END_COM_MAP()

不包含我们试图转换的ITaskAct,因此QueryInterface返回E_NOINTERFACE。

我遇到的问题是什么原因导致它在同一行代码中查看两个不同的COM?这是某种继承问题吗?我只需要朝着正确的方向迈出一步。

2 个答案:

答案 0 :(得分:2)

  

在工作正常的工作流程中,QueryInterface从pvTaskCOM / pvTaskAct.h中获取条目

不应该。

这一行:

ITaskActPtr pTaskAct = m_pChartObj;

在幕后做这件事:

ITaskAct *pTaskAct = NULL;
m_pChartObj->QueryInterface(IID_ITaskAct, (void*)&pTaskAct);

它询问IChartingObject的实现对象是否支持ITaskAct接口,如果是,则返回指向该实现的指针。因此,此代码应仅查看COM_MAP类的CChartingObject条目。它根本不应该看CTaskAct类。

  

但是在其他工作流中m_pChartObj以相同的方式实例化,但QueryInterface出于某种奇怪的原因在pvFormsCOM / ChartingObject.h中查找

这是正确的行为,因为那是实际实现CChartingObject的地方。如果ITaskAct COM_MAPCChartingObject没有CChartingObject::QueryInterface()条目,则E_NOINTERFACE的正确行为会因QueryInterface()错误而失败。

所以,真正的问题是你的“工作”工作流程实际上是有缺陷的,你的“非工作”工作流程正在做正确的事情。

  

是什么原因导致它在同一行代码中查看两个不同的COM?这是某种继承问题吗?

没有。 “工作”工作流程已损坏,简单明了。在IChartingObject界面上拨打CChartingObject::QueryInterface()应该拨打CTaskAct::QueryInterface(),但它显然正在呼叫IChartingObject*。所以要么

  • CTaskAct指针实际指向CChartingObject对象而不是IChartingObject对象

  • 某些内容已损坏,IChartingObject*的vtable是毫无疑问的受害者。

我会怀疑前者。因此,在“工作”工作流程中,确保ITaskAct*指针实际指向正确的对象。听起来有人使用了IChartingObject*并在不使用QueryInterface()的情况下将其输入QueryInterface()。或者他们在某个对象上调用IID_ITaskAct并询问IID_IChartingObject而不是IChartingObject*但是将返回的指针保存在ITaskAct*指针而不是{{1}}指针中

答案 1 :(得分:2)

你可能在管道中有点迷失了。这是C ++代码,旨在使COM不那么严苛。 COM的一个重要方面是客户端代码只能与接口一起使用。它对对象一无所知。接口是一个简单的契约,一个可以调用的函数列表。例如,IChartingObject将具有Paint()函数。 ITaskAct会有一个“任务”,一个Schedule()函数,而不是真正的想法。

请注意m_pChartObj是一个非常具有误导性的名称。它存储接口指针,而不是对象。但并不罕见,如果对象仅实现一个接口或者具有您一直使用的“主导”接口,则很容易将接口指针视为对象指针。将对象隐藏在服务器代码中是COM中非常强大的目标,您只能进行接口调用。

所以ITaskActPtr pTaskAct = m_pChartObj;基本上宣布,“我有一个图表,我想让下一个任务函数调用”。像Schedule()。这要求COM询问图表对象实现“你对任务接口合同有什么了解吗?”。不可避免地,它必须在IChartingObject来自CChartingObject的接口映射中查询服务器,以查看它是否也实现了ITaskAct。

所以你看到的情况完全正常。答案是“不”。