在.NET 4.0中,“已从其基础RCW分离的COM对象无法使用”。

8

我在我的.NET 3.5 C# WinForms应用程序中有一个类,该类有五个方法。每个方法都使用不同的C++ COM接口集合。 在清理这些COM对象时,我使用Marshal.FinalReleaseCOMObject。这段代码在这个.NET平台上可以正常工作,没有任何问题。 但是当我将这个应用程序移植到.NET 4.0时,在其中一个方法的一行代码处,我从ICOMInterface1转换为ICOMInterface2时,开始出现这个错误:

ICOMInterface1  myVar= obj as ICOMInterface2; 

已从其基础 RCW 中分离的 COM 对象无法使用。

如果我删除使用 Marshal.FinalReleaseCOMObject 的行,我就不会遇到这个错误。

我错过了什么?在 .NET 4.0 平台上如何清理这些非托管的 COM 对象?

1个回答

15
简单的答案是除非你必须这样做,否则永远不要使用Marshal.FinalReleaseComObject。如果你确实需要使用它,那么还有一些额外的规则你必须遵循。
当一个COM对象在.NET中被使用时,运行时会为该对象创建一个称为"RCW"或"runtime callable wrapper"的包装器。这个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撰写了一篇关于[Final]ReleaseComObject及其危害的好blog post。这是一种危险的武器,除非需要,否则只会伤害您。既然您提出了这个问题,我怀疑您根本不需要调用它。 :-)

1
RCW本身具有引用计数,当您调用Marshal.ReleaseComObject时,该引用计数会递减。FinalReleaseComObject只是重复调用此操作,直到引用计数归零。 - Jason Malinowski
1
关于导致这种情况的CLR发生了什么变化...没有调试器很难说。 - Jason Malinowski
Jason,我不太清楚。1、RCW是否会减少包装对象(而非包装器对象)的引用计数直到为0?还是说它会减少包装器(如果有)的引用计数直到为0?2、什么意思?CLR为任何基础COM对象或COM对象(复数形式)创建单个包装器?你的意思是,如果我有一些本地COM对象IBuffer,并在该IBuffer上创建两个包装器,那么我会有……嗯,我的大脑出现了问题,一个包装器和两个基础IBuffers?这没有意义。 - eric frazer
这是我想要的:每次我从C#创建一个COM对象时,我希望它为该COM对象创建一个1-1的RCW。我们将C++对象称为IBuffer,C#对象称为IBuffer'(带撇号)。当我在C#中传递IBuffer'时,我假设它不会添加底层的IBuffer对象的引用计数。当我知道我不再访问底层的IBuffer时,我想调用一个方法(Marshal.XXX),它将释放底层的本机*,但是C#中的所有包装器现在都将持有一个无效或已处理的对象。这可能吗? - eric frazer
https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.releasecomobject.aspx 详细解释了引用计数机制。简单地多次传递对象不会增加底层对象的 AddRef,可以使用 FinalReleaseComObject 来进行精确操作以释放托管引用。假设没有其他不相关的 AddRefs,原生对象将被释放。 - Jason Malinowski
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接