// C++異常處理分析,及格式化異常處理方法<br />//<br />#include <Windows.h><br />#include <stdio.h><br />#include <exception><br />using namespace std;<br />/*對於一個Windows平台上的C++程式來說,異常其實可以分為兩種:<br />Win32異常,也就是結構化異常(Structured Exception)以及C++異常。<br />無論是Win32還是C++都為我們提供了一些機制來處理未被正常捕獲的異常,讓我們在應用程式退出之前盡一些人事。<br />針對C++異常,可以使用set_unexpected,但微軟並不支援它。<br />Win32異常(也可以捕獲C++異常),可以使用SetUnhandledExceptionFilter設定一個SEH的過濾函數。*/<br />//C++標準,異常規格申明(指明函數可以拋出哪些異常):<br />//void fun() throw( A,B,C,D);這表明函數fun()可能並且只可能拋出類型(A,B,C,D)及其子類型的異常<br />//void fun();如果在函數的聲明中沒有包括異常的介面聲明,則此函數可以拋出任何類型的異常<br />//void f4() throw(...)比前面的“拋任何東西”的函數更好,因為它類似“捕獲任何東西”<br />//void fun() thow();不會拋出任何類型異常<br />//對於違反異常規格申明,C++標準規定:<br />//當一個函數試圖拋出沒有列出的異常時,通過unexpected()函數調用了一個異常處理函數。這個異常處理函數的預設實現是調用terminate() 來結束程式<br />//unexpected()函數是標準運行庫在標頭檔<exception>中申明的函數;它不接受參數,也不返回任何東西,實際上unexpected()函數從不返回,就象abort()和exit()一樣。<br />void throw_unexpected_exception() throw()//指定此函數不會拋出任何類型異常<br />//warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow)<br />{<br />printf("throw unexpected exception for test/n");<br />throw 1;//微軟沒有遵循C++標準規定,它忽略了異常規格<br />}<br />void my_unexpected_handler()<br />{<br />printf("in my unexpected handler/n");<br />//可以通過轉換一個unexpected異常為expected異常;維持程式存活<br />//http://www.cnblogs.com/youyou/archive/2005/11/22/282131.html<br />//throw 2; // 重新拋出一個程式可以處理的異常,讓程式重新獲得控制,得以繼續運行<br />terminate();<br />}<br />void termfunction()<br />{<br />printf("in my termfunction/n");<br />terminate();//會再次調用termfunction;程式會陷入遞迴死迴圈<br />}<br />long WINAPI ExceptionFilterProc(EXCEPTION_POINTERS *lParam)<br />{<br />//如果處於調試狀態(F5),系統只會把異常發送給調試器(VS);<br />printf("in my ExceptionFilterProc/n");<br />if(lParam->ExceptionRecord->ExceptionFlags == 0) //可修複異常<br />return -1;<br />else<br />return 1;<br />/*<br />EXCEPTION_EXECUTE_HANDLER = 1<br />已經處理了異常,結束程式,這樣程式將無疾而終。<br />EXCEPTION_CONTINUE_SEARCH = 0<br />不處理異常,轉交系統處理,彈出常見的錯誤訊息框。<br />EXCEPTION_CONTINUE_EXECUTION = -1<br />修複錯誤,從異常發生處繼續執行,最理想的做法,不過非常困難。<br />*/<br />return -1;<br />}<br />long __stdcall __CxxUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *);<br />void HackUnhandledException()<br />{<br />//所有未被捕獲的C++異常最終都會調用__CxxUnhandledExceptionFilter來進行處理<br />//http://blog.116.com.cn/?uid-31476-action-viewspace-itemid-208426<br />//call stack: http://blog.csdn.net/agan4014/archive/2008/03/20/2199790.aspx<br />SetUnhandledExceptionFilter( ExceptionFilterProc );<br />DWORD oldProtect;<br />VirtualProtect( __CxxUnhandledExceptionFilter, 5, PAGE_EXECUTE_READWRITE, &oldProtect );<br />*(char*)__CxxUnhandledExceptionFilter = '/xe9';// far jmp<br />*(unsigned int*)( (char*)__CxxUnhandledExceptionFilter + 1 ) =<br />(unsigned int)ExceptionFilterProc - ( (unsigned int)__CxxUnhandledExceptionFilter + 5 );<br />VirtualProtect( __CxxUnhandledExceptionFilter, 5, oldProtect, &oldProtect );<br />}<br />void DisableSetUnhandledExceptionFilter()<br />{<br />//在設定自己的異常處理函數後,調用DisableSetUnhandledExceptionFilter禁止CRT設定即可。<br />//http://www.cppblog.com/woaidongmao/archive/2009/10/21/99129.html<br />void *addr = (void*)GetProcAddress(LoadLibrary("kernel32.dll"),"SetUnhandledExceptionFilter");<br />if (addr)<br />{<br />unsigned char code[16];<br />int size = 0;<br />//33 C0 xor eax,eax<br />//C2 04 00 ret 4<br />code[size++] = 0x33;<br />code[size++] = 0xC0;<br />code[size++] = 0xC2;<br />code[size++] = 0x04;<br />code[size++] = 0x00;<br />DWORD dwOldFlag, dwTempFlag;<br />VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);<br />WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);<br />VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);<br />}<br />}<br />int main(int argc, char* argv[])<br />{<br />//標準c++規定:你可以通過標準運行庫的函數std::set_unexpected()來掛接自己的處理函數<br />//不過微軟並沒有對它提供支援:http://msdn.microsoft.com/en-us/library/7twc8dwy(VS.80).aspx<br />//unexpected_handler oldHand = set_unexpected(my_unexpected_handler);<br />//unexpected();//calls unexpected directly, which then calls the unexpected_handler.<br />//類似的還有set_terminate,並且微軟對它提供了支援<br />//terminate_handler oldHand = set_terminate(termfunction);<br />//SetUnhandledExceptionFilter(ExceptionFilterProc);<br />//據說VS2005編譯,一些C++異常無法用上面的方法捕獲到,原因是:<br />/*****************************************************/<br />/*Make sure any filter already in place is deleted.<br />/*SetUnhandledExceptionFilter(NULL); //強行刪掉了之前註冊的異常處理函數<br />/*UnhandledExceptionFilter(&ExceptionPointers);<br />/*****************************************************/<br />//可以用一些hack的方法解決這個問題:(參考如下)<br />/*1*///DisableSetUnhandledExceptionFilter();<br />/*2*///HackUnhandledException();<br />throw_unexpected_exception();</p><p>printf("i got end/n");<br />return 0;<br />}<br />//基於全域try/catch的異常處理的缺點:<br />//1.各個線程間的異常是獨立的,一個線程中的try/catch塊並不能夠捕獲到它所建立的子線程中拋出的異常。<br />//2.調試時某個異常被全域的例外處理常式catch到導致失去出錯內容相關的情況。<br />//參考和引用:<br />//C與C++中的異常處理:http://www.cnblogs.com/youyou/archive/2005/11/22/282131.html<br />//c++異常處理機制樣本及講解(作者提供了一個自訂異常類):http://ticktick.blog.51cto.com/823160/191881<br />//VS2005中SetUnhandledExceptionFilter函數應用: http://blog.csdn.net/alicehyxx/archive/2009/07/17/4355802.aspx<br />//程式自動產生Dump檔案: http://blog.csdn.net/alicehyxx/archive/2009/07/15/4351450.aspx<br />//Windows使用者態程式高效排錯 -- 異常(Exception)和通知(Debug Event): http://blog.csdn.net/agan4014/archive/2008/03/20/2199790.aspx<br />//Structured Exception Handling: http://msdn.microsoft.com/en-us/library/ms680657(v=VS.85).aspx<br />//Windows 系統編程初探 (四)結構化異常處理之一: http://blog.csdn.net/xlt123/archive/2005/04/18/352005.aspx<br />//VS2005中SetUnhandledExceptionFilter函數應用: http://www.cppblog.com/woaidongmao/archive/2009/10/21/99129.html<br />//SetUnhandledExceptionFilter: http://blog.116.com.cn/?uid-31476-action-viewspace-itemid-208426