using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Word;
namespace TextReplace
{
public class WordOperator : IDisposable
{
private Application _app;
private Document _doc;
private readonly object _missing = Missing.Value;
///
/// 開啟 Word 文件
///
public void Open(string filePath)
{
_app = new ApplicationClass();
object file = filePath;
_doc = _app.Documents.Open(
ref file, ref _missing, ref _missing, ref _missing,
ref _missing, ref _missing, ref _missing, ref _missing,
ref _missing, ref _missing, ref _missing, ref _missing,
ref _missing, ref _missing, ref _missing, ref _missing);
}
///
/// 全文件文字替換
///
/// 搜尋文字
/// 替換文字
public void ReplaceAll(string findText, string replaceText)
{
_app.Selection.Find.ClearFormatting();
_app.Selection.Find.Replacement.ClearFormatting();
_app.Selection.Find.Text = findText;
_app.Selection.Find.Replacement.Text = replaceText;
object replaceAll = WdReplace.wdReplaceAll;
_app.Selection.Find.Execute(
ref _missing, ref _missing, ref _missing, ref _missing,
ref _missing, ref _missing, ref _missing, ref _missing,
ref _missing, ref _missing, ref replaceAll, ref _missing,
ref _missing, ref _missing, ref _missing);
}
///
/// 儲存文件
///
public void Save()
{
_doc.Save();
}
///
/// 另存新檔
///
public void SaveAs(string filePath)
{
object file = filePath;
_doc.SaveAs2(ref file, ref _missing, ref _missing, ref _missing,
ref _missing, ref _missing, ref _missing, ref _missing,
ref _missing, ref _missing, ref _missing, ref _missing,
ref _missing, ref _missing, ref _missing, ref _missing);
}
///
/// 關閉文件並結束 Word 應用程式
///
public void Dispose()
{
try
{
if (_doc != null)
{
_doc.Close(ref _missing, ref _missing, ref _missing);
Marshal.ReleaseComObject(_doc);
_doc = null;
}
}
finally
{
if (_app != null)
{
_app.Quit(ref _missing, ref _missing, ref _missing);
Marshal.ReleaseComObject(_app);
_app = null;
}
// 強制回收 COM 物件
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
///
/// 緊急終止殘留 Word 進程
///
public static void KillWinwordProcesses()
{
foreach (var p in Process.GetProcessesByName("WINWORD"))
{
try { p.Kill(); } catch { }
}
}
}
}
var replacements = new Dictionary<string, string>
{
{ "[ClientName]", "台積電" },
{ "[Date]", DateTime.Today.ToString("yyyy年MM月dd日") },
{ "[Amount]", "NT$ 1,250,000" }
};
foreach (var file in Directory.GetFiles(@"D:\Templates\", "*.docx"))
{
using (var word = new WordOperator())
{
try
{
word.Open(file);
foreach (var kvp in replacements)
word.ReplaceAll(kvp.Key, kvp.Value);
string output = Path.Combine(@"D:\Output\", Path.GetFileName(file));
word.SaveAs(output);
}
catch (Exception ex)
{
Log.Error($"處理 {file} 失敗: {ex.Message}");
}
}
}
// 防禦性清理
WordOperator.KillWinwordProcesses();
| 議題 | 說明 | 解決方案 |
|---|---|---|
| COM 物件未釋放導致 Word 殘留進程 | Interop 物件需顯式 `Marshal.ReleaseComObject` | `Dispose()` 中釋放 Document/Application,呼叫 `GC.Collect()` |
| 伺服器端自動化不支援 | Microsoft 官方不建議伺服器端自動化 Office | 改用 Open XML SDK、NPOI、Aspose.Words 等無 Office 相依的純 .NET 程式庫 |
| 對話框阻塞(如宏啟用、轉換確認) | 無人值守環境會卡住 | `_app.DisplayAlerts = WdAlertLevel.wdAlertsNone`;設定信任中心關閉宏警告 |
| 權限問題 | IIS 僅有者身分執行,無桌面權限 | 應用程式集區「進階設定」→ 載入使用者設定檔 = True;身分識別使用具權限帳號 |
| 大量檔案處理效能 | 逐個啟動/關閉 Word 極慢 | 單一 Word 實例處理多檔案;或改用無 Office 相依方案 |
Interop 方案適合低頻率、需精確排版、已有 Office 授權的內部工具。生產環境高併發、無人值守、跨平台部署場景,強烈建議改用 Open XML SDK 或 NPOI,避免 COM 互通層帶來的穩定性與授權風險。