1. 程式人生 > >【厚積薄發系列】C++專案總結14—Windows平臺下異常捕獲不到問題分析

【厚積薄發系列】C++專案總結14—Windows平臺下異常捕獲不到問題分析

問題背景:

Windows平臺常用的異常捕獲函式有時候不起作用,特別是XP系統上,有些runtime error捕獲不到,但是其他系統如win7、win8等又能正常捕獲,導致產品發出去後遇到這類異常,莫名其妙的彈一個錯誤框出來導致使用者體驗特別不好。

問題解決:

windows平臺win32提供的常用捕獲異常的介面有下面三種:

_set_invalid_parameter_handler(MyInvalidParameterHandler);

_set_purecall_handler(MyPureCallHandler);

SetUnhandledExceptionFilter(doSomething);

這上面三個win32的介面引數可以設定自己的回撥函式,及遇到異常後開發可以做最後的處理,如生成dump檔案、啟動報警機制等等。上面三個函式的設定後能捕獲系統SEH異常和CRT異常。但是程式在XP系統上或者其他系統上會出現設定了全域性的SetUnhandledExceptionFilter,卻捕獲不到子執行緒或者子模組的CRT異常,還是會彈出系統預設的錯誤框。

之所以還彈出預設的錯誤框,因為設定的全域性捕獲函式沒起作用而是呼叫了系統預設的SetUnhandledExceptionFilter,開發自己設定SetUnhandledExceptionFilter() 時,是將使用者註冊的函式地址存到全域性變數 kernel32!BasepCurrentTopLevelFilter 中,而捕獲不到異常的時候發現自己設定的函式地址和系統觸發的地址不一樣,說明系統呼叫了預設的。為了解決上面註冊了全域性捕獲函式而捕獲不到子模組子執行緒異常的問題,這裡可以採用人工程式碼打補丁的方式來解決,需要自己實現一個打補丁函式,在設定全域性捕獲函式後手動呼叫打補丁函式,例項程式碼如下:

SetUnhandledExceptionFilter(doSomething);

PatchSetUnhandledExceptionFilter()  //打補丁的具體實現如下

void PatchSetUnhandledExceptionFilter()
{
static BYTE RETURN_CODE[] = { 0xc2, 0x04, 0x00}; 
MEMORY_BASIC_INFORMATION   mbi;
DWORD dwOldProtect = 0;
DWORD pfnSetFilter =(DWORD)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "SetUnhandledExceptionFilter");
VirtualQuery((void *)pfnSetFilter, &mbi, sizeof(mbi) );
VirtualProtect( (void *)pfnSetFilter, sizeof(RETURN_CODE), PAGE_READWRITE, &dwOldProtect);
//VirtualProtectEx(GetCurrentProcess(), (void *)pfnSetFilter, sizeof(RETURN_CODE), PAGE_READWRITE, &dwOldProtect);
//如64系統下VirtualProtect後WriteProcessMemory崩潰,則用VirtualProtectEx
WriteProcessMemory(GetCurrentProcess(),(void *)pfnSetFilter,RETURN_CODE,sizeof(RETURN_CODE), NULL);
VirtualProtect((void *)pfnSetFilter, sizeof(RETURN_CODE), mbi.Protect, 0); 
//VirtualProtectEx(GetCurrentProcess(),(void *)pfnSetFilter, sizeof(RETURN_CODE), mbi.Protect, 0); 
FlushInstructionCache(GetCurrentProcess(), (void *)pfnSetFilter, sizeof(RETURN_CODE));

}