如何从C#应用程序中正确清理Excel互操作对象?
这个问题已经被问过很多次了,例如: 但Hans Passent对以下问题的回答让我相信这些问题已经过时和/或根本不正确: 所以,我的问题是: 如何清理Excel互操作对象,以便及时释放所有托管和非托管Excel资源(即当内存压力触发垃圾收集时)如何从C#应用程序中正确清理Excel互操作对象?,c#,excel,office-interop,C#,Excel,Office Interop,这个问题已经被问过很多次了,例如: 但Hans Passent对以下问题的回答让我相信这些问题已经过时和/或根本不正确: 所以,我的问题是: 如何清理Excel互操作对象,以便及时释放所有托管和非托管Excel资源(即当内存压力触发垃圾收集时) 在释放模式下 在调试模式下(如果我们关心的话) 在释放模式下: 仅仅让所有托管Excel互操作对象超出范围就足够了吗 我也需要调用excelApp.Quit()吗 非托管堆上的内存压力会触发垃圾回收吗?i、 e.我是否也需要打电话: GC
- 在释放模式下李>
- 在调试模式下(如果我们关心的话)
GC.Collect();
GC.WaitForPendingFinalizers();
确保我的托管应用不会耗尽内存?
我是否需要调用:System.Runtime.InteropServices.Marshal.FinalEleaseComObject(managedExcelObject)
除非您已经阅读并理解了Hans Passent的答案,否则请不要回答此问题。随着我对C#Excel互操作的使用变得越来越复杂,在关闭应用程序后,我开始在Task Manager中运行“Microsoft Office Excel(32位)”对象的无头副本。我没有发现voodoo Marshal.ReleaseComObject()和GC.Collect()的组合可以完全消除它们。我最终删除了所有的巫毒密码,听从了汉斯·帕森特的建议。当应用程序关闭时,我可以使用以下模式在大多数情况下终止它们:
using System;
using System.IO;
using excel = Microsoft.Office.Interop.Excel;
namespace ExcelInterop {
static class Program {
// Create only one instance of excel.Application(). More instances create more Excel objects in Task Manager.
static excel.Application ExcelApp { get; set; } = new excel.Application();
[STAThread]
static int Main() {
try {
ExcelRunner excelRunner = new ExcelRunner(ExcelApp)
// do your Excel interop activities in your excelRunner class here
// excelRunner MUST be out-of-scope when the finally clause executes
excelRunner = null; // not really necessary but kills the only reference to excelRunner
} catch (Exception e) {
// A catch block is required to ensure that the finally block excutes after an unhandled exception
// see: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-finally
Console.WriteLine($"ExcelRunner terminated with unhandled Exception: '{e.Message}'");
return -1;
} finally {
// this must not execute until all objects derived from 'ExcelApp' are out of scope
if (ExcelApp != null) {
ExcelApp.Quit();
ExcelApp = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
Console.WriteLine("ExcelRunner terminated normally");
return 0;
}
}
}
在我的ExcelRunner课程中,我将数百个csv文件读入excel工作簿,并创建数十个带有表格和图表的.xlsx文件。我只创建了Microsoft.Office.Interop.Excel.Application()的一个实例,并反复使用它。更多实例意味着任务管理器中运行的更多需要清理的“Microsoft Office Excel”对象
请注意,必须执行finally子句,以除去无头Excel对象。上面的模式处理大多数应用程序关闭情况(包括大多数由未处理的异常导致的中止-但请参阅)。当您从VS调试器(Shift-F5或工具栏上的红色“停止调试”方框)中止应用程序时,会出现一个值得注意的异常。如果从调试器中止应用程序,则finally子句不会执行,Excel对象将保持运行状态。这是不幸的,但我找不到解决办法
我使用Excel 2007 interop和Excel 2016 interop在Visual Studio 2019和.NET Framework 4.7.2中对此进行了测试。随着我对C#Excel interop的使用变得越来越复杂,在关闭应用程序后,我开始在Task Manager中运行“Microsoft Office Excel(32位)”对象的无头副本。我没有发现voodoo Marshal.ReleaseComObject()和GC.Collect()的组合可以完全消除它们。我最终删除了所有的巫毒密码,听从了汉斯·帕森特的建议。当应用程序关闭时,我可以使用以下模式在大多数情况下终止它们:
using System;
using System.IO;
using excel = Microsoft.Office.Interop.Excel;
namespace ExcelInterop {
static class Program {
// Create only one instance of excel.Application(). More instances create more Excel objects in Task Manager.
static excel.Application ExcelApp { get; set; } = new excel.Application();
[STAThread]
static int Main() {
try {
ExcelRunner excelRunner = new ExcelRunner(ExcelApp)
// do your Excel interop activities in your excelRunner class here
// excelRunner MUST be out-of-scope when the finally clause executes
excelRunner = null; // not really necessary but kills the only reference to excelRunner
} catch (Exception e) {
// A catch block is required to ensure that the finally block excutes after an unhandled exception
// see: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-finally
Console.WriteLine($"ExcelRunner terminated with unhandled Exception: '{e.Message}'");
return -1;
} finally {
// this must not execute until all objects derived from 'ExcelApp' are out of scope
if (ExcelApp != null) {
ExcelApp.Quit();
ExcelApp = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
Console.WriteLine("ExcelRunner terminated normally");
return 0;
}
}
}
在我的ExcelRunner课程中,我将数百个csv文件读入excel工作簿,并创建数十个带有表格和图表的.xlsx文件。我只创建了Microsoft.Office.Interop.Excel.Application()的一个实例,并反复使用它。更多实例意味着任务管理器中运行的更多需要清理的“Microsoft Office Excel”对象
请注意,必须执行finally子句,以除去无头Excel对象。上面的模式处理大多数应用程序关闭情况(包括大多数由未处理的异常导致的中止-但请参阅)。当您从VS调试器(Shift-F5或工具栏上的红色“停止调试”方框)中止应用程序时,会出现一个值得注意的异常。如果从调试器中止应用程序,则finally子句不会执行,Excel对象将保持运行状态。这是不幸的,但我找不到解决办法
我使用Excel 2007 interop和Excel 2016 interop在Visual Studio 2019和.NET Framework 4.7.2中对此进行了测试。您会发现,所有使用过COM的人都理解这些答案,并且可以告诉您相同的答案。在大多数情况下,解决方案是尽可能不使用Office互操作。使用像Epplus这样的库来创建真正的
xlsx
文件。我同意。如果不需要,请不要使用互操作。使用类似的方法。你是否真的编写了你的程序,并证明了这里突出显示的项目是一个问题?在同一条船上,互操作的东西总是让人头痛不已<如果您只是想将工作表加载到内存中,那么code>ExcelDataReader是另一个值得一看的工具。@Robert Harvey我最初计划用Java编写我的应用程序。这篇文章:让我相信ApachePOI或OpenXML并没有很好地支持图表。因此,我选择了C#和Excel互操作系统,因为我相信它将是完整且易于使用的——事实确实如此。我的应用程序是一次性的,可以为我捕获的csv数据生成一堆(150多个)图表。如果它完成了,我真的不在乎它是否泄漏。这个问题实际上是为了扩大我对这个问题的理解。你会发现,每个曾经与COM合作过的人都理解这些答案,并且可以告诉你同样的答案。在大多数情况下,解决方案是尽可能不使用Office互操作。使用像Epplus这样的库来创建真正的xlsx
文件。我同意。如果不需要,请不要使用互操作。使用类似的方法。你是否真的编写了你的程序,并证明了这里突出显示的项目是一个问题?在同一条船上,互操作的东西总是让人头痛不已<如果您只是想将工作表加载到内存中,那么代码>ExcelDataReader是另一个值得一看的工具。@Robert Harvey我本来打算这样做的