【问题标题】:Is calling QueryInterface() ever absolutely necessary in COM?在 COM 中调用 QueryInterface() 是绝对必要的吗?
【发布时间】:2019-10-19 11:38:11
【问题描述】:

this C++/COM shell 扩展教程中,程序员演示了(出于启发目的)您可以放弃调用QueryInterface() 并简单地传递一个通用对象。至少在实现DllGetClassObject() 时有效。他说QueryInterface() 的目的只是让每个对象自己说明它是否支持给定的接口。

与此同时,Microsoft 似乎 say 认为 QueryInterface() 是获取指向对象上特定接口的指针所必需的。

那么QueryInterface()在多大程度上是必要的?是否有任何时候调用QueryInterface() 是绝对必要的,没有它,代码将无法工作?还是如视频教程所建议的那样,获取对象本身在技术上就足够了?

【问题讨论】:

  • 这不是“技术上足够”,它是UB。未定义的行为可能会偶然发生。在 C++ 中需要转换为接口类型,这是 QI 的工作。
  • 如果您不能 100% 确定要使用的某个界面是否受支持,这绝对是必不可少的,并且每次都使用它是个好主意。本教程正在教授错误的信息和不正确的编码技术。找一个更好的。
  • 在同一个对象上,不同的接口可以有不同的二进制指针。所以调用 QI 是绝对必要的,如果你不知道对象的布局
  • 他说 QueryInterface() 的目的只是让每个对象自己说出它是否支持给定的接口。这绝对是错误的。 QI easy 可以返回另一个二进制指针,与它调用的不同:p->QI(iid, &q) 和 (void*)p != (void*)q 可以是
  • @RbMm 我的意思是我说的。被查询的对象可以分配并返回指向内存中完全不同的对象的接口指针。例如,在 ATL 中,使用 tear-off classes。没有要求QueryInterface() 必须为被查询的同一对象返回一个接口,except 当查询的接口是IUnknown 时,因为 COM 使用它来进行身份检查。如果查询IUnknown的两个接口返回相同的指针,那么它们是由相同的对象实现的

标签: c++ winapi com shell-extensions queryinterface


【解决方案1】:

不,作为一般规则,您不能跳过调用QueryInterface,除非您知道您拥有的接口指针已经正确。

如果我们想象一个实现 IFoo 和 IBar 的对象,布局可能看起来像这样:

VT
  IFoo_QueryInterface(...)
  IFoo_AddRef()
  IFoo_Release()
  IFoo_FooFight(int, int)
VT
  IBar_QueryInterface(...)
  IBar_AddRef()
  IBar_Release()
  IBar_BarBarBar(int)

对象的一个​​实例可能指向IFoos v-table 指针或IBars v-table 指针。在不知道它到底是哪一个的情况下调用第四个方法会崩溃,因为参数计数不一样。即使签名相同,调用任意方法也不是一个好主意。

您所指的视频只是因为DllGetClassObject 的呼叫者通常只要求IClassFactory。但即使在那里也不安全,因为有人可能会要求IClassFactory2。因此,正确的DllGetClassObject 实现也应该调用QueryInterface

我建议在学习 COM 基础知识时尝试使用 C 而不是 C++ 进行编码,这会迫使您自己处理所有 v-table 间接寻址。详情请查看this series

【讨论】:

  • vtable(函数指针数组)永远不会放在对象内存中(不是它布局的一部分)。将所有 this 指针都放在对象主体中,这将是非常低效的。真正的对象,在自身主体中包含指向 vtable 的单个指针。接口 - 这是指向 vtable 的指针。然而,因为 bar 和 foo 的 vtable 不同 - 指向它的指针也总是不同的。 IClassFactory2 的例子很不幸,因为它是 IClassFactory 的扩展 - 结果指向两个接口的指针在实践中总是相同的
  • 1) 表是对象的一部分还是“全局”是一个实现细节,与本讨论无关。 2) 是的 CF2 是一个扩展,指针很可能是相同的,但如果有人在你只真正实现 CF1 时调用 CF2 方法,它仍然会崩溃。
  • 表是对象的一部分还是“全局”是一个实现细节 是的(尽管怀疑存在此表放置对象的实现)。但这真的不相关,因为接口指向的不是这个表而是指向这个表的指针。说class Demo : IFoo, IBar 接口将是static_cast<IFoo*>(this)static_cast<IBar*>(this) - 总是二进制不同的指针。
  • 我确实尝试在布局中显示带有缩进的 vtbl 指针,但 ASCII 艺术不是我的强项。我们现在绕了一圈吗? OP的问题基本上是指针是否相同。
  • 那么强制转换本质上是重新定位指向对象的指针,以便指向另一个特定的接口?
猜你喜欢
  • 1970-01-01
  • 2012-05-05
  • 2010-10-02
  • 2015-05-21
  • 2019-01-19
  • 1970-01-01
  • 2020-04-07
  • 2018-06-11
  • 1970-01-01
相关资源
最近更新 更多