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

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

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

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


当前回答

我一直遵循VVS答案中的建议。然而,为了使这个答案与最新的选项保持同步,我认为我未来的所有项目都将使用“NetOffice”库。

NetOffice是Office PIA的完全替代品,完全不受版本限制。它是一个托管COM包装器的集合,可以处理在.NET中使用Microsoft Office时经常引起此类麻烦的清理。

一些关键功能包括:

大部分与版本无关(记录了与版本相关的功能)无相关性无PIA没有注册无VSTO

我与该项目毫无关联;我真的很欣赏头痛的明显减轻。

其他回答

使用Word/Excel互操作应用程序时应该非常小心。在尝试了所有解决方案之后,我们仍然有很多“WinWord”进程在服务器上打开(有2000多个用户)。

在解决这个问题几个小时后,我意识到,如果我在不同的线程上同时使用Word.ApplicationClass.Document.open()打开多个文档,IIS工作进程(w3wp.exe)就会崩溃,所有WinWord进程都会打开!

因此,我想这个问题没有绝对的解决方案,而是改用其他方法,如OfficeOpenXML开发。

在其他答案中考虑的三种一般策略中,杀死excel进程显然是一种黑客行为,而调用垃圾收集器是一种野蛮的猎枪方法,旨在补偿COM对象的错误释放。经过大量的实验,并在我的版本不可知和后期绑定包装器中重写COM对象的管理,我得出结论,准确和及时地调用Marshal.ReleaseComObject()是最有效和优雅的策略。不,您永远不需要FinalReleaseComObject(),因为在一个编写良好的程序中,每个COM只获取一次,因此需要减少一次引用计数器。

应确保释放每个COM对象,最好是在不再需要时释放。但是,在退出Excel应用程序后立即释放所有内容是完全可能的,唯一的代价是更高的内存使用率。只要不松开或忘记释放COM对象,Excel将按预期关闭。

该过程中最简单、最明显的帮助是将每个互操作对象包装到实现IDisposable的.NET类中,其中Dispose()方法在其互操作对象上调用ReleaseComObject()。在析构函数中这样做,正如这里所建议的,没有意义,因为析构函数是非确定性的。

下面显示的是我们的包装器方法,它绕过中间Cells成员从WorkSheet中获取单元格。请注意使用后它处理中间对象的方式:

public ExcelRange XCell( int row, int col)
{   ExcelRange anchor, res;
    using( anchor = Range( "A1") )
    {   res = anchor.Offset( row - 1, col - 1 );  }
    return res;
}

下一步可能是一个简单的内存管理器,它将跟踪获得的每个COM对象,并确保在Excel退出后释放它,如果用户希望用一些RAM来换取更简单的代码。

进一步阅读

如何正确释放Excel COM对象,释放COM对象:垃圾收集器与Marshal.RelseaseComObject。

只是为了给这里列出的众多解决方案添加另一个解决方案,使用C++/ATL自动化(我想您可以使用类似于VB/C#??的东西)

Excel::_ApplicationPtr pXL = ...
  :
SendMessage ( ( HWND ) m_pXL->GetHwnd ( ), WM_DESTROY, 0, 0 ) ;

这对我来说很有魅力。。。

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

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

我认为其中一些只是框架处理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进程,但我可能只是在这方面有点偏执。