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

805

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

while (System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet) != 0) { }
excelSheet = null;
GC.Collect();
GC.WaitForPendingFinalizers();
尽管这样做是有效的,但即使在关闭Excel后,Excel.exe进程仍然在后台运行。只有手动关闭应用程序后,它才会释放。我做错了什么,或者有没有其他方法确保interop对象被正确处理?

3
你是想在不关闭应用程序的情况下关闭Excel.exe吗?我不确定我完全理解你的问题。 - Bryant
4
我正在尝试确保未受控制的互操作对象能够得到正确的处理和释放。这样即使用户已经完成了从应用程序创建的Excel电子表格,也不会出现Excel进程挂起的情况。 - HAdes
4
如果你能尝试通过生成XML Excel文件来完成它,否则请考虑使用VSTO托管/非托管内存管理:http://jake.ginnivan.net/vsto-com-interop - Jeremy Thompson
这个能很好地转换成Excel吗? - Paul C
但是我仍然有两个建议针对上面的代码:1)你应该使用Marshal.FinalReleaseComObject(excelSheet)而不是使用这个while循环2)当“excelSheet”是一个局部变量时,行“excelSheet = null;”是不需要的 - jreichert
2
请参考微软的支持文章,其中他们专门提供了解决此问题的方案:http://support.microsoft.com/kb/317109/ - Arjan
43个回答

0

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

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

在这个过程中最简单、最明显的帮助是将每个互操作对象包装到一个实现了IDisposable接口的.NET类中,其中Dispose()方法在其互操作对象上调用ReleaseComObject()。如此处所提出的,在析构函数中执行此操作是没有意义的,因为析构函数是不确定性的。

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

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退出后释放它们。
更多阅读:
  1. 如何正确释放Excel COM对象,
  2. 释放COM对象:垃圾回收器 vs. Marshal.RelseaseComObject

-1

Excel不是通过C++或C#进行编程的设计。COM API专门设计用于与Visual Basic、VB.NET和VBA一起使用。

此页面上的所有代码示例都不是最佳选择,因为每个调用都必须跨越托管/非托管边界,并进一步忽略了Excel COM API可以自由地失败任何带有加密HRESULT的调用,表明RPC服务器正忙。

我认为自动化Excel的最佳方法是将数据收集到尽可能大的数组中,然后通过Application.Run将其发送到VBA函数或子程序中,然后执行任何所需的处理。此外,在调用Application.Run时,请务必注意异常,指示excel正在忙碌并重试调用Application.Run


4
C#和VB.NET都在CLR下运行,因此不能只为VB.NET设计而不考虑C#。它们本质上是相同的东西,只是用于构建应用程序的语言语义不同。 - Dominic Zukiewicz
1
“Excel不支持通过C++或C#进行编程” - 这是错误的信息。 - Jeremy Thompson
@Jeremy - 这是真的。它主要是设计为通过像VBA和VB这样的后期绑定客户端进行编程的。使用c++或c#来处理Excel是一场噩梦。 - quixver
我们可以有不同的看法,但在我看来,这远非噩梦。使用支持可选参数的C# .Net 4.0实际上与VB.Net几乎相同。根据我的经验,更多的公司正在将旧的VBA代码移植到C#而不是VB.Net。 - Jeremy Thompson
Excel是一个用C++编写的自动化服务器,所以当然最好是用C++来进行自动化。但是通过主要的Microsoft.Office.Interop.Excel程序集,用C#来处理它和用VBA一样简单,详见我的回答。 - undefined
显示剩余3条评论

-1

这是我真正有效的唯一方法

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

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