如何从C#应用程序中正确清除Excel互操作对象?

问题描述 投票:1回答:1

例如,这个问题已经被问过很多次了:

How do I properly clean up Excel interop objects?

How to dispose Interop Excel Application and workbook correctly?

Why does Microsoft.Office.Interop.Excel.Application.Quit() leave the background process running?

How can I dispose my Excel Application

但是Hans Passent对以下问题的回答使我相信它们已经过时和/或完全不正确:

Clean up Excel Interop Objects with IDisposable

Understanding garbage collection in .NET

所以,我的问题是:如何清除Excel互操作对象,以便及时释放所有托管和非托管Excel资源(即,当内存压力触发垃圾回收时)?

  • 处于发布模式?
  • 在调试模式下(如果需要的话)?

在释放模式下:

仅让所有托管的Excel互操作对象超出范围就足够了吗?

我也需要调用excelApp.Quit()吗?

非托管堆上的内存压力会触发垃圾回收吗?即我是否还需要致电:

GC.Collect();
GC.WaitForPendingFinalizers();

以确保我的托管应用程序不会耗尽内存?我是否需要调用:System.Runtime.InteropServices.Marshal.FinalReleaseComObject(managedExcelObject)?

除非您已阅读并理解Hans Passent的答案,否则请不要回答此问题。

c# excel office-interop
1个回答
0
投票

随着我对C#Excel互操作的使用变得越来越复杂,当我关闭应用程序后,我开始在任务管理器中运行无头'Microsoft Office Excel(32位)'对象的副本。我发现没有伏都教元帅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对象。上面的模式处理大多数应用程序关闭情况(包括由未处理的异常导致的大多数中止-但请参见Does the C# "finally" block ALWAYS execute?)。当您从VS调试器(Shift-F5或工具栏上的红色“停止调试”方块)中止应用程序时,会发生一个值得注意的异常。如果您从调试器中止应用程序,则finally子句会执行not并保留Excel对象。这很不幸,但是我找不到解决方法。

我使用Excel 2007互操作和Excel 2016互操作在Visual Studio 2019和.NET Framework 4.7.2中对此进行了测试。

© www.soinside.com 2019 - 2024. All rights reserved.