透過 Microsoft.Office.Interop.Excel (PIA),C# 可完整控制 Excel 應用程式:建立活頁簿、寫入/讀取儲存格、格式化、圖表、列印等。本文涵蓋COM 參考設定、核心物件模型、陣列批量讀寫 (效能關鍵)、WinForms 完整範例、資源釋放最佳實踐與常見坑規避。
using Excel = Microsoft.Office.Interop.Excel;
Application (Excel.exe 進程)
└─ Workbooks 集合
└─ _Workbook (單一 .xls/.xlsx 檔案)
└─ Worksheets/Sheets 集合
└─ _Worksheet (單一工作表)
└─ Range (儲存格區域) → Value2 / Formula / NumberFormat / Interior / Font / Borders
var app = new Excel.Application();
try
{
app.Visible = false; // 背景執行
app.DisplayAlerts = false; // 關閉儲存/覆蓋提示
app.ScreenUpdating = false; // 關閉畫面更新 (大量寫入加速)
var wb = app.Workbooks.Add(); // 新活頁簿
var ws = (Excel._Worksheet)wb.Worksheets[1]; // 索引從 1 開始
// 單儲存格寫入
ws.Cells[1, 1] = "標題";
ws.get_Range("A1").Font.Bold = true;
// 儲存
wb.SaveAs(@"C:\Temp\Report.xlsx", Excel.XlFileFormat.xlOpenXMLWorkbook);
}
finally
{
// 關鍵:正確釋放 COM 物件 (逆序)
Marshal.ReleaseComObject(ws);
wb.Close(false); Marshal.ReleaseComObject(wb);
app.Quit(); Marshal.ReleaseComObject(app);
GC.Collect(); GC.WaitForPendingFinalizers(); // 強制清理 RCW
}
逐儲存格存取極慢 (每次跨進程 COM 呼叫)。陣列一次讀寫 只需 1-2 次 COM 呼叫,效能提升 100-1000 倍。
var data = new object[1000, 5]; // 1000 列 x 5 欄
for (int r = 0; r < 1000; r++)
for (int c = 0; c < 5; c++)
data[r, c] = $"R{r}C{c}";
var range = ws.get_Range("A1", "E1000"); // 目標區域
range.Value2 = data; // 一次寫入!
var range = ws.get_Range("A1", "E1000");
object[,] data = (object[,])range.Value2; // 1-based 索引!
// 遍歷 (注意下限為 1)
for (int r = 1; r <= data.GetUpperBound(0); r++)
for (int c = 1; c <= data.GetUpperBound(1); c++)
Console.Write($"{data[r, c]}\t");
Value2 比 Value 快 (不轉換 Date/Currency 格式)object[row, col],下限為 1 (非 0)= 開頭的字串
using System;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
public partial class ExcelDemoForm : Form
{
private Excel.Application _app;
private Excel._Workbook _wb;
public ExcelDemoForm() => InitializeComponent();
// 按鈕1:建立活頁簿並寫入陣列資料
private void btnCreate_Click(object sender, EventArgs e)
{
try
{
_app = new Excel.Application();
_wb = _app.Workbooks.Add();
var ws = (Excel._Worksheet)_wb.Worksheets[1];
var range = ws.get_Range("A1", "E5");
range = range.get_Resize(5, 5);
if (chkString.Checked)
{
var data = new string[5, 5];
for (int r = 0; r < 5; r++)
for (int c = 0; c < 5; c++)
data[r, c] = $"{r}|{c}";
range.Value2 = data;
}
else
{
var data = new double[5, 5];
for (int r = 0; r < 5; r++)
for (int c = 0; c < 5; c++)
data[r, c] = r * c;
range.Value2 = data;
}
_app.Visible = true;
_app.UserControl = true; // 使用者關閉視窗時自動釋放
}
catch (Exception ex) { MessageBox.Show($"Error: {ex.Message}"); }
}
// 按鈕2:從 Excel 讀取陣列並顯示
private void btnRead_Click(object sender, EventArgs e)
{
try
{
if (_wb == null) { MessageBox.Show("請先按建立"); return; }
var ws = (Excel._Worksheet)_wb.Worksheets[1];
var range = ws.get_Range("A1", "E5");
object[,] data = (object[,])range.Value2;
var sb = new System.Text.StringBuilder("Array Data\n");
for (int r = 1; r <= data.GetUpperBound(0); r++)
{
for (int c = 1; c <= data.GetUpperBound(1); c++)
sb.Append($"{data[r, c]}, ");
sb.AppendLine();
}
MessageBox.Show(sb.ToString(), "Array Values");
}
catch (Exception ex) { MessageBox.Show($"Error: {ex.Message}"); }
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
if (_wb != null) { _wb.Close(false); Marshal.ReleaseComObject(_wb); }
if (_app != null) { _app.Quit(); Marshal.ReleaseComObject(_app); }
GC.Collect(); GC.WaitForPendingFinalizers();
base.OnFormClosing(e);
}
}
| 場景 | 關鍵技術 |
|---|---|
| DataTable/DataSet 導出 | 逐行寫入或 CopyFromRecordset (ADO.NET) |
| 大量資料 (10萬+ 列) | 分批陣列寫入 + ScreenUpdating=false + 關閉自動計算 |
| 樣式/格式/合併儲存格 | Range.NumberFormat, Merge(), Borders, Interior.Color |
| 圖表生成 | Charts.Add(), ChartWizard, SeriesCollection |
| 樞紐分析表 | PivotTableWizard, PivotFields |
| 無 Office 環境 (Server) | EPPlus/ClosedXML/NPOI (純 .NET, 無 COM) |
app.Workbooks.Add() 產生中間 RCW → 必須分層存變數釋放ws.Cells[r, c] (每次產生新 RCW)| 錯誤 | 原因 | 解決 |
|---|---|---|
| 0x800A03EC / 0x800401A8 | Excel 忙碌/對話框開啟/安全性阻擋 | DisplayAlerts=false, 信任中心設定, 重試機制 |
| HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE) | Excel 未安裝/版本不符/64位元不匹配 | 安裝對應版 Office, 專案目標平台設 x64/x86 一致 |
| 儲存失敗/檔案被鎖定 | 前次進程未正確退出 | 工作管理員殺掉 EXCEL.EXE, 加強釋放邏輯 |
| 中文亂碼 | 編碼/字型問題 | 確保 .NET 字串 Unicode, Excel 內部 Unicode, 字型支援中文 |
結語:C# 操作 Excel 核心在於陣列批量讀寫 (Value2) 與嚴格 COM 釋放。掌握這兩點,效能與穩定性即可達生產級。若部署環境無 Office (如 Linux Container、Azure Function),請直接改用 EPPlus/ClosedXML/NPOI 純 .NET 方案,API 相似且無 COM 依賴。