实现 QueryInterface 的规则

在 COM 对象上实现 IUnknown::QueryInterface 方法有三个主要规则:

  • 对象必须具有标识。
  • 对象实例上的接口集必须是静态的。
  • 必须能够从任何其他接口成功查询对象上的任何接口。

对象必须具有标识

对于任何给定的对象实例,对具有IID_IUnknown QueryInterface 的调用必须始终返回相同的物理指针值。 这样就可以在任何两个接口上调用 QueryInterface,并比较结果以确定它们是否指向对象同一实例。

对象实例上的接口集必须是静态的

通过 QueryInterface 在对象上可访问的接口集必须是静态的,而不是动态的。 具体而言,如果 QueryInterface 返回给定 IID 的S_OK,则它决不能在同一对象的后续调用中返回E_NOINTERFACE;如果 QueryInterface 返回给定 IID 的E_NOINTERFACE,则对同一对象上的同一 IID 的后续调用绝不必须返回S_OK。

必须能够从任何其他接口成功查询对象上的任何接口

也就是说,给定以下代码:

IA * pA = (some function returning an IA *); 
IB * pB = NULL; 
HRESULT   hr; 
hr = pA->QueryInterface(IID_IB, &pB); 
 

以下规则适用:

  • 如果有指向对象上的接口的指针,则对同一接口 QueryInterface 的调用必须成功:

    pA->QueryInterface(IID_IA, ...) 
    
    
  • 如果对第二个接口指针 QueryInterface 的调用成功,则对第一个接口的指针 QueryInterface 的调用也必须成功。 如果 pB 已成功获取,则还必须成功:

    pB->QueryInterface(IID_IA, ...) 
    
    
  • 任何接口都必须能够查询对象上的任何其他接口。 如果 pB 已成功获取,并且你已成功使用该指针查询第三个接口(IC),则还必须能够使用第一个指针 pA 成功查询 IC。 在这种情况下,以下序列必须成功:

    IC * pC = NULL; 
    hr = pB->QueryInterface(IID_IC, &pC); 
    pA->QueryInterface(IID_IC, ...) 
    
    

接口实现必须维护对给定对象上所有接口的未完成指针引用的计数器。 应为计数器使用 无符号整数

如果客户端需要知道资源已释放,则必须在对象的某些接口中使用具有更高级别的语义的方法,然后才能调用 IUnknown::Release

使用和实现 IUnknown