IUnknown :: QueryInterface()是否会增加引用计数?

时间:2011-09-11 04:22:06

标签: com queryinterface iunknown

如果我有IUnknown *ptr,我是否需要通过Release() 以及 获取的每个界面都致电ptr->QueryInterface() >当我完成ptr->Release()时,打电话给ptr

我曾经认为答案是“是”,但this quote from MSDN让我困惑:

  

有时您可能需要获取对象的弱引用(也就是说,您可能希望获得指向其中一个接口的指针而不增加引用计数),但是通过以下方式执行此操作是不可接受的:调用QueryInterface后跟Release

我不明白为什么会有问题 - 如果我调用ptr->QueryInterface()然后在结果指针上调用Release,那么对象上的引用计数是否仍然是正数?这是如何导致指针无效的?

4 个答案:

答案 0 :(得分:7)

文档是正确的。并且您需要遵循引用计数规则 - 除了在创建对象之外,还包括在从Release获取的接口上调用QueryInterface

要清除为什么你不能使用Release执行弱指针 - 在紧随其后的QueryInterface然后Release之间存在竞争条件。

  • Thread1创建对象 - 引用计数1
  • Thread2调用QueryInterface以获取弱引用 - 引用计数2
  • Thread1释放对象 - 引用计数1
  • Thread2调用Release以获取弱引用 - 引用计数0.对象被销毁。
  • Thread2尝试使用object - error。

警告是为了防范上述情况 - 可能是一些程序员认为他们可以“调用ptr->QueryInterface()然后在结果指针上调用Release”然后使用该对象...

答案 1 :(得分:4)

  

IUnknown :: QueryInterface方法

     

检索指向对象上支持的接口的指针。

     

此方法在它返回的指针上调用IUnknown :: AddRef。

直接来自IUnknown :: QueryInterface参考 http://msdn.microsoft.com/en-us/library/ms682521%28v=vs.85%29.aspx

答案 2 :(得分:2)

Theading不是唯一的场景;我甚至说线程实际上根本不是主要场景:这些COM规则可以追溯到Win16,然后首先将抢占式多线程添加到Windows中。

关键问题是,就COM而言,引用计数是每个接口,而不是每个对象。 COM实现可以通过实现每个对象来实际实现引用计数 - 这可能是在C ++中执行它的最简单方法,尤其是当COM对象映射到单个C ++对象时 - 但这只不过是一个实现细节,和COM客户端代码不能依赖于它。

有许多COM对象可以根据需要动态生成接口,然后在不再需要时立即销毁它们。在这些情况下,如果您调用QI来获取其中一个接口,一旦调用Release,该接口的内存可以被废除,因此使用它可能会导致故障/崩溃/等。

一般来说,您必须将 - > Release()的任何调用视为可能释放指针后面的内存。

(另外,请注意,COM实际上并没有弱引用的概念:有计数(强)引用,就是这样。)

答案 3 :(得分:0)

避免弱引用的建议并不能解决种族问题。

T1 operator new, create object, references: 1
T1     passes interface object reference to T2, thinking it can "share" ownership
T1     suspends
T2     resumes
T2 QueryInterface
T2     suspends before InterlockedIncrement, references: 1
T1     resumes
T1 Calls Release
T1     suspends between InterlockedDecrement and operator delete, references: 0
T2     resumes, InterlockedIncrement occurs, references 1
T2     suspends
T1     resumes, operator delete executes, references 1 !!!
T1     suspends
T2     resumes
T2 Any reference to the interface is now invalid since it has been deleted with reference count 1.

这在COM服务器中是可解决的。但是,COM客户端不应该依赖服务器来防止这种竞争条件。因此,COM客户端不得在线程之间共享接口对象。应该允许访问接口对象的唯一线程是当前“拥有”接口对象的ONE线程。

T1不应该调用Release。是的,它可以在将接口对象传递给T2之前调用AddRef。但这可能无法解决竞争,只能将其转移到其他地方。最佳实践是始终保持one-interface-object,one-owner的概念。

如果COM服务器希望支持两个(或更多)接口可以引用某些共享服务器内部状态的概念,则COM服务器应通过提供CreateCopyOfInstance方法并在内部管理争用来通告合同。当然,还有处理这种“扇出”的服务器的例子。看一下Microsoft的持久存储接口。在那里,接口不会“扇出”......每个接口应由单个用户(线程/进程/无论什么)拥有,并且“扇出”由服务器在内部进行管理,并提供给COM客户端控制争用问题的一些方面。因此,Microsoft的COM服务器必须将竞争条件作为与客户签订合同的一部分来解决。

相关问题