結構化例外處理 (Structured Exception Handling, SEH) 是 Microsoft Visual C++ 提供的錯誤處理機制,與 C++ 標準的 try/catch/throw 不同,SEH 採用 __try/__finally (終止處理) 與 __try/__except (例外處理) 兩組關鍵字,允許將「主要邏輯」與「錯誤處理/資源清理」分離,提升程式碼可讀性與健壯性。
__try {
// Guarded body: 可能提前返回、跳轉、發生硬體例外
}
__finally {
// Termination handler: 保證一定會執行
}
流程:執行 __try 區塊 → 无论正常結束、return、goto、longjmp 或硬體例外,__finally 區塊**必定執行** → 接著執行 __finally 之後的程式碼 (若無 return/goto 中斷)。
DWORD Funcenstein2(void)
{
DWORD dwTemp;
__try {
WaitForSingleObject(g_hSem, INFINITE);
g_dwProtectedData = 5;
dwTemp = g_dwProtectedData;
return dwTemp; // 提前返回
}
__finally {
ReleaseSemaphore(g_hSem, 1, NULL); // 保證釋放
}
// 此處程式碼不會執行
}
若無 __finally,提前 return 會導致訊號量永不釋放,造成死結。
__leave當 __try 區塊透過 return/goto/longjmp 離開時,編譯器會插入額外程式碼確保 __finally 執行,這稱為「局部展開」,會降低效能。使用 __leave 可避免局部展開:
__try {
if (error) __leave; // 直接跳到 __finally,不產生局部展開開銷
// 正常處理
}
__finally {
// 清理
}
ExitThread、ExitProcess、abort 立即終止執行緒/進程。__try {
// 可能發生硬體例外 (除以零、存取違規等)
}
__except (EXCEPTION_EXECUTE_HANDLER) {
// 例外處理器
}
__except 後的運算式為例外篩選器,常用值:
EXCEPTION_EXECUTE_HANDLER (1):進入處理器。EXCEPTION_CONTINUE_SEARCH (0):繼續尋找上層處理器。EXCEPTION_CONTINUE_EXECUTION (-1):修復錯誤後繼續執行觸發指令。BOOL Funcarama1(void)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
LPVOID lpBuf = NULL;
__try {
hFile = CreateFile(_T("SOMEDATA.DAT"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) __leave;
lpBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (!lpBuf) __leave;
DWORD dwRead;
if (!ReadFile(hFile, lpBuf, 1024, &dwRead, NULL) || dwRead == 0) __leave;
// 資料處理...
}
__finally {
if (lpBuf) VirtualFree(lpBuf, 0, MEM_RELEASE);
if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
}
return TRUE;
}
對比傳統巢狀 if 錯誤檢查,SEH 版本邏輯線性、資源釋放集中、不易遺漏。
DWORD FuncaDoodleDoo(void)
{
DWORD dwTemp = 0;
while (dwTemp < 10)
{
__try {
if (dwTemp == 2) continue;
if (dwTemp == 3) break;
}
__finally { dwTemp++; }
dwTemp++;
}
dwTemp += 10;
return dwTemp; // 答案:14
}
SEH 提供了強大的資源管理與錯誤處理能力,特別適合系統層級程式設計。善用 __try/__finally 可確保清理程式碼必定執行;__try/__except 則能捕捉硬體例外。但需注意 ExitProcess 等會繞過 __finally,且濫用局部展開會影響效能,宜配合 __leave 最佳化。