QueryInterface

QueryInterface

QueryInterface是组件对象模型(COM)中基础接口 IUnknown 的一个成员函数,用于在运行时向一个 COM 对象询问它是否支持某个特定接口;若支持,则返回该接口的指针并增加引用计数,若不支持则返回错误码 E_NOINTERFACE。[1] 由于所有 COM 接口都派生自 IUnknown,任何接口指针都可以当作 IUnknown 使用,客户端因此不必预先知道对象支持哪些功能即可进行查询,这一机制是 COM 组件之间实现跨语言、跨进程互操作的基础。[2]

目录
  1. 定义
  2. 原理
  3. 发展历程
  4. 应用
  5. 局限
  6. 参见

定义

QueryInterface是 IUnknown 接口中声明的方法之一,另外两个是用于增减引用计数的 AddRef 与 Release。[3] 它在 C++ 中的形式为 HRESULT QueryInterface(REFIID riid, void **ppvObject),第 1 个参数是所需接口的接口标识符 IID,第 2 个参数用于接收查询到的接口指针。[1]

IUnknown 在 unknwn.h 中定义,3 个方法均声明为纯虚函数,其自身的接口标识符为 {00000000-0000-0000-C000-000000000046},因此不能直接实例化,必须由其他接口继承。[2] 查询成功时方法返回 S_OK,失败时返回 E_NOINTERFACE,并把输出指针置为空;若输出指针本身为空,则返回 E_POINTER。[1]

原理

COM 对象以虚函数表(vtable)对外暴露方法,而所有接口都派生自 IUnknown,故每个接口的 vtable 前 3 项依次是 QueryInterface、AddRef 和 Release,这使得任何接口指针同时也是一个 IUnknown 指针。[2] 客户端只持有一个 IUnknown 指针,就能通过 QueryInterface 逐个取得对象所支持的其他接口,而无需另外维护一个指向组件本身的指针。[4]

典型的实现是在函数内逐项比较传入的 IID:命中某个接口就把 this 指针转换为该接口类型并写入输出参数,随后调用 AddRef 增加引用计数并返回 S_OK;所有分支都不命中时把输出指针置空并返回 E_NOINTERFACE。[4] 不同组件的实现差别仅在于各自需要比对的 IID 清单不同,凡是组件支持的接口都必须在该清单中出现。[4]

由于 C++ 多重继承会改变基类指针的地址,把 this 直接转换为 IUnknown* 可能产生二义性,实现时通常先转换为某个具体接口指针再转换为 IUnknown,并保证每次查询 IID_IUnknown 得到同一地址。[5] 微软给出的实现规则包括:对象必须具有同一性,即用 IID_IUnknown 查询总返回相同的物理指针值;对象实例支持的接口集合必须是静态的,某次查询成功后不得再次失败,反之亦然;并且从任意一个接口出发都应能查询到对象上的其他任意接口,即具备自反性、对称性与可传递性。[6]


sequenceDiagram

participant C as 客户端

participant U as IUnknown

participant O as COM 对象

C->>U: QueryInterface(IID_IX, &pIX)

U->>O: 比对 IID

alt 支持该接口

O-->>C: pIX 指针 + AddRef,返回 S_OK

else 不支持

O-->>C: pIX = NULL,返回 E_NOINTERFACE

end

发展历程

COM 由微软于 1993 年提出,作为 OLE 2.0 的基础,用以解决 OLE 1.0 结构过于复杂的问题。[7] 微软规定每个 COM 组件至少要实现 IUnknown,由此确立了 QueryInterface、AddRef、Release 作为所有组件共同具备的 3 项能力。[7]

1996 年发布的 DCOM 将 COM 扩展到网络环境,接口查询经代理与存根在跨进程、跨机器调用中被转发;代理通常会缓存查询结果,以避免每次查询都产生一次网络往返。[8] 2000 年随 Windows 2000 出现的 COM+ 在 COM 之上整合了事务等服务;此后 .NET 成为微软新的组件技术,通过运行时可调用包装器等机制与既有 COM 组件互操作,COM 及其接口查询机制仍保留在 Windows 中并被广泛使用。[9]

Windows 运行时(WinRT)同样是基于 COM 的 API,属于 COM 的一种增强变体;纳米 COM(Nano-COM)则只保留 COM 的应用程序二进制接口部分,被用作 DirectX、Direct3D 与 DirectML 的基础 ABI 技术,其中 IUnknown 仍是对象引用的基础类型。[9]

应用

在 OLE 自动化中,客户端可先通过 QueryInterface 取得 IDispatch 接口,再借助其 Invoke 方法以名称或方法编号调用对象成员,这是后期绑定调用的常见路径。[10] 组件聚合场景下,被聚合组件需要把 IUnknown 的调用委托给外部组件,使聚合体对客户端只呈现一个 IUnknown 接口,QueryInterface 的委派实现因此成为聚合正确工作的前提。[4]

在 .NET 与 COM 互操作中,ICustomQueryInterface 接口允许开发者提供自定义的托管 QueryInterface 实现,为除 IID_IUnknown 以外的特定接口标识符返回特定接口,该接口已被标记为在后续版本中可能不再提供。[11] 在工业控制领域,IEC 61131-3 的扩展运算符 __QUERYINTERFACE 也以类似方式在运行时把一种接口引用转换为另一种类型,并返回表示转换是否成功的布尔结果。[12]

系统监控类工具同样会利用该机制:由于一个对象可能拥有多个 vtable,可以通过已知接口调用 QueryInterface 定位其他 vtable,但这样做会带来相当可观的处理开销。[10]

局限

QueryInterface 只能回答“是否支持某个接口”,无法表达接口的版本差异或能力强弱;接口一经发布即不应修改,客户端若需新功能只能定义新接口并另行查询。[13] 查询失败时客户端必须自行处理该情形,要么放弃操作,要么退回到功能较弱但可用的接口。[14]

该机制不提供访问控制能力。微软明确指出 QueryInterface 的实现绝不能检查访问控制列表,原因之一是 COM 要求支持某接口的对象在查询时必定返回成功,而且即便检查也无法带来实际安全性,因为能访问该接口的客户端可以直接把指针转交给他人。[1] 此外,接口集合必须是静态的,实现者不能在运行中动态增删对象所支持的接口。[15]

在使用层面,调用 QueryInterface 可能开销较高,代码中常通过缓存已取得的接口指针来减少重复查询,而先调用 QueryInterface 再立即调用 Release 以获取弱引用的做法不被接受,这样得到的指针无效。[16]

参见

  • IUnknown —— QueryInterface 所在的 COM 基础接口,另含 AddRef 与 Release 两个方法。

  • 组件对象模型 —— QueryInterface 所属的二进制组件标准,定义了接口与对象标识规则。

  • IDispatch —— 派生自 IUnknown 的自动化接口,可通过 QueryInterface 取得后用于后期绑定调用。

  • DCOM —— 把 COM 扩展到网络的技术,接口查询需经代理与存根转发。

  • ActiveX —— 基于 COM 的控件技术,其控件同样通过 QueryInterface 暴露接口。

  • 引用计数 —— 与 QueryInterface 配合使用的对象生命周期管理方式。

参考资料

  1. IUnknown::QueryInterface (REFIID,void) [引用日期2026-10-29]
  2. IUnknown [引用日期2026-10-29]
  3. IUnknown [引用日期2026-10-29]
  4. IUnknown 的工作原理 [引用日期2026-10-29]
  5. QueryInterface详解 COM [引用日期2026-10-29]
  6. rules-for-implementing-queryinterface [引用日期2026-10-29]
  7. 组件对象模型_百度百科 [引用日期2026-10-29]
  8. 6zzy7zky(v=vs [引用日期2026-10-29]
  9. Component Object Model [引用日期2026-10-29]
  10. 给定了类 id 和接口 id 之后,则可创建对象实例 [引用日期2026-10-29]
  11. ICustomQueryInterface 介面 (System.Runtime.InteropServices) [引用日期2026-10-29]
  12. Operator '__QUERYINTERFACE' [引用日期2026-10-29]
  13. PerssonE-Shadows(PDF) [引用日期2026-10-29]
  14. QueryInterface 在 物件中巡覽 [引用日期2026-10-29]
  15. title: Rules for Implementing QueryInterface [引用日期2026-10-29]
  16. github.com 上的网页 [引用日期2026-10-29]
词条评价
词条统计

浏览次数:0 次

阅读量:0 次 · 阅读完成量:0 次

最近更新:2026-10-28T18:18:36Z

历史版本

本条目引用的词条
IUnknown 组件对象模型 IDispatch DCOM ActiveX 引用计数