簡體   English   中英

如何從 C# 應用程序中正確清理 Excel 互操作對象?

[英]How do I properly clean up Excel interop objects from an C# application?

這個問題已經被問過和回答過很多次了,例如:

如何正確清理 Excel 互操作對象?

如何正確配置 Interop Excel 應用程序和工作簿?

為什么 Microsoft.Office.Interop.Excel.Application.Quit() 讓后台進程運行?

如何處置我的 Excel 應用程序

但是 Hans Passent 對以下問題的回答讓我相信它們已經過時和/或完全不正確:

使用 IDisposable 清理 Excel 互操作對象

了解 .NET 中的垃圾收集

所以,我的問題是:如何清理我的 Excel 互操作對象,以便及時釋放所有托管和非托管 Excel 資源(即,當內存壓力觸發垃圾回收時)?

  • 在發布模式?
  • 在調試模式下(如果我們關心的話)?

在發布模式下:

僅僅讓所有托管 Excel 互操作對象超出范圍就足夠了嗎?

我還需要調用 excelApp.Quit() 嗎?

非托管堆上的內存壓力會觸發垃圾回收嗎? 即我還需要打電話:

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

確保我的托管應用程序不會耗盡內存? 我是否需要調用:System.Runtime.InteropServices.Marshal.FinalReleaseComObject(managedExcelObject)?

請不要回答這個問題,除非您已經閱讀並理解了 Hans Passent 的回答。

隨着我對 C# Excel 互操作的使用變得更加復雜,在我關閉應用程序后,我開始在任務管理器中運行“Microsoft Office Excel(32 位)”對象的無頭副本。 我發現沒有巫毒 Marshal.ReleaseComObject() 和 GC.Collect() 的組合可以完全消除它們。 我最終刪除了所有巫毒教代碼並遵循了 Hans Passent 的建議。 在大多數情況下,當應用程序關閉時,我可以使用以下模式終止它們:

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 對象。 上面的模式處理大多數應用程序關閉情況(包括由未處理的異常引起的大多數中止 - 但請參閱是否 C#“最終”阻止始終執行? )。 當您從 VS 調試器(Shift-F5 或工具欄上的紅色“停止調試”方塊)中止應用程序時,會發生一個值得注意的異常。 如果您在調試器中止程序,最終條文執行和Excel對象是左捉迷藏。 這是不幸的,但我沒有找到解決方法。

我使用 Excel 2007 互操作和 Excel 2016 互操作在 Visual Studio 2019 和 .NET Framework 4.7.2 中對此進行了測試。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM