让我首先说我对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?这是某种继承问题吗?我只需要朝着正确的方向迈出一步。
答案 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_MAP
中CChartingObject
没有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。
所以你看到的情况完全正常。答案是“不”。