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

时间:2011-02-23 01:44:26

标签: c# com interop

我一直在使用一些DirectShow界面来使用C#和DirectShow.Net播放数字电视(DVB-T)。我最近遇到了运行时错误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 COM对象上的_transportInformationFilter调用,因此需要单独发布。但是,对Marshal.ReleaseComObject(connPointContainer)的调用是导致_transportInformationFilter与其RCW分离的罪魁祸首;删除此调用解决了问题。

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

2 个答案:

答案 0 :(得分:10)

几乎没有。 ReleaseComObject管理RCW的引用计数,而不是基础对象,并且不直接类似于IUnknown.Release。您应该让CLR管理其QueryInterfaceRelease'。

  

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

答案 1 :(得分:5)

我认为我找到了使用Marshal.ReleaseComObject的真正合法的情况。使用ExcelDNA在C#中编写excel插件时,我倾向于使用来自工作线程的COM互操作,并访问excel自动化对象,如“应用程序”,“工作簿”等。

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