Excel 2010 com对象引用未被释放。

3
以下代码示例在Excel 2007中完美运行,但当我安装了Excel 2010(32位)时,它会保留excel.exe进程,除非我添加GC.Collect()。我的简单问题是我做错了什么吗?在我看来,我释放了我使用的一切。
    public override void Update()
    {

        StatusBox.AddStatus("Opening File " + ImportPath);

        Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
        Microsoft.Office.Interop.Excel.Workbook wb = app.Workbooks.Open(ImportPath, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
        Microsoft.Office.Interop.Excel.Worksheet ws = (Microsoft.Office.Interop.Excel.Worksheet)wb.Sheets[1];

        Range rng = ws.Cells.SpecialCells(XlCellType.xlCellTypeLastCell, Type.Missing);

        int LastRow = rng.Row;

        StatusBox.AddStatus(LastRow.ToString() + " Rows Found in File");


        StatusBox.AddStatus("Closing File " + ImportPath);

        System.Runtime.InteropServices.Marshal.ReleaseComObject(rng);
        rng = null;

        System.Runtime.InteropServices.Marshal.ReleaseComObject(ws);
        ws = null;

        wb.Close(true, ImportPath, null);
        System.Runtime.InteropServices.Marshal.ReleaseComObject(wb);
        wb = null;

        GC.Collect();

        app.Quit();
        System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
        app = null;
    }

我经常在Excel和MapPoint中使用COM互操作。我认为我从来没有需要使用ReleaseComObject。清除所有空引用并调用相关的close/shutdown方法已经足够了。有人可以确认/详细说明在这些Office类型情况下是否需要ReleaseComObject()吗? - winwaed
第一次编写Excel 2003代码时,我遇到了挂起的Excel引用问题,因此添加了ReleaseComObject。实际上,我已经阅读过var = null;可能是不必要的。 - Matthew Bierman
1个回答

1

你需要同时调用GC.Collect / GC.WaitForPendingFinalizers和Marshall.FinalReleaseComObject。

有关详细信息,请参见我的答案:

如何正确清理Excel互操作对象?

请注意,建议在任何给定命令中“永远不要使用两个点”的建议(似乎是更受欢迎的答案),但实际上几乎不可能强制执行。如果您在代码的任何地方犯了任何错误,Excel应用程序将挂起,并且没有任何分析工具可以帮助您 - 您必须逐个查看所有代码。对于大型代码库,这基本上是不可能的。

在你的代码中,你没有在调用GC.Collect之后调用GC.WaitForPendingFinalizers。这是必要的,以确保你的垃圾回收调用是同步的。(GC.Collect在不同的线程上运行,如果你不等待它,垃圾回收可能会与你后续的对象释放顺序不一致,你想先释放小的COM对象,如Ranges,最后释放大的COM对象,如Workbooks和Application。)在调用GC.Collect和GC.WaitForPendingFinalizers之后,你需要在命名引用上调用Marshall.FinalReleaseComObject。
简而言之,策略是调用GC.Collect和GC.WaitForPendingFinalizers来释放你没有引用的COM对象,并调用Marshall.FinalReleaseComObject来释放你有命名引用的COM对象。
-- Mike

虽然Hans列出了正确的内容,但是我的“ws.Cells.”引用有问题(尽管我不确定为什么在Excel 2007中没有问题)。我将其标记为答案,因为这是一个更好的解释。还有一些情况下,我可能会将我的Excel引用传递给其他库,虽然这些库也在我的控制范围内,但从理论上讲它们可能不受控制。 - Matthew Bierman
很高兴WS.Cells解决了问题,但是确实很奇怪不同版本的Excel在这里的行为不同。甚至可能是每个版本的内存压力不同,因此在一个版本中GC.Collect在正确的时间被隐式调用,而在另一个版本中则没有。然而,你对第三方库的讨论才是真正的问题——其中一些库根本无法正确释放它们的引用,无论你的代码是否与它们交互。在这些情况下,你别无选择,只能在调用Application.Close后使用Process.Kill(或者不使用插件)。 - Mike Rosenblum

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