我在C#(ApplicationClass)中使用Excel互操作,并在finally子句中放置了以下代码:
while (System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet) != 0) { }
excelSheet = null;
GC.Collect();
GC.WaitForPendingFinalizers();
尽管这种方法有效,但即使在我关闭Excel之后,Excel.exe进程仍处于后台。它只在我的应用程序被手动关闭后发布。
我做错了什么,或者是否有其他方法可以确保正确处理互操作对象?
在其他答案中考虑的三种一般策略中,杀死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。
关于释放COM对象的一篇很棒的文章是2.5释放COM对象(MSDN)。
我建议的方法是,如果Excel.Interop引用是非本地变量,则将其置空,然后调用GC.Collect()和GC.WaitForPendingFinalizers()两次。将自动处理本地范围的Interop变量。
这消除了为每个COM对象保留命名引用的需要。
以下是文章中的一个示例:
public class Test {
// These instance variables must be nulled or Excel will not quit
private Excel.Application xl;
private Excel.Workbook book;
public void DoSomething()
{
xl = new Excel.Application();
xl.Visible = true;
book = xl.Workbooks.Add(Type.Missing);
// These variables are locally scoped, so we need not worry about them.
// Notice I don't care about using two dots.
Excel.Range rng = book.Worksheets[1].UsedRange;
}
public void CleanUp()
{
book = null;
xl.Quit();
xl = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
这些话直接来自文章:
在几乎所有的情况下,取消RCW引用并强制垃圾收集将正确清理。如果同时调用GC.WaitForPendingFinalizers,垃圾收集将尽可能具有确定性。也就是说,在第二次调用WaitForPending Finalizers返回时,您将非常确定对象何时被清理。作为替代方案,您可以使用Marshal.ReleaseComObject。但是,请注意,您不太可能需要使用此方法。
我不敢相信这个问题已经困扰了世界五年。。。。如果您已经创建了应用程序,则需要在删除链接之前先关闭它。
objExcel = new Excel.Application();
objBook = (Excel.Workbook)(objExcel.Workbooks.Add(Type.Missing));
关闭时
objBook.Close(true, Type.Missing, Type.Missing);
objExcel.Application.Quit();
objExcel.Quit();
当您新建一个excel应用程序时,它会在后台打开一个excel程序。您需要在释放链接之前命令该excel程序退出,因为该excel程序不是您直接控制的一部分。因此,如果链接被释放,它将保持开放!
每个人都很好编程~~