轉載請註明出處
作者:小馬
上一篇文章說過,要寫一個關於SEH的博文,實現seh與c++ try/catch的整合或混合使用.
因為通過vc或vs設定的方法來處理有種種的缺點(可參見上篇文章).
以下所有代碼均在vs2005 下測試.
明確一點, seh是windows特有的, 所以它不具移植性.
windows SEH捕捉代碼塊為 __try/ __except 塊, 下面這段代碼展示如何使用它:
__try{ int *p = NULL; *p = 10;}__except(EXCEPTION_EXECUTE_HANDLER){ cout << "seh exception happened" << endl;}
在__try塊裡,給一個非法的記憶體位址賦值, 這是一個典型的seh異常,所以程式的結果會輸出:
seh exception happened
__except的參數比較有意思, 因為它可以是一個運算式, 這樣處理帶來的是靈活性. 當__try塊裡有異常出現時, 它會執行__except裡的運算式(當有異常出現時, __except塊的語句可能不會被執行, 但運算式一定會被執行), 這個運算式執行的結果決定了異常如何被處理.運算式執行的結果一般有三個,msdn裡如是說:
EXCEPTION_CONTINUE_EXECUTION (–1) Exception is dismissed. Continue execution at the point where the exception occurred. EXCEPTION_CONTINUE_SEARCH (0) Exception is not recognized. Continue to search up the stack for a handler, first for containing try-except statements, then for handlers with the next highest precedence. EXCEPTION_EXECUTE_HANDLER (1) Exception is recognized. Transfer control to the exception handler by executing the __except compound statement, then continue execution after the __except block.
意思很明顯, 只有__except的運算式的結果是EXCEPTION_EXECUTE_HANDLER時, 才會執行__except裡的語句(其它兩個不解釋了).
EXCEPTION_EXECUTE_HANDLER表示異常可以被識別, 所以交給__except去處理. 像上面的程式碼片段, 直接讓__except的參數為EXCEPTION_EXECUTE_HANDLER, 所以"seh exception happened"會列印出來.
實際項目的應用稍微複雜一些, 往往不是這樣直接給__except的參數賦值, 想一下, 如果一段代碼出現了異常, 我們肯定希望知道哪種異常發生了, 也就是要識別這種異常, 我們需要寫一個過濾函數(因為__except的參數是運算式, 所以可以是函數調用), 過濾出我們可識別的異常,讓這些異常成為EXCEPTION_EXECUTE_HANDLER, 這樣才能在__except裡去處理. 代碼如下:
int FilterFunc(DWORD exCode){ if (exCode == EXCEPTION_ACCESS_VIOLATION)//非法訪問 { cout << "EXCEPTION_ACCESS_VIOLATION" << endl; return EXCEPTION_EXECUTE_HANDLER; } else if (exCode == EXCEPTION_STACK_OVERFLOW)//堆疊溢位 { cout << "EXCEPTION_STACK_OVERFLOW" << endl; return EXCEPTION_EXECUTE_HANDLER; } else if (exCode == EXCEPTION_INT_DIVIDE_BY_ZERO)//除數為0 { cout << "EXCEPTION_INT_DIVIDE_BY_ZERO" << endl; return EXCEPTION_EXECUTE_HANDLER; } else { cout << "unknown exception" << endl; return EXCEPTION_CONTINUE_SEARCH; }}
然後可以這樣用:
__try { int *p = NULL; *p = 10; } __except(FilterFunc(GetExceptionCode())) { cout << "seh exception happened" << endl; }
輸出:
EXCEPTION_ACCESS_VIOLATION
seh exception happened
對於GetExceptionCode函數,只需要知道它是win32定義的一個宏,可以返回異常代碼就行了.
我們還可以把FilterFunc完善一些, 更詳細的跟蹤異常, 代碼如下:
int FilterFunc(DWORD exCode, char *fileName, int nLine, char *time){ if (exCode == EXCEPTION_ACCESS_VIOLATION)//非法訪問 { cout << "EXCEPTION_ACCESS_VIOLATION" << endl; cout << "at " << fileName << ",line: " << nLine << ",time: " << time <<endl; return EXCEPTION_EXECUTE_HANDLER; } else if (exCode == EXCEPTION_STACK_OVERFLOW)//堆疊溢位 { cout << "EXCEPTION_STACK_OVERFLOW" << endl; cout << "at " << fileName << ",line: " << nLine << ",time: " << time <<endl; return EXCEPTION_EXECUTE_HANDLER; } else if (exCode == EXCEPTION_INT_DIVIDE_BY_ZERO)//除數為0 { cout << "EXCEPTION_INT_DIVIDE_BY_ZERO" << endl; cout << "at " << fileName << ",line: " << nLine << ",time: " << time <<endl; return EXCEPTION_EXECUTE_HANDLER; } else { cout << "unknown exception" << endl; cout << "at " << fileName << ",line: " << nLine << ",time: " << time <<endl; return EXCEPTION_CONTINUE_SEARCH; }}
然後這樣調用:
__try{ int *p = NULL; *p = 10;}__except(FilterFunc(GetExceptionCode(), __FILE__, __LINE__, __TIME__)){ cout << "seh exception happened" << endl;}
結果輸出:
EXCEPTION_ACCESS_VIOLATION
at d:\study\test\exceptiontest\exceptiontest\exceptiontest.cpp,line: 36,time: 14
:34:50
seh exception happened
好了,以後我們的代碼中,就可以用try/catch捕獲throw的異常, 用__try/__except捕獲seh異常. 各司其職.
最後有一句話要說, 微軟希望我們在c裡才有__try/__except, c++還是用try/catch,不要用前者. 原因是多方面的,不多說了.
思考一個問題, 如果不用__try/__except,如何在c++的代碼中SEH呢?
等下篇文章吧......