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

发布于 2024-12-03 20:35:26 字数 640 浏览 3 评论 0原文

如果我有一个 IUnknown *ptr,我是否需要在通过 ptr->QueryInterface() 获取的每个接口上调用 Release()此外当我完成ptr时调用ptr->Release()

我曾经认为答案是“是”,但是这段来自MSDN的引用 让我困惑:

有时候你可能需要获取一个对象的弱引用(也就是说,你可能希望获取一个指向其接口之一的指针而不增加引用计数),但是通过以下方式做到这一点是不可接受的调用QueryInterface,然后调用Release

我不明白为什么这是有问题的 - 如果我调用 ptr->QueryInterface() 然后对结果指针调用 Release,引用计数不应该对象仍然是积极的吗?这如何导致无效指针?

If I have an IUnknown *ptr, do I need to call Release() on every interface I obtain through ptr->QueryInterface(), in addition to calling ptr->Release() when I'm done with ptr?

I used to think that the answer is "Yes", but this quote from MSDN confused me:

Occasionally you may need to obtain a weak reference to an object (that is, you may wish to obtain a pointer to one of its interfaces without incrementing the reference count), but it is not acceptable to do this by calling QueryInterface followed by Release.

I don't understand why that's problematic -- if I call ptr->QueryInterface() and then call Release on the resulting pointer, shouldn't the reference count on the object still be positive? How does that result in an invalid pointer?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

荭秂 2024-12-10 20:35:26

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

为了弄清楚为什么不能使用 Release 执行弱指针 - 在调用 QueryInterface 和之后立即调用 Release 时存在竞争条件。

  • 线程 1 创建对象 - 引用计数 1
  • 线程 2 调用 QueryInterface 进行弱引用 - 引用计数 2
  • 线程 1 释放对象 - 引用计数 1
  • 线程 2 调用 Release 进行弱引用 - 引用计数 0。被摧毁了。
  • Thread2 尝试使用对象 - 错误。

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

The documentation is correct. And you need to follow reference counting rules - that includes calling Release on interfaces obtained from QueryInterface in addition to after you created the object.

To clear up why you can't do weak pointers with Release - there exists a race condition in calling QueryInterface and then Release immediately after.

  • Thread1 creates object - reference count 1
  • Thread2 calls QueryInterface for weak reference - reference count 2
  • Thread1 releases object - reference count 1
  • Thread2 calls Release for weak reference - reference count 0. Object is destroyed.
  • Thread2 tries to use object - error.

The warning is there to guard against the above - presumably some programmers think that they can "call ptr->QueryInterface() and then call Release on the resulting pointer" and then use the object...

花开浅夏 2024-12-10 20:35:26

IUnknown::QueryInterface方法

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

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

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

IUnknown::QueryInterface Method

Retrieves pointers to the supported interfaces on an object.

This method calls IUnknown::AddRef on the pointer it returns.

Straight from IUnknown::QueryInterface reference at
http://msdn.microsoft.com/en-us/library/ms682521%28v=vs.85%29.aspx

神爱温柔 2024-12-10 20:35:26

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

关键问题是,就 COM 而言,引用计数是针对每个接口的,而不是针对每个对象的。 COM 实现可以通过按对象实现引用计数来实际实现引用计数 - 这可能是在 C++ 中执行此操作的最简单方法,特别是当 COM 对象映射到单个 C++ 对象时 - 但这只不过是实现细节, COM 客户端代码不能依赖这种情况。

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

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

(另外,请注意,COM 实际上并没有弱引用的概念:有计数的(强)引用,仅此而已。)

Theading is not the only scenario; I'd go so far as to say that threading is not actually the primary scenario at all: these COM rules date back to Win16 before preemptive multithreading was added to Windows in the first place.

The key issue is that as far as COM is concerned, reference counts are per-interface, not per-object. A COM implementation is free to actually implement a reference count by implementing it per-object - that's perhaps the simplest way of doing it in C++, especially when a COM object maps to a single C++ object - but that's nothing more than an implementation detail, and COM client code cannot rely on it being the case.

There are many COM objects out there that may generate interfaces on the fly as required, and then destroy them as soon as they are no longer needed. In those cases, if you call QI to get one of these interfaces, once you call Release, the memory for that interface can be dealloated, so using it could lead to a fault/crash/etc.

Generally speaking, you have to regard any call to ->Release() as potentially deallocating the memory behind the pointer.

(Also, note that COM does not really have a concept of weak references to begin with: there's counted (strong) references, and that's it.)

谁对谁错谁最难过 2024-12-10 20:35:26

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

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 客户端不得在线程之间共享接口对象。应该允许访问接口对象的唯一线程是当前“拥有”接口对象的一个​​线程。

T1 不应该调用 Release。是的,它可以在将接口对象传递给 T2 之前调用 AddRef。但这可能无法解决比赛问题,只能将其转移到其他地方。最佳实践是始终保持一接口对象、一所有者的概念。

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

The suggestion to avoid weak references does not solve the race issue.

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.

This is solvable in the COM server. The COM client, however, should not depend upon the server preventing this race condition. Therefore, COM clients MUST NOT share interface objects between threads. The only thread which should be allowed to access the interface object, is the ONE thread which currently "owns" the interface object.

T1 should NOT have called Release. Yes, it could have called AddRef prior to passing the interface object to T2. But that may not solve the race, only move it someplace else. The best practice is to always maintain the concept of one-interface-object, one-owner.

If the COM server wishes to support the concept that two (or more) interfaces can reference some shared server-internal state, the COM server should advertise the contract by supplying a CreateCopyOfInstance method and manage contention, internally. There are, of course, examples of servers which handle this sort of "fan-out". Take a look at the persistent storage interfaces from Microsoft. There, the interfaces do NOT "fan-out" .. each interface should be owned by a single user (thread/process/whatever) and the "fan-out" is managed, internally, by the server, with methods provided to the COM clients to control some facets of the contention issues. Microsoft's COM servers must, therefore, address the race conditions as part of their contracts to their clients.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文