許多開發者習慣寫 catch (Exception e) { /* 吞掉所有例外 */ },無論為了不讓使用者看到錯誤、或讓程式繼續跑。但在微軟眼中,這對嚴重例外(如存取違規)是危險的——特別當你的程式作為外掛寄宿在 VS、Office 等進程中時,嚴重例外應讓進程終止,而非繼續執行導致更大災難。.NET 4.0 引入 Corrupted State Exceptions (CSE) 機制,由 CLR 統一攔截處理這類「可能導致進程崩潰」的例外,不再交給使用者程式碼。
AccessViolationException):記憶體損毀、堆疊溢位等,通常意味著進程狀態已不可預測,繼續執行極可能造成資料損毀或連帶崩潰宿主進程(如 Outlook、IE)。.NET 4.0 前,CLR 將所有例外(含 COM/Win32 例外)一律拋給使用者程式碼。許多開發者為避免客訴,濫用 catch (Exception) 吞掉一切,導致嚴重例外被隱藏,最終讓作業系統或宿主應用程式背負「不穩定」罵名。
.NET 4.0 起,CLR 將特定嚴重例外標記為 CSE(如繼承自 SystemException 的 AccessViolationException),並判斷拋出來源:
throw new AccessViolationException())→ CLR 認為開發者知情且可控,正常拋給使用者 catch。unsafe static void T2() {
try {
int* pd = stackalloc int[1];
pd[1911] = 2; // 故意越界寫入
Console.WriteLine(pd[1111]);
} catch (Exception e) {
Console.WriteLine(e); // 不會執行到這裡!
}
}
結果:程式直接終止,CLR 顯示未處理的 AccessViolationException,catch 區塊完全被繞過。
static void T3() {
try {
throw new AccessViolationException(); // 使用者代碼拋出
} catch (Exception e) {
Console.WriteLine(e); // 正常進入 catch,程式繼續執行
}
}
結果:catch 正常捕獲,程式不中斷。
在 app.config 加入:
<configuration>
<runtime>
<legacyCorruptedStateExceptionsPolicy enabled="true" />
</runtime>
</configuration>
或直接在 .NET 4.0 上執行舊版編譯好的組件(CLR 自動啟用舊行為)。
使用 System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptionsAttribute 標記方法:
[HandleProcessCorruptedStateExceptions]
public static int Main() {
try {
CallMainProgramLoop();
} catch (Exception e) { // 此處可捕獲 CSE
Console.WriteLine(e.Message);
return 1; // 記錄後優雅退出
}
return 0;
}
限制:此屬性只能套用在方法上。
微軟引入 CSE 是為了阻止「吞掉嚴重例外」的惡習,強迫開發者區分可復原與不可復原錯誤。雖提供相容管道,但最佳實務仍是:只捕獲明確知道如何處理的具體例外類型,讓嚴重錯誤正確終止進程,避免擴大損害。