【发布时间】:2012-03-31 08:03:47
【问题描述】:
假设我有一个 COM 对象(非托管)和 .NET 客户端。
是否需要从 .NET 客户端调用 Marshal.FinalReleaseComObject 方法才能释放 COM 对象?
【问题讨论】:
标签: .net com com-interop
假设我有一个 COM 对象(非托管)和 .NET 客户端。
是否需要从 .NET 客户端调用 Marshal.FinalReleaseComObject 方法才能释放 COM 对象?
【问题讨论】:
标签: .net com com-interop
没有。无需从 .Net 客户端显式释放 COM 对象。 COM 对象将像任何其他 .Net 对象一样被收集,并且在删除所有对它的引用后将释放它的底层本机句柄。
明确使用FinalReleaseComObject 实际上会导致编程错误。如果代码中的另一个组件仍在引用 COM 对象,您将从它下面拉出本机对象。这可能会导致运行时失败。
【讨论】:
GC.Collect + GC.WaitForPendingFinalizers 不起作用但FinalReleaseComObject 起作用,那么您的FinalReleaseComObject 很可能在程序中的某处孤立了对COM 对象的托管引用。
using 与希望 Stream 终结器能够运行确实没有什么不同。有时它只是在它应该运行的时候却没有运行。再加上缓存(在 OOM 中臭名昭著),这只会“让事情无法工作”。
修改“是”和“否”。
首先,请记住ReleaseComObject 不会自动减少真正的 COM 对象引用计数。相反,它会减少内部 RCW 计数器。 (每次从 COM->NET 移动 same COM 对象时,它都会使用 same RCW 并将 RCW 计数器加一。)同样,FinalRelaseComObject 也会影响RCW 计数器并有效地“将其设置为 0”。当 RCW 计数器变为零时,.NET 将减少 实际 COM ref-count。
所以,“是”,但根据这些规则进行了修改:
ReleaseComObject,但不 FinalReleaseComObject。也就是说,每次对象交叉时都应该调用它一次,即使它导致引用等于 RCW。我说上面的规则,不是FinalReleaseComObject,因为RCW对象被缓存在一个身份代理中,所以使用FinalReleaseComObject会影响COM->NET边界跨越你甚至都不知道! (这很糟糕,在这一点上,我完全同意 JaredPars 的回答。)
而“否”,当一个 RCW 被回收时(终结器被调用),它会自动“释放”COM 引用(有效地调用@987654327 @ 本身)。请记住,由于只有一个 RCW 对象,这意味着只要在 .NET 中有对它的引用,它就永远不会被回收。如果回收 RCW,则 没有问题,因为从上面看,不再有对所述 RCW 的引用,因此 .NET 知道它可以将 COM 引用计数减一(这可能会导致 COM要销毁的对象)。
但是,由于 .NET GC 是挑剔的且非确定性,因此依赖此行为意味着对象的生命周期不受控制。例如,在使用 Outlook 对象模型时,这可能会导致许多微妙的问题。 (其他 COM 服务器可能不太容易出现问题,但 OOM 在内部对对象进行了有趣的“缓存”。如果不明确控制生命周期,很容易得到“项目已被修改”异常。)
在我的代码中,我有一个支持IDisposable 的ComWrapper 类。这使我可以传递从 COM->NET 获得的具有明确生命周期所有权的 RCW 对象。在切换到这种方法后,我在 Outlook-Addin 开发中遇到的问题非常少(实际上几乎没有),并且之前有许多问题。 “缺点”是需要确定 COM->NET 边界,但一旦确定,那么内部所有生命周期都得到正确处理。
编码愉快。
【讨论】: