我在C#(ApplicationClass)中使用Excel互操作,并在finally子句中放置了以下代码:

while (System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet) != 0) { }
excelSheet = null;
GC.Collect();
GC.WaitForPendingFinalizers();

尽管这种方法有效,但即使在我关闭Excel之后,Excel.exe进程仍处于后台。它只在我的应用程序被手动关闭后发布。

我做错了什么,或者是否有其他方法可以确保正确处理互操作对象?


当前回答

这似乎太复杂了。根据我的经验,要让Excel正确关闭,只有三件关键的事情:

1:确保没有对您创建的excel应用程序的剩余引用(无论如何,您应该只有一个引用;将其设置为空)

2:调用GC.Collect()

3:必须通过用户手动关闭程序或通过对Excel对象调用“退出”来关闭Excel。(请注意,“退出”的功能与用户试图关闭程序的功能相同,如果存在未保存的更改,即使Excel不可见,也会显示一个确认对话框。用户可以按“取消”,Excel将不会关闭。)

1需要在2之前发生,但3可以随时发生。

实现这一点的一种方法是用自己的类包装interop Excel对象,在构造函数中创建interop实例,并使用Dispose实现IDisposable

if (!mDisposed) {
   mExcel = null;
   GC.Collect();
   mDisposed = true;
}

这将从程序的方面清理出优秀的东西。一旦Excel关闭(由用户手动或您调用退出),该过程将消失。如果程序已经关闭,那么进程将在GC.Collect()调用中消失。

(我不确定它有多重要,但您可能需要在GC.Collect()调用之后调用GC.WaitForPendingFinalizers(),但这并不是完全需要摆脱Excel进程。)

多年来,这对我来说毫无问题。请记住,虽然这是有效的,但实际上您必须优雅地关闭它才能工作。如果在清理excel之前中断程序(通常在调试程序时单击“停止”),则仍会累积excel.exe进程。

其他回答

这对我正在进行的一个项目起到了作用:

excelApp.Quit();
Marshal.ReleaseComObject (excelWB);
Marshal.ReleaseComObject (excelApp);
excelApp = null;

我们了解到,在使用Excel COM对象时,将其每个引用都设置为空非常重要。这包括单元格、工作表和所有内容。

我认为其中一些只是框架处理Office应用程序的方式,但我可能错了。在某些日子,一些应用程序会立即清理进程,而在其他日子,似乎要等到应用程序关闭。总的来说,我不再关注细节,只是确保一天结束时没有任何额外的流程。

还有,也许我过于简化了,但我想你可以。。。

objExcel = new Excel.Application();
objBook = (Excel.Workbook)(objExcel.Workbooks.Add(Type.Missing));
DoSomeStuff(objBook);
SaveTheBook(objBook);
objBook.Close(false, Type.Missing, Type.Missing);
objExcel.Quit();

正如我之前所说,我不太关注Excel进程出现或消失的细节,但这通常对我有用。我也不喜欢在最短的时间内保持Excel进程,但我可能只是在这方面有点偏执。

在我的VSTO插件中新建应用程序对象后,我在关闭PowerPoint时遇到了同样的问题。我在这里尝试了所有的答案,但收效甚微。

这是我为我的案例找到的解决方案-不要使用“新应用程序”,ThisAddIn的AddInBase基类已经有了“应用程序”的句柄。如果你在需要的地方使用这个手柄(如果必须的话,让它保持静态),那么你不必担心清理它,PowerPoint也不会挂在近处。

这看起来确实太复杂了。根据我的经验,要让Excel正确关闭,只有三件关键的事情:

1:确保没有对您创建的excel应用程序的剩余引用(无论如何,您应该只有一个引用;将其设置为空)

2:调用GC.Collect()

3:必须通过用户手动关闭程序或通过对Excel对象调用“退出”来关闭Excel。(请注意,“退出”的功能与用户试图关闭程序的功能相同,如果存在未保存的更改,即使Excel不可见,也会显示一个确认对话框。用户可以按“取消”,Excel将不会关闭。)

1需要在2之前发生,但3可以随时发生。

实现这一点的一种方法是用自己的类包装interop Excel对象,在构造函数中创建interop实例,并使用Dispose实现IDisposable

这将从程序的方面清理出优秀的东西。一旦Excel关闭(由用户手动或您调用退出),该过程将消失。如果程序已经关闭,那么进程将在GC.Collect()调用中消失。

(我不确定它有多重要,但您可能需要在GC.Collect()调用之后调用GC.WaitForPendingFinalizers(),但这并不是完全需要摆脱Excel进程。)

多年来,这对我来说毫无问题。请记住,虽然这是有效的,但实际上您必须优雅地关闭它才能工作。如果在清理excel之前中断程序(通常在调试程序时单击“停止”),则仍会累积excel.exe进程

这是唯一对我有效的方法

        foreach (Process proc in System.Diagnostics.Process.GetProcessesByName("EXCEL"))
        {
            proc.Kill();
        }