windows核心程式設計---未處理異常,向量化異常處理與C++異常

raindayinrain發表於2017-12-23

-未處理異常
異常過濾返回EXCEPTION_CONTINUE_SEARCH,向上搜尋,但無法搜尋到處理部分,產生未處理異常。

// 負責處理未處理異常
// 程式初始化時,設定處理未處理異常過濾函式
PTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(
PTOP_LEVEL_EXCEPTION_FILTER pTopLevelExceptionFilter
);

// 返回值和結果
// EXCEPTION_EXECUTE_HANDLER 程式終止
// EXCEPTION_CONTINUE_EXECUTION 從丟擲異常處再執行
// EXCEPTION_CONTINUE_SEARCH 不處理
LONG WINAPI TopLevelUnhandledExceptionFilter(
PEXCEPTION_POINTERS pExceptionInfo
);

// C/C++執行函式安裝的預設的全域性異常過濾程式: __CxxUnhandledExceptionFilter.
// 用NULL呼叫SetUnhandledExceptionFilter將全域性未處理異常過濾程式設回UnhandledExceptionFilter

執行緒相關:

VOID BaseThreadStart(
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam
)
{
__try
{
ExitThread((pfnStartAddr)(pvParam));
}
__except(UnhandledExceptionFilter(GetExceptionInformation()))
{
ExitProcess(GetExceptionCode());
}
}

// 上述UnhandledExceptionFilter的返回值和結果
// EXCEPTION_EXECUTE_HANDLER 執行ExitProcess,程式退出
// EXCEPTION_CONTINUE_EXECUTION 從丟擲異常處再次執行
// EXCEPTION_CONTINUE_SEARCH 未處理異常

-UnhandledExceptionFilter解釋
1.允許對資源進行寫入操作並繼續執行
如執行緒寫操作引起非法訪問異常,UnhandledExceptionFilter會檢視這個執行緒是否正在修改.exe或DLL模組的資源。
如是,UnhandledExceptionFilter呼叫VirtualProtect將資源頁的保護屬性設為PAGE_READWRITE,
並返回EXCEPTION_CONTINUE_EXECUTION,以允許失敗的指令再執行。
2.將未處理異常報告給偵錯程式
如當前程式在偵錯程式控制下,它返回EXCEPTION_CONTINUE_SEARCH。
此時,通知當前程式的偵錯程式。程式碼裡可用IsDebuggerPresent來檢測當前程式是否在偵錯程式控制下。
3.通知我們設定全域性異常過濾函式
呼叫設定的全域性異常過濾程式。
如異常過濾函式返回值為:
EXCEPTION_EXECUTE_HANDLER或EXCEPTION_CONTINUE_EXECUTION,直接返回值給系統。
如返回EXCEPTION_CONTINUE_SEARCH,將未處理異常報告給偵錯程式。

__CxxUnhandledExceptionFilter在呼叫UnhandledExceptionFilter前,會呼叫SetUnhandledExceptionFilter(NULL)

程式用C/C++執行庫時,執行庫會用try/except結構保護執行緒入口點函式,對應的異常過濾程式會呼叫C/C++執行庫的_XcpFilter函式。
_XcpFilter在內部呼叫UnhandledExceptionFilter

-UnhandledExceptionFilter與WER的互動

預設處理下,未處理異常,會使windows彈出對話方塊,讓使用者選擇,除錯或終止程式。
HKEY_CURRENT_USER\Software\Microsoft\Windows\Windows Error Reporting子項下的DontShowUI值為1時,就不會有任何對話方塊彈出,報告會在後臺生成併傳送給Microsoft伺服器。
可改變DefaultConsent來決定錯誤發生時,是否給Microsoft伺服器發報告。

除錯程式時,一般不用等待報告生成和傳送。
執行自動測試案列,一般也不希望WER對話方塊來破壞或終止自動測試。
將登錄檔Reporting子項的ForceQueue設為1,WER將在後臺生成錯誤報告 。自動測試完,可用WER控制檯,列出發生的問題並得到詳細資訊。

核心態執行緒丟擲未處理異常,系統讓相關裝置驅動呼叫CrashDmp.sys在頁檔案中建立Crash Dump,再停止所有操作。
重啟後,系統讓WerFault執行,產生錯誤報告,併傳送給Microsoft伺服器。

-即時除錯

-向量化異常和繼續處理程式
程式可註冊一個函式。
當異常發生或一個未處理異常脫離標準SEH控制時,這函式會被呼叫。

PVOID AddVectoredExceptionHandler(
// 0 傳遞的函式新增到列表尾端
// 非0 傳遞的函式新增到列表頭部
ULONG bFirstInTheList,
// 指向向量化異常處理程式的指標
PVECTORED_EXCEPTION_HANDLER pfnHandler
);

LONG WINAPI ExceptionHandler(
struct _EXCEPTION_POINTERS* pExceptionInfo
);

// pHandler為之前Add...返回值
ULONG RemoveVectoredExceptionHandler(PVOID pHandler);

PVOID AddVectoredContinueHandler(
// 0 傳遞的函式新增到繼續處理函式列表尾端
// 非0 傳遞的函式新增到繼續處理函式列表頭部
ULONG bFirstInTheList,
PVECTORED_EXCEPTION_HANDLER pfnHandler
);

ULONG RemoveVectoredContinueHandler(
PVOID pHandler
);

異常發生時,在執行SEH過濾程式前,將按列表順序逐個呼叫這些函式。
一旦某個函式返回,EXCEPTION_CONTINUE_EXECUTION異常處理便結束,從發生異常處再次執行。
如返回EXCEPTION_CONTINUE_SEARCH,則執行列表下個函式。列表所有函式執行完,最後一個仍返回EXCEPTION_CONTINUE_SEARCH,則執行SEH過濾程式。

在SetUnhandledExceptionFilter安裝的全域性異常處理程式返回EXCEPTION_CONTINUE_SEARCH後,
按列表順序逐個呼叫繼續處理函式列表中函式。如返回EXCEPTION_CONTINUE_SEARCH,則執行列表下個函式。列表所有函式執行完,最後一個仍返回EXCEPTION_CONTINUE_SEARCH,則。。。

-C++異常與結構化異常比較
SEH是作業系統支援的功能。
C++異常處理是C++語言支援的。

如果開發C++應用,應使用C++異常,而非結構化異常。

Microsoft的Visual C++編譯器使用作業系統的結構化異常機制來實現C++異常處理機制.
在建立C++的try塊時,編譯器生成一個SEH的__try塊。
C++的catch語句對應SEH的異常過濾程式。
catch塊中程式碼對應__except塊中程式碼。
編譯器會為C++的throw語句生成對windows RaiseException函式的呼叫。

void ChunkyFunky()
{
try
{
...
throw 5;
}
catch(int x)
{
...
}
...
}


void ChunkyFunky()
{
__try
{
...
RaiseException(
Code = 0xE06D7363,
Flag = EXCEPTION_NONCONTINUABLE,
Args = 5
);
}
__except(
(ArgType == Integer) ?
EXCEPTION_EXECUTE_HANDLER :
EXECEPTION_CONTINUE_SEARCH
)
{
...
}
}

C++異常在內部實際是通過結構化異常來實現的,這使得我們可在一個應用中同時使用兩種機制。
但C++語言不支援可恢復異常處理。對程式碼中不需要可恢復異常處理部分,堅持用C++異常處理,對程式碼中需要可恢復異常處理部分,可編用SEH,讓他返回EXCEPTION_CONTINUE_EXECUTION。

-異常與偵錯程式