WinForms 應用程式若發生未捕獲的例外,預設會彈出系統錯誤對話框並終止程式,嚴重影響使用者體驗。本文示範如何透過 Application.ThreadException 與 AppDomain.UnhandledException 兩大事件,實現全域例外攔截、友善提示與日誌記錄。
按鈕點擊直接拋出例外:
private void button1_Click(object sender, EventArgs e)
{
throw new Exception();
}
執行結果將顯示預設的 CLR 未處理例外對話框:

static class Program
{
[STAThread]
static void Main()
{
// 1. 註冊 UI 執行緒例外處理
Application.ThreadException += Application_ThreadException;
// 2. 註冊非 UI 執行緒例外處理
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
HandleException(e.ExceptionObject as Exception, "非 UI 執行緒");
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
HandleException(e.Exception, "UI 執行緒");
}
static void HandleException(Exception ex, string threadType)
{
if (ex == null) return;
MessageBox.Show($"抱歉,操作失敗 ({threadType})。請重試或聯繫技術支援。\n詳細錯誤:{ex.Message}",
"系統錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
LogException(ex);
}
static void LogException(Exception ex)
{
// 實際專案請接入 Log4Net、Serilog 或寫入事件檢視器
System.Diagnostics.Debug.WriteLine($"[{DateTime.Now}] {ex}");
}
}
套用上述程式碼後,同樣的按鈕點擊將彈出自訂的友善錯誤視窗,程式不會強制終止:

ThreadAbortException 應重新拋出)。ex.ToString() 以取得完整堆疊追蹤。Application.SetUnhandledExceptionMode:可強制所有 UI 執行緒例外走 ThreadException 事件,避免某些情況下仍彈出系統對話框。透過雙事件機制,WinForms 即可實現健全的全域例外處理,大幅提升應用程式穩定度與使用者體驗。