我的.NET 3.5 C#WinForms应用程序中有一个类,它有五种方法。
每种方法都使用不同的C ++ COM接口集。
我使用Marshal.FinalReleaseCOMObject
来清理这些COM对象。此代码在此.NET平台上运行正常,没有任何问题。
但是,当我将此应用程序移动到.NET 4.0时,我开始在其中一个方法中将此错误从ICOMInterface1
转换为ICOMInterface2
,即:
ICOMInterface1 myVar= obj as ICOMInterface2;
已与基础RCW分离的COM对象不能 使用
如果我删除使用Marshal.FinalReleaseCOMObject
的行,我就不会收到此错误。
我在这里缺少什么?如何从.NET 4.0平台上的内存中清除这些非托管COM对象?
答案 0 :(得分:13)
简单的答案是从不使用Marshal.FinalReleaseComObject
,除非你绝对必须。如果你这样做,你必须遵循一些额外的规则。
在.NET中使用COM对象时,运行时会为该对象创建所谓的“RCW”或“运行时可调用包装器”。这个RCW只是一个普通的对象,它在对象上保存一个COM引用。当这个对象被垃圾收集时,它会像你期望的那样在COM对象上调用IUnknown :: Release()。这意味着,除非您的COM对象需要最后一个Release()在非常特定的时刻完成,只需让垃圾收集器处理它。许多COM对象属于这种情况,因此请完全验证您必须仔细管理对Release()的调用。
因此,当您调用FinalReleaseComObject时,这实际上是递减RCW对COM对象的引用,直到它达到零并且RCW然后释放COM对象。此时,这个RCW现在被僵尸了,任何使用它都会给你看到的例外。 CLR(默认情况下)只为任何底层COM对象创建一个RCW,这意味着如果您使用的COM API两次返回相同的对象,它将只有一个RCW。调用FinalReleaseComObject意味着突然所有使用该RCW都是吐司。
保证您拥有唯一Marshal.GetUniqueObjectForIUnknown的唯一方法,它可以阻止任何RCW共享。但正如我之前所说,在大多数COM API中,首先不需要这样做,所以不要这样做。
保罗·哈灵顿(Paul Harrington)写了一篇关于[最终] ReleaseComObject的好blog post,这是邪恶的。它是一种危险的武器,除非需要只会伤害你。既然你问这个问题,我怀疑你实际上根本不需要调用它。 : - )