跳至頁尾內容
USING IRONXL

釋放 Excel 物件(C#)| 使用 IronXL 停止殘留的 Excel 進程

Release Excel Object C#: Stop Lingering Excel Processes with IronXL: Image 1 - Release Excel Object C#: Two methods compared

在C#應用程式中操作Microsoft Excel文件經常導致一個令人沮喪的問題:無法關閉的Excel進程。 您可能會發現多個EXCEL.EXE實例在工作管理員中不斷累積,即使您的程式碼執行完畢後仍占用記憶體。 本指南說明為何會發生這種情況(傳統Excel Interop的情形),並演示IronXL如何完全消除這些COM物件的問題,讓您不再需要複雜的清理程式碼和生產記憶體洩漏。

問題的根本原因在於.NET Framework透過COM引用與Microsoft Office的互動方式。 當您使用Microsoft.Office.Interop.Excel撰寫程式碼時,每個Excel應用程式物件、活頁簿和工作表都會建立需要顯式清理的COM物件。即使少了一次釋放調用或使用雙點模式如app.Workbooks.Open(),也會留下無人為之參考的記憶體,從而阻止Excel進程結束。

現在開始使用IronXL。
green arrow pointer

為何Excel進程在關閉後仍會留在工作管理員?

Excel應用程式不會終止是因為COM物件引用計數未正確遞減。 每次您的程式碼存取Excel應用程式、活頁簿或工作表時,執行時將建立持有對底層COM物件的引用計數的運行可調用包裝函式(RCW)。 .NET垃圾收集器無法釋放這些互操作物件,直到每個引用都被顯式釋放,使用Marshal.ReleaseComObject

導致進程滯留的常見錯誤包括:

  • 在屬性鏈中使用雙點,例如app.Workbooks.Open()(這會建立隱藏的臨時物件)
  • 使用foreach迴圈遍歷COM集合(這會生成未釋放的枚舉器)
  • 忘記在Excel應用程式物件上調用Quit()
  • finally區塊中捕捉異常而未清理

在較早版本的Microsoft Office(2000-2003)中,未能釋放Office API物件將導致主Excel窗口無限期掛起。 雖然較新版本更加寬容,但Excel進程仍會積累在工作管理員中,導致記憶體洩漏和潛在的Excel試算表文件鎖定問題。

在伺服器端或排定的應用程式中,問題變得尤其痛苦,因為進程會隨時間累積,直到伺服器記憶體耗盡。 了解為何發生這種情況是選擇合適工具的第一步。

COM層發生了什麼?

在Excel COM物件上,每次屬性存取都可能建立一個新的RCW。 當您寫Range,另一個是通過雙點存取隱式建立的。 垃圾收集器可能最終會清理這些物件,但對時間沒有保證。 在高吞吐量應用程式中,積壓可能增長得比收集速度更快。

Windows在內部計算COM引用。 直到計數降到零,底層進程(EXCEL.EXE)才會終止。調用GC.WaitForPendingFinalizers()會強制進行收集週期,但這種方法有性能成本且不是生產程式碼的長期解決方案。

雙點模式為什麼會導致問題?

雙點模式(app.Workbooks.Open(path))在C#中很常見,但在COM Interop環境中,它會建立一個未被引用的中間物件。 Open,然後因為您的程式碼不持有對其的引用,它被立即遺棄。 垃圾收集器對這些物件沒有可預測的最終確定時間表,因此EXCEL.EXE保持開著。

與Interop一起使用的唯一安全方法是將每個中間物件儲存在命名變數中,並按建立的反順序顯式釋放每個物件。

您如何正確釋放Excel Interop物件?

傳統的方法需要仔細跟蹤建立的每個COM物件。 您必須避免雙點,將每個中間物件儲存在區域變數中,並按反順序釋放它們。 以下程式碼演示了一個通常在Stack Overflow答案和Microsoft文件中找到的冗長清理模式:

using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;

// Verbose Interop cleanup pattern
Excel.Application excelApp = new Excel.Application();
Excel.Workbooks workbooks = excelApp.Workbooks;
Excel.Workbook workbook = workbooks.Open("report.xlsx");
Excel.Sheets sheets = workbook.Sheets;
Excel.Worksheet worksheet = (Excel.Worksheet)sheets[1];

// Work with data
worksheet.Cells[1, 1] = "Updated Value";

// Cleanup -- release EVERY COM object in reverse order
workbook.Close(false);
excelApp.Quit();

Marshal.ReleaseComObject(worksheet);
Marshal.ReleaseComObject(sheets);
Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(workbooks);
Marshal.ReleaseComObject(excelApp);

GC.Collect();
GC.WaitForPendingFinalizers();
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;

// Verbose Interop cleanup pattern
Excel.Application excelApp = new Excel.Application();
Excel.Workbooks workbooks = excelApp.Workbooks;
Excel.Workbook workbook = workbooks.Open("report.xlsx");
Excel.Sheets sheets = workbook.Sheets;
Excel.Worksheet worksheet = (Excel.Worksheet)sheets[1];

// Work with data
worksheet.Cells[1, 1] = "Updated Value";

// Cleanup -- release EVERY COM object in reverse order
workbook.Close(false);
excelApp.Quit();

Marshal.ReleaseComObject(worksheet);
Marshal.ReleaseComObject(sheets);
Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(workbooks);
Marshal.ReleaseComObject(excelApp);

GC.Collect();
GC.WaitForPendingFinalizers();
Imports Excel = Microsoft.Office.Interop.Excel
Imports System.Runtime.InteropServices

' Verbose Interop cleanup pattern
Dim excelApp As New Excel.Application()
Dim workbooks As Excel.Workbooks = excelApp.Workbooks
Dim workbook As Excel.Workbook = workbooks.Open("report.xlsx")
Dim sheets As Excel.Sheets = workbook.Sheets
Dim worksheet As Excel.Worksheet = CType(sheets(1), Excel.Worksheet)

' Work with data
worksheet.Cells(1, 1) = "Updated Value"

' Cleanup -- release EVERY COM object in reverse order
workbook.Close(False)
excelApp.Quit()

Marshal.ReleaseComObject(worksheet)
Marshal.ReleaseComObject(sheets)
Marshal.ReleaseComObject(workbook)
Marshal.ReleaseComObject(workbooks)
Marshal.ReleaseComObject(excelApp)

GC.Collect()
GC.WaitForPendingFinalizers()
$vbLabelText   $csharpLabel

此程式碼單獨儲存每個Excel物件以確保正確的清理。GC.WaitForPendingFinalizers()調用在末尾強制進行垃圾收集。 一些開發者將所有內容包裹在try/finally塊中,以保證即使發生異常也能清理。

對於標準清理失敗的極端情況,一些團隊採用使用Windows作業物件的進程終止方法。 這涉及對AssignProcessToJobObject的P/Invoke聲明。 雖然這作為最後手段有效,但它是在治標不治本,未解決設計問題。

如何在C#中安裝無COM的Excel程式庫?

IronXL對Excel文件操作採取了根本不同的方法。 IronXL不包裝Microsoft Office COM物件,而是直接使用其自己的解析器和渲染器來讀寫Excel文件格式。 這意味著沒有COM引用,沒有Office Interop依賴性,也沒有遺留進程。 您甚至不需要在機器上安裝Microsoft Excel。

通過Visual Studio中的NuGet套件管理器控制台安裝IronXL:

Install-Package IronXL.Excel

或使用 .NET CLI:

dotnet add package IronXL.Excel

安裝完成後,將using IronXL;指令新增到您的文件。您的專案現在可以直接、原生地存取Excel文件操作,而無需任何Office依賴。 IronXL目標.NET 8、.NET 9、.NET 10、.NET Framework 4.6.2+,並可以在Windows、Linux、macOS、Docker和Azure上運行。

需要哪些NuGet套件?

只需要一個NuGet套件:IronXL.Excel。 它不依賴於Microsoft Office、Office Interop程式集或任何COM註冊。 這使得部署極其簡單——您可以將應用程式發佈為自包含的執行檔,它可以在任何未安裝Office的伺服器上運行。

對於還需要PDF生成的專案,IronPDF與IronXL整合,可直接將Excel資料導出到PDF,而無需通過Excel自動化。

如何在沒有COM物件的情況下讀取Excel文件?

以下程式碼演示了如何使用IronXL讀取Excel文件。 請注意,無需清理程式碼:

using IronXL;

// Load and read Excel files without COM objects
WorkBook workBook = WorkBook.Load("report.xlsx");
WorkSheet workSheet = workBook.DefaultWorkSheet;

// Access cell values directly
string cellValue = workSheet["A1"].StringValue;
decimal columnSum = workSheet["B2:B10"].Sum();

// 否 cleanup required -- workBook is a standard .NET object
Console.WriteLine($"Cell A1: {cellValue}");
Console.WriteLine($"Sum B2:B10: {columnSum}");
using IronXL;

// Load and read Excel files without COM objects
WorkBook workBook = WorkBook.Load("report.xlsx");
WorkSheet workSheet = workBook.DefaultWorkSheet;

// Access cell values directly
string cellValue = workSheet["A1"].StringValue;
decimal columnSum = workSheet["B2:B10"].Sum();

// 否 cleanup required -- workBook is a standard .NET object
Console.WriteLine($"Cell A1: {cellValue}");
Console.WriteLine($"Sum B2:B10: {columnSum}");
Imports IronXL

' Load and read Excel files without COM objects
Dim workBook As WorkBook = WorkBook.Load("report.xlsx")
Dim workSheet As WorkSheet = workBook.DefaultWorkSheet

' Access cell values directly
Dim cellValue As String = workSheet("A1").StringValue
Dim columnSum As Decimal = workSheet("B2:B10").Sum()

' No cleanup required -- workBook is a standard .NET object
Console.WriteLine($"Cell A1: {cellValue}")
Console.WriteLine($"Sum B2:B10: {columnSum}")
$vbLabelText   $csharpLabel

控制台輸出

Release Excel Object C#: Stop Lingering Excel Processes with IronXL: Image 3 - Output IronXL reading an input Excel file

IronXL將Excel物件處理為本地.NET型別。當Marshal.ReleaseComObject或強制垃圾收集週期。

您也可以在IronXL活頁簿中使用using塊進行確定性處置,儘管這不需要以防止進程洩漏——這只是良好的.NET資源管理實踐問題。

如何存取多個工作表?

在IronXL中存取多個工作表就像操作任何.NET集合一樣直接:

using IronXL;

WorkBook workBook = WorkBook.Load("multi-sheet-report.xlsx");

// Iterate all worksheets
foreach (WorkSheet sheet in workBook.WorkSheets)
{
    string sheetName = sheet.Name;
    int rowCount = sheet.RowCount;
    Console.WriteLine($"Sheet '{sheetName}' has {rowCount} rows");
}

// Access a specific sheet by name
WorkSheet salesSheet = workBook["Sales"];
decimal totalRevenue = salesSheet["C2:C100"].Sum();
Console.WriteLine($"Total Revenue: {totalRevenue:C}");
using IronXL;

WorkBook workBook = WorkBook.Load("multi-sheet-report.xlsx");

// Iterate all worksheets
foreach (WorkSheet sheet in workBook.WorkSheets)
{
    string sheetName = sheet.Name;
    int rowCount = sheet.RowCount;
    Console.WriteLine($"Sheet '{sheetName}' has {rowCount} rows");
}

// Access a specific sheet by name
WorkSheet salesSheet = workBook["Sales"];
decimal totalRevenue = salesSheet["C2:C100"].Sum();
Console.WriteLine($"Total Revenue: {totalRevenue:C}");
Imports IronXL

Dim workBook As WorkBook = WorkBook.Load("multi-sheet-report.xlsx")

' Iterate all worksheets
For Each sheet As WorkSheet In workBook.WorkSheets
    Dim sheetName As String = sheet.Name
    Dim rowCount As Integer = sheet.RowCount
    Console.WriteLine($"Sheet '{sheetName}' has {rowCount} rows")
Next

' Access a specific sheet by name
Dim salesSheet As WorkSheet = workBook("Sales")
Dim totalRevenue As Decimal = salesSheet("C2:C100").Sum()
Console.WriteLine($"Total Revenue: {totalRevenue:C}")
$vbLabelText   $csharpLabel

沒有COM迭代枚舉器,沒有隱藏的臨時物件,也不需要清理。 IronXL的API在整個過程中遵循標準.NET集合模式。

如何在沒有Interop的情況下建立新的Excel文件?

建立Excel文件使用相同的簡單方法模式。 以下程式碼展示了如何建立一個新的活頁簿、新增結構化資料、應用基本格式和保存:

using IronXL;

// Create a new Excel spreadsheet in XLSX format
WorkBook workBook = WorkBook.Create(ExcelFileFormat.XLSX);
WorkSheet workSheet = workBook.CreateWorkSheet("Sales Data");

// Write headers
workSheet["A1"].Value = "Product";
workSheet["B1"].Value = "Units Sold";
workSheet["C1"].Value = "Revenue";

// Write data rows
workSheet["A2"].Value = "Widget Alpha";
workSheet["B2"].Value = 450;
workSheet["C2"].Value = 22500;

workSheet["A3"].Value = "Widget Beta";
workSheet["B3"].Value = 310;
workSheet["C3"].Value = 15500;

// Add a formula
workSheet["C4"].Formula = "=SUM(C2:C3)";

// Save the Excel file
workBook.SaveAs("sales_report.xlsx");
Console.WriteLine("Spreadsheet saved successfully.");
using IronXL;

// Create a new Excel spreadsheet in XLSX format
WorkBook workBook = WorkBook.Create(ExcelFileFormat.XLSX);
WorkSheet workSheet = workBook.CreateWorkSheet("Sales Data");

// Write headers
workSheet["A1"].Value = "Product";
workSheet["B1"].Value = "Units Sold";
workSheet["C1"].Value = "Revenue";

// Write data rows
workSheet["A2"].Value = "Widget Alpha";
workSheet["B2"].Value = 450;
workSheet["C2"].Value = 22500;

workSheet["A3"].Value = "Widget Beta";
workSheet["B3"].Value = 310;
workSheet["C3"].Value = 15500;

// Add a formula
workSheet["C4"].Formula = "=SUM(C2:C3)";

// Save the Excel file
workBook.SaveAs("sales_report.xlsx");
Console.WriteLine("Spreadsheet saved successfully.");
Imports IronXL

' Create a new Excel spreadsheet in XLSX format
Dim workBook As WorkBook = WorkBook.Create(ExcelFileFormat.XLSX)
Dim workSheet As WorkSheet = workBook.CreateWorkSheet("Sales Data")

' Write headers
workSheet("A1").Value = "Product"
workSheet("B1").Value = "Units Sold"
workSheet("C1").Value = "Revenue"

' Write data rows
workSheet("A2").Value = "Widget Alpha"
workSheet("B2").Value = 450
workSheet("C2").Value = 22500

workSheet("A3").Value = "Widget Beta"
workSheet("B3").Value = 310
workSheet("C3").Value = 15500

' Add a formula
workSheet("C4").Formula = "=SUM(C2:C3)"

' Save the Excel file
workBook.SaveAs("sales_report.xlsx")
Console.WriteLine("Spreadsheet saved successfully.")
$vbLabelText   $csharpLabel

輸出

Release Excel Object C#: Stop Lingering Excel Processes with IronXL: Image 4 - Excel file created without Interop

這種方法消除了管理Excel應用程式物件的複雜性,處理未保存更改提示,或確保主執行緒使用STA公寓模型。 IronXL簡化了對試算表功能的存取,沒有Office插件依賴性或Interop程式集註冊的負擔。

對於需要更複雜操作的場景,IronXL提供了用於公式評估單元格樣式設定和多張表活頁簿的方法。 您可以編寫程式碼操作Excel工作表資料,應用格式,並導出到多個文件格式——所有這些都無需擔心COM物件生命週期管理。

Excel Interop和直接庫之間的主要區別是什麼?

下表匯總了兩種方法之間最重要的差異:

Excel Interop與IronXL的C#應用程式比較
功能 Excel Interop IronXL
需要Microsoft Office
COM物件清理 手動(每個物件) 自動(.NET GC)
EXCEL.EXE滯留風險 否ne
可在伺服器環境中運行 有限(需要STA執行緒) 是(所有環境)
Docker / Linux支持
支持的文件格式 XLS,XLSX(透過Office) XLS,XLSX,CSV,TSV,JSON
執行緒安全 僅限STA 多執行緒
程式碼複雜性(基本讀取) 高(許多釋放調用) 低(3-5行)

Release Excel Object C#: Stop Lingering Excel Processes with IronXL: Image 2 - Interop vs. IronXL comparison table

IronXL還支持讀取CSV文件將資料導出到CSV使用Excel圖表應用條件格式設定。 這些功能都不依賴於任何Office,因此IronXL適用於桌面和伺服器端應用程式。

有關高級場景的更多詳細資訊,請參見IronXL文件IronXL API參考。 您還可以瀏覽IronXL範例庫,其中包含應對數十種常見Excel自動化任務的現成程式碼範例。

Excel公式和資料驗證呢?

IronXL原生支持讀寫Excel公式。 您可以設置單元格的Formula屬性,IronXL將同時在文件中儲存公式並進行評估。 對於資料驗證規則,IronXL支持下拉列表、數字範圍約束和日期驗證——這一切都不需要Excel進行計算。

該庫還處理Excel密碼保護合併單元格Excel凍結窗格配置。 請參考IronXL教程獲得每個功能的逐步指南。

如何從Excel Interop遷移到原生.NET程式庫?

將現有基於Interop的程式碼庫遷移到IronXL通常涉及四個步驟:

  1. 刪除Microsoft.Office.Interop.Excel NuGet引用和COM註冊
  2. 通過NuGet安裝IronXL.Excel
  3. 使用IronXL等效物件(WorkBook.Create)替換Interop物件建立
  4. 刪除所有GC.Collect模式

大多數屬性名稱對應直觀:workbook.SaveAs(path)幾乎保持不變。 IronXL遷移指南涵蓋了常見的轉換模式。

需要注意的一個方面是多執行緒。 Interop需要STA執行緒,這在ASP.NET中強迫使用特定的執行緒池配置。 切換到IronXL後,您可以刪除任何[STAThread]屬性和執行緒公寓設置——IronXL預設是執行緒安全的。

對於涉及多個文件的大規模遷移,IronXL批量處理範例展示了如何使用平行迴圈和異步模式高效處理數百個Excel文件。

您接下來的步驟是什麼?

Excel Interop中的COM物件清理問題多年來一直讓.NET開發者感到沮喪。 追踪每個中間物件,按正確順序釋放它們,並處理異常而不泄露引用,增加了應當簡單的試算表操作的複雜性。

IronXL提供了一條更清晰的前進道路。 通過直接在Excel文件格式上操作——獨立於Microsoft Office——它從源頭上消除了COM物件的問題。 無論您是閱讀現有文件、建立新文件,還是處理大批量文件,IronXL以現代.NET開發所需的簡單性處理Excel試算表操作。

啟動方法:

關於.NET生態系統中的其他Excel庫的比較,請參見Microsoft文件上的Excel InteropEPPlus庫的GitHub儲存庫ClosedXML項目瞭解不同選項的權衡。 IronXL的優勢在於其豐富的API、無需安裝Office的部署以及在單一商業授權下的全面跨平台支持。

常見問題

什麼是IronXL?

IronXL是.NET程式庫,簡化了C#應用程式中Excel文件的操作,無需Microsoft Office Interop。

為什麼我需要在C#中釋放Excel物件?

在C#中釋放Excel物件對防止Excel進程滯留至關重要,這可能會導致性能問題和記憶體洩漏。

IronXL如何協助Excel物件清理?

IronXL自動化Excel物件清理,減少了手動Interop物件管理在C#中帶來的複雜性和錯誤。

在C#中使用Excel Interop的挑戰有哪些?

C#中的Excel Interop經常導致問題,如進程滯留和記憶體洩漏,這是由於手動管理COM物件所致。

沒有安裝Microsoft Office的情況下,我可以操作Excel文件嗎?

是的,IronXL允許您在系統上不需安裝Microsoft Office即可操作Excel文件。

IronXL是否支持所有Excel文件格式?

IronXL支持包括XLSX、XLS、CSV等在內的多種Excel文件格式,提供多樣化的文件操作。

IronXL適合於大規模Excel文件操作嗎?

IronXL旨在高效處理大規模Excel文件操作,適用於企業級應用。

使用IronXL比使用Excel Interop有哪些優勢?

IronXL提供的益處包括簡化程式碼、更好的性能以及消除與Excel Interop相關的COM問題。

我如何將IronXL整合到我的C#專案中?

您可以透過NuGet包管理器安裝IronXL,並在應用程式程式碼中引用它來整合到您的C#專案中。

IronXL是否提供對Excel公式的支持?

是的,IronXL支持Excel公式,允許您在C#應用程式中讀取、寫入和評估它們。

Curtis Chau
技術作家

Curtis Chau擁有Carleton大學的電腦科學學士學位,專精於前端開發,擁有Node.js、TypeScript、JavaScript和React的專業知識。Curtis熱衷於建立直觀且美觀的使用者介面,喜愛使用現代框架並建立結構良好、視覺吸引力的手冊。

除了開發,Curtis對物聯網(IoT)有濃厚的興趣,探索創新的方法來整合硬體和軟體。在空閒時間,他喜歡玩遊戲和建立Discord機器人,結合他對技術的熱愛與創造力。

Iron 支援團隊

我們線上24小時,每週5天。
聊天
電子郵件
給我打電話