【问题标题】:How does Running Object Table implement weak references?Running Object Table是如何实现弱引用的?
【发布时间】:2016-11-19 17:09:16
【问题描述】:

当您在运行对象表中注册一个带有零标志的 COM 对象(请求弱引用)时,ROT 将引用计数增加 1。从 ROT 获取对象的行为将引用计数再增加一.一旦该对象被释放,该对象将保持活动状态,并且引用计数至少为 1。它在 ROT 中的注册也不会在检索时被神奇地撤销。

怎么这么弱?这与强注册有何不同?

强注册遵循相同的模式 - 注册和检索都将引用计数增加一。

ROT返回给公寓内客户端的接口指针不是代理; ROT 无法知道我已经释放了检索到的接口指针。

【问题讨论】:

  • 来自IRunningObjectTable::Register: "对于弱注册(ROTFLAGS_REGISTRATIONKEEPSALIVE 未设置),每当对该对象的最后一个强引用被释放时,ROT 都会释放该对象。对于强注册(ROTFLAGS_REGISTRATIONKEEPSALIVE set),ROT 会阻止对象被销毁,直到对象的注册被显式撤销。”
  • 我已经读过了。你能解释一下“对对象的最后一个强引用被释放”是什么意思吗?如果您只是从 ROT 中获取它然后释放,它不会释放该对象。在弱场景中,ROT 持有引用,就像在强场景中一样。
  • 我理解它的方式(我不得不承认我从未做过额外的检查)是强注册请求ROTFLAGS_REGISTRATIONKEEPSALIVE 确保 ROT 无论如何都持有有效的参考。没有标志,它以类似的方式开始(根本没有其他方式,特别是文档指出初始 AddRef 总是发生),但 API 保留随时释放条目的权利,特别是。在特定事件上,例如释放代理。
  • 弱引用是一次性使用的场景。客户端应用程序执行它需要做的任何事情来启动服务器并检索对象引用。完成后,它的最后一个 Release() 调用也会自动将其从 ROT 中删除,不再有任何引用。服务器知道它可以停止运行。 OLE 已经死了,所以不知道这到底有多实用。
  • 在这种情况下'弱引用' == '外部引用' - 来自另一个 com 公寓的对象的引用计数

标签: winapi com win32com running-object-table


【解决方案1】:

真正从 ROT 行为中移除不仅取决于 ROTFLAGS_REGISTRATIONKEEPSALIVE 标志,还取决于(以及如何)您的对象实现 IExternalConnection

仅针对 @IInspectable 的特别说明 - 是的,所有这些都没有记录,不受支持,可以更改 - 所以请不要阅读更多内容)。

当我们在 ROT com 中注册对象时,总是向他查询 IExternalConnection 接口。如果对象没有实现它 - 使用默认实现。

以防ROTFLAGS_REGISTRATIONKEEPSALIVE 已经在注册时IExternalConnection::AddConnection 被调用。所以我们已经有 1 个外部连接。没有ROTFLAGS_REGISTRATIONKEEPSALIVE - 没有调用这个方法。

每当有人打电话给IRunningObjectTable::GetObject!从另一个公寓)我们的过程中调用CRemoteUnknown::RemAddRef。这个方法调用IExternalConnection::AddConnection只有在我们注册没有ROTFLAGS_REGISTRATIONKEEPSALIVE标志。

每次我们结束 Release 对象时(!在代理上,从先前的 GetObject 调用中获得) - CRemoteUnknown::RemReleaseWorker 在我们的本地进程中调用。并且它在内部调用IExternalConnection::ReleaseConnection only 以防 no ROTFLAGS_REGISTRATIONKEEPSALIVE 在对象上。 IExternalConnection 的默认实现称为 CStdMarshal::Disconnect -> InternalIrotRevoke 当外部引用(不是总对象引用)达到 0 和 fLastReleaseCloses == TRUE - 结果我们的对象从 ROT 中撤消。但是如果我们自己实现IExternalConnection,我们可以调用或不调用CoDisconnectObject,这样我们就可以被撤销或不被ROT。

最后,当我们直接或间接调用IRunningObjectTable::Revoke com 调用IExternalConnection::ReleaseConnection 如果 我们注册 ROTFLAGS_REGISTRATIONKEEPSALIVE

所以结论:

如果我们向ROTFLAGS_REGISTRATIONKEEPSALIVE 注册 - IExternalConnection::AddConnection 将在注册时只被调用一次(真的可以称为 n+1 时间和 n 时间 - ReleaseConnection - 但所有这些都在 IRunningObjectTable::Register 调用.)。当有人从 ROT 获取我们的对象时 - 我们将不会收到通知。最后,当我们调用IRunningObjectTable::Revoke 时,IExternalConnection::ReleaseConnection 也只会被调用一次。

另一方面,如果我们不使用 ROTFLAGS_REGISTRATIONKEEPSALIVE 标志 - IExternalConnection 方法将在 RegisterRevoke不调用。但它将在IRunningObjectTable::GetObject 和最终Release多次调用在对象代理上)。如果我们没有自己实现IExternalConnection 或在外部引用达到0 和fLastReleaseCloses 时调用CoDisconnectObject - 我们将从ROT 中删除。但是我们释放不调用CoDisconnectObject(在这种情况下,行为就像我们使用ROTFLAGS_REGISTRATIONKEEPSALIVE)或者说在某些情况下调用它。

优势 - 我们可以跟踪我们的每个对象的使用情况,以防没有 ROTFLAGS_REGISTRATIONKEEPSALIVE 标志,并自行决定是否需要在外部引用达到 0 时断开连接。

最后一个 - 如果我们从调用 IRunningObjectTable::Register 的同一个公寓调用 IRunningObjectTable::GetObject - 我们得到的不是代理,而是直接对象指针。在这种情况下,当然不会调用IExternalConnection 方法

【讨论】:

  • 现在说得通了。我用一个公寓外客户端测试了这个场景,当它断开连接时,对象被释放到零。
  • @SevaAlekseyev - 是的,公寓外客户端(真正的代理)和公寓内客户端之间存在根本区别。但是为了最好地理解情况 - 最好在 self 对象上实现 IExternalConnection(只有 2 个简单的方法),并且在此之后会非常清楚
猜你喜欢
  • 1970-01-01
  • 2010-11-08
  • 2021-08-10
  • 1970-01-01
  • 2013-10-06
  • 1970-01-01
  • 2011-03-11
  • 1970-01-01
  • 2011-12-07
相关资源
最近更新 更多