如何在应用保持运行时清理 .NET 中的 COM 引用

本文关键字:NET 中的 COM 引用 运行时 应用 | 更新日期: 2023-09-27 17:56:08

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

在这种情况下处理 COM 对象释放的最佳方法是什么? 如果我第一次没有对应用程序对象执行"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;
}

谢谢

如何在应用保持运行时清理 .NET 中的 COM 引用

通常,在使用 Office PIA 时,我发现当您具有保存 COM 对象的实例变量时,会出现此类对象未释放的问题。在您的情况下,这些将是xlsAppxlsWb.您不必退出 Excel 应用程序即可释放对象,但您必须执行以下清理过程:

Marshal.FinalReleaseComObject(xlsWb);
xlsWb = null;
Marshal.FinalReleaseComObject(xlsApp);
xlsApp = null;
GC.Collect();

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

我有点困惑 - 在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 使用的相同魔法发布(我相信它会在终结器运行时被清理,但我不是 100% 确定这一点)。

最好在本地(

而不是作为类成员或静态成员)xlsWb范围,因为只有在清理类时才会清理类成员,并且在卸载 appdomain 之前永远不会清理静态成员。 如果确实需要清理静态字段引用的 COM 对象,则执行此操作的方法是将静态字段设置为 null以便可以通过 GC 范围规则清理基础 COM 对象:

xlsWb = null;
此外,出于

类似的原因,也不应该真正需要打电话给GC.Collect - Mike Rosenblum给出了一个公平的解释,为什么他打电话给GC.CollectReleaseComObject,但没有真正的理由来解释为什么他不满足于让垃圾收集器做它的工作。 就像我说的,在某些情况下,您可能希望对 COM 对象的发布进行更多控制,但这些是例外而不是规则。

您可能还会发现阅读 Marshal.ReleaseComObject 被认为是危险的很有用。