问题 我何时需要在C#中通过COM查询的接口上调用Marshal.ReleaseComObject


我一直在使用一些DirectShow接口来使用C#和数字电视播放数字电视(DVB-T) DirectShow.Net。我最近遇到了运行时错误 COM object that has been separated from its underlying RCW cannot be used.

此错误发生在以下行:

_guideData = _transportInformationFilter as IGuideData;

_transportInformationFilter 是IBaseFilter类型,以前通过DirectShow.Net实用程序函数分配的COM对象。

我认为错误是由于 _transportInformationFilter 以某种方式提前释放,我跟踪它到以下方法(删除错误处理):

private void AttachGuideDataEvent()
{
    IConnectionPoint connPoint = null;
    IConnectionPointContainer connPointContainer = null;
    try
    {
        connPointContainer = _transportInformationFilter as IConnectionPointContainer;
        if (connPointContainer == null) /* error */

        var guideDataEventGuid = typeof (IGuideDataEvent).GUID;
        connPointContainer.FindConnectionPoint(ref guideDataEventGuid, out connPoint);
        if (connPoint == null) /* error */

        int cookie;
        connPoint.Advise(this, out cookie);
        if (cookie == 0) /* error */    
        _persistIGuideDataEventCookie = cookie;
    }
    finally
    {
        if (connPointContainer != null)
            Marshal.ReleaseComObject(connPointContainer);
        if (connPoint != null)
            Marshal.ReleaseComObject(connPoint);
    }
}

据我所知, connPointContainer = _transportInformationFilter as IConnectionPointContainer 本来应该打电话给 QueryInterface 在...上 _transportInformationFilter COM对象,因此需要单独发布。但是,打电话给 Marshal.ReleaseComObject(connPointContainer) 是造成罪魁祸首的原因 _transportInformationFilter 与RCW分开;删除此调用修复了该问题。

鉴于这种, 在什么情况下我需要显式释放COM对象(使用 Marshal.ReleaseComObject)在C#中避免资源泄漏?


1723
2018-02-23 01:44


起源



答案:


几乎从不。 ReleaseComObject管理RCW的引用计数,而不是底层对象,并且不直接类似于 IUnknown.Release。您应该让CLR管理它 QueryInterface'和 Release“ING。

RCW的引用计数在每次COM接口指针映射到它时递增。 ReleaseComObject方法减少RCW的引用计数。当引用计数达到零时,运行时将释放其对非托管COM对象的所有引用,如果您尝试进一步使用该对象,则抛出System.NullReferenceException。如果从非托管代码到托管代码多次传递相同的COM接口,则包装器上的引用计数每次都会递增,并且调用ReleaseComObject将返回剩余引用的数量。

...

此方法使您可以强制RCW引用计数释放,以便在您需要时准确发生。但是,不恰当地使用ReleaseComObject可能会导致应用程序失败,或者可能导致访问冲突。

http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.releasecomobject.aspx

仅供参考,打电话的方式 IUnknown.Release 直接是 Marshal.Release不是 ReleaseComObject


10
2018-02-23 02:01



谢谢(你的)信息。如果MSDN文档说调用者专门负责释放返回的接口(例如, msdn.microsoft.com/en-us/library/ms692476(VS.85).aspx),当对象超出范围时,运行时是否会为我管理对Release的调用,或者是我应该使用ReleaseComObject的情况? - jeffora
好吧,当对象“超出范围”时它不会运行,而是当RCW的终结器运行时,因此对于connPoint的情况,它不会受到伤害。要记住的重要一点是 ReleaseComObject 在RCW上运行,就像打电话一样 Dispose,它因类似的原因而破产 a = _member as IFoo ; a.Dispose(); _member.DoSomething() 可能会破裂。 - Logan Capaldo
非常感谢您的反馈意见。 - jeffora
@Logan:你有一些“几乎从来没有”的参考资料,似乎我的编码员不能忍受这种不确定性(他是.NET的新手) - Daniel Mošmondor
@Daniel这个答案可以写得更清楚。如果您正在尝试管理COM对象的引用计数(AddRef / Release),则不应该这样做,如果要确定性地释放COM对象,则最有可能使用FinalReleaseComObject。该引用几乎是ReleaseComObject的文档,我与之相关联。 “...因此,只有在绝对需要时才使用ReleaseComObject。” - Logan Capaldo


答案:


几乎从不。 ReleaseComObject管理RCW的引用计数,而不是底层对象,并且不直接类似于 IUnknown.Release。您应该让CLR管理它 QueryInterface'和 Release“ING。

RCW的引用计数在每次COM接口指针映射到它时递增。 ReleaseComObject方法减少RCW的引用计数。当引用计数达到零时,运行时将释放其对非托管COM对象的所有引用,如果您尝试进一步使用该对象,则抛出System.NullReferenceException。如果从非托管代码到托管代码多次传递相同的COM接口,则包装器上的引用计数每次都会递增,并且调用ReleaseComObject将返回剩余引用的数量。

...

此方法使您可以强制RCW引用计数释放,以便在您需要时准确发生。但是,不恰当地使用ReleaseComObject可能会导致应用程序失败,或者可能导致访问冲突。

http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.releasecomobject.aspx

仅供参考,打电话的方式 IUnknown.Release 直接是 Marshal.Release不是 ReleaseComObject


10
2018-02-23 02:01



谢谢(你的)信息。如果MSDN文档说调用者专门负责释放返回的接口(例如, msdn.microsoft.com/en-us/library/ms692476(VS.85).aspx),当对象超出范围时,运行时是否会为我管理对Release的调用,或者是我应该使用ReleaseComObject的情况? - jeffora
好吧,当对象“超出范围”时它不会运行,而是当RCW的终结器运行时,因此对于connPoint的情况,它不会受到伤害。要记住的重要一点是 ReleaseComObject 在RCW上运行,就像打电话一样 Dispose,它因类似的原因而破产 a = _member as IFoo ; a.Dispose(); _member.DoSomething() 可能会破裂。 - Logan Capaldo
非常感谢您的反馈意见。 - jeffora
@Logan:你有一些“几乎从来没有”的参考资料,似乎我的编码员不能忍受这种不确定性(他是.NET的新手) - Daniel Mošmondor
@Daniel这个答案可以写得更清楚。如果您正在尝试管理COM对象的引用计数(AddRef / Release),则不应该这样做,如果要确定性地释放COM对象,则最有可能使用FinalReleaseComObject。该引用几乎是ReleaseComObject的文档,我与之相关联。 “...因此,只有在绝对需要时才使用ReleaseComObject。” - Logan Capaldo


我想我已经找到了使用Marshal.ReleaseComObject的真正合法的情况。当使用ExcelDNA在C#中编写excel addins时,我倾向于使用来自工作线程的COM互操作,并访问excel自动化对象,如“Application”,“Workbook”等。

如果我等待垃圾收集器来完成这些对象,那么在用户退出excel之后,我将拥有一个看不见的excel“zombie”实例。这是因为那些RCW保持活力,他们可以保持运行很长一段时间。


2
2018-05-07 18:28