当app继续运行时,如何在.NET中清理COM引用?

时间:2011-05-11 00:29:53

标签: c# .net excel com com-interop

我正在开发一个.NET程序,它启动一个新的Excel实例,做一些工作,然后结束,但必须让Excel运行。稍后,当程序再次运行时,它将尝试挂钩到前一个实例。

在这种情况下处理COM对象释放的最佳方法是什么?如果我第一次没有在app对象上执行“ReleaseComObject”,那么在第二次运行时获取活动对象,然后最终释放com对象,我是否有内存泄漏?

以下简化代码说明了我要做的事情:

private Microsoft.Office.Interop.Excel.Application xlsApp;
private Microsoft.Office.Interop.Excel.Workbook xlsWb;

public void RunMeFirst()
{
    //Start a new instance
    System.Type oSEType = Type.GetTypeFromProgID("Excel.Application");
    xlsApp = Activator.CreateInstance(oSEType);
    xlsWb = xlsApp.Workbooks.Open("C:\\test1.xls");

    //Do some stuff

    xlsWb.Close(false);
    Cleanup(ref xlsWb);

    //Do not quit Excel here
    //No Cleanup of xlsApp here?  Is this OK?

    System.Environment.Exit(0);
}

public void RunMeSecond()
{
    //Hook into existing instance
    xlsApp = Marshal.GetActiveObject("Excel.Application");
    xlsWb = xlsApp.Workbooks.Open("C:\\test2.xls");

    //Do some stuff

    xlsWb.Close(false);
    Cleanup(ref xlsWb);

    xlsApp.Quit();
    Cleanup(ref xlsApp);

    System.Environment.Exit(0);
}

public void Cleanup(ref object theObj)
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    GC.WaitForPendingFinalizers();

    Marshal.FinalReleaseComObject(theObj);
    theObj = null;
}

由于

2 个答案:

答案 0 :(得分:4)

通常,在使用Office PIA时,我发现当您拥有包含COM对象的实例变量时,会出现这些对象未被释放的问题。在您的情况下,这些将是xlsAppxlsWb。您不必退出Excel应用程序以释放对象,但执行必须执行以下操作作为清理过程:

Marshal.FinalReleaseComObject(xlsWb);
xlsWb = null;

Marshal.FinalReleaseComObject(xlsApp);
xlsApp = null;

GC.Collect();

包含COM对象的本地范围变量似乎不会导致此问题,只会导致实例变量。我希望这有帮助!

答案 1 :(得分:2)

我有点困惑 - 在RunMeFirst结束时退出流程,RunMeSecond是否在与第一个方法或同一流程不同的流程中运行?

无论哪种方式,我都会更改您的代码,以便xlsWb在本地作用域,并执行以下操作:

public void RunMeFirst()
{
    System.Type oSEType = Type.GetTypeFromProgID("Excel.Application");
    xlsApp = Activator.CreateInstance(oSEType);
    Workbook xlsWb = xlsApp.Workbooks.Open("C:\\test1.xls");

    // Do stuff
    xlsWb.Close(false);

    System.Environment.Exit(0);
}

除非在某些情况下(例如,在许多COM对象都将被使用的服务器应用程序中,因此尽快释放对象至关重要),您不应该真正调用任何ReleaseComObject方法。 。清理COM对象的常用机制应该只是让它们超出范围,在这种情况下,COM对象将使用GC使用的相同魔法释放(我相信它会在终结器运行时被清理,但是我不是百分百肯定的。)

在本地(而不是作为类成员或静态成员)范围内xlsWb的好主意的原因是,只有在清理类时才清除类成员,并且静态成员是在卸载appdomain之前,从不清理。如果确实需要清理静态字段引用的COM对象,那么执行此操作的方法是将静态字段设置为null,以便GC范围规则可以清除基础COM对象:

xlsWb = null;

同样不应该出于类似的原因打电话给GC.Collect - Mike Rosenblum给出了一个公平的解释,说明他为什么打电话给GC.CollectReleaseComObject,但没有真正的理由。为什么他不满足于让垃圾收集器完成它的工作。就像我说的那样,有些情况下你可能想要更多地控制COM对象的释放,但是这些是例外而不是规则。

您可能还会发现阅读Marshal.ReleaseComObject Considered Dangerous很有用。