【问题标题】:Why is QueryInterface looking at two different COM projects for the same line of code?为什么 QueryInterface 会针对同一行代码查看两个不同的 COM 项目?
【发布时间】:2017-09-27 01:00:49
【问题描述】:

首先让我说我对 COM 的工作方式非常缺乏经验,但我的任务是为其他人调试问题

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

pvTaskCOM 中的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?这是某种继承问题吗?我只需要朝着正确的方向迈出一步。

【问题讨论】:

    标签: c++ casting com queryinterface


    【解决方案1】:

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

    不应该。

    这一行:

    ITaskActPtr pTaskAct = m_pChartObj;
    

    在幕后做这件事:

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

    它询问IChartingObject 的实现对象是否支持ITaskAct 接口,如果支持则返回指向该实现的指针。所以这段代码应该只查看COM_MAPCChartingObject 类的条目。它根本不应该查看CTaskAct 类。

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

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

    因此,真正的问题是您的“工作”工作流程实际上存在缺陷,而您的“非工作”工作流程正在做正确的事情。

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

    没有。 “工作”的工作流程已损坏,简单明了。在IChartingObject 接口上调用QueryInterface() 应该调用CChartingObject::QueryInterface(),但它显然是调用CTaskAct::QueryInterface()。所以要么

    • IChartingObject* 指针实际上指向的是 CTaskAct 对象,而不是 CChartingObject 对象

    • 内存已损坏,IChartingObject 的 vtable 是毫无防备的受害者。

    我会怀疑前者。因此,在“工作”工作流中,确保IChartingObject* 指针实际上指向正确的对象。听起来好像有人在不使用 QueryInterface() 的情况下将 ITaskAct* 类型转换为 IChartingObject*。或者他们在某个对象上调用QueryInterface() 并要求它提供IID_ITaskAct 而不是IID_IChartingObject,但随后将返回的指针保存在IChartingObject* 指针而不是ITaskAct* 指针中。

    【讨论】:

      【解决方案2】:

      您可能在管道中迷路了。这是 C++ 代码,旨在让 COM 变得不那么苛刻。 COM 的一个重要方面是客户端代码只能使用接口。它对对象一无所知。接口是一个简单的合约,是你可以调用的函数列表。 IChartingObject 会有一个 Paint() 函数。 ITaskAct 将有,没有真正的想法,一些“任务”,一个 Schedule() 函数。

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-12-05
        • 1970-01-01
        相关资源
        最近更新 更多