Windows異常處理

1、Windows對於C++異常處理的擴展:

爲了更便捷的在Windows系統中進行異常處理,微軟在C++異常處理的機制上,又增加了擴展,稱爲SEH(Structured Exception Handling),即結構化異常處理;

語法如下:
__try

{

……

}
__except (<exception>)

{

……

}

Windows exception 最常見的問題 :
runtime_error 運行時錯誤:僅在運行時才能檢測到的問題 
range_error 運行時錯誤:生產的問題超出了有意義的值域範圍 
overflow_error 運行時錯誤:計算上溢 
underflow_error 運行時錯誤:計算下溢 
logic_error 邏輯錯誤: 可在運行前檢測到的問題 
domain_error 邏輯錯誤: 參數的結果值不存在 
invalid_argument 邏輯錯誤: 不合適的參數 
length_error 邏輯錯誤: 試圖生成一個超出該類型最大長度的對象 
out_of_range 邏輯錯誤:使用一個超出有效範圍的值 


2、__try -__except:

__try-__except是Microsoft擴展出的C++關鍵字,__try塊中出現錯誤或異常,一般不再用throw拋出,而是直接產生一個EXCEPTION_POINTERS類型的異常數據,然後開始查找SEH例程入口(調試的情況除外)。首先就會找到與__try塊對應的__except塊。__except的參數<exception>與catch的參數作用完全不同,也不類似於函數的參數,它主要是用於控制後面的程序執行,爲這幾個值之一:
EXCEPTION_EXECUTE_HANDLER(1),表示下面執行__except塊內及其後面的代碼

EXCEPTION_CONTINUE_EXECUTION(-1),表示回到拋出異常處繼續向下執行

EXCEPTION_CONTINUE_SEARCH(0), 表示查找下一個異常處理例程入口
Microsoft提供兩個函數GetExceptionCode(), GetExceptionInformation(),分別可以獲取異常號和EXCEPTION_POINTERS類型的異常數據指針。而且這兩個函數只能在__except參數<exception>的表達式中使用。爲了保證這一點,在VC中,編譯器做了特殊處理,如果這兩個函數沒有在正確的位置,將產生編譯錯誤。


3、例:處理除零異常:

 #include <windows.h>

Intmain()
{
inti= 1;
intj = 0;
__try
{
i/= j;
}
__except(GetExceptionCode() ==
EXCEPTION_INT_DIVIDE_BY_ZERO ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH)
{
cout<< “除零異常" << endl;
}
}

4、例:處理寫內存異常:

Int main()
{
__try
{
int *p=NULL;
*p = 13;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
cout << “內存異常" << endl;
}
}

5、Windows異常處理流程:

(1)中止當前程序的執行。
(2)如果程序處於被調試狀態,向調試器發送EXCEPTION_DEBUG_EVENT消息。
(3)如果程序沒有被調試或者調試器未能處理異常,查找線程相關的異常處理例程(如對應__except塊)並處理。如果前面查找到的例程返回  EXCEPTION_CONTINUE_SEARCH,且線程有多個異常處理例程,則沿這些例程入口地址組成的鏈式結構逐一向後查找,請求下一個例程處理。
(4)如果線程沒有對應的異常處理例程,或線程所有例程都返回EXCEPTION_CONTINUE_SEARCH,而且程序處於被調試狀態,再次通知調試器。
(5)如果程序沒有被調試或者調試器仍未處理異常,則進入主線程的“最終異常處理例程”鏈繼續查找。
(6)“最終異常處理例程”鏈的最後是Windows默認的系統異常處理程序__CxxUnhandledExceptionFilter(),其處理通常是彈出一個異常對話框,上面顯示一些異常信息,提   供“關閉”、“調試”等按鈕。

6、 SEH 到 C++異常的轉換

在同一個程序中,如果使用WIN32API它會拋出SHE,使用C++庫函數,它們又會拋出C++異常,Win32API和C++函數混和使用時如果使用兩種異常捕獲機制時,使用起來會影響程序的可讀性,因此C++運行庫提供了_set_se_translator 函數,在SHE異常發生時通過回調方式來轉換SEH異常爲C++異常。在此提供一個轉換的宏來實現轉換。

轉換宏的代碼:

#define INSTALL_SEHCONVERT() ExceptionConvert ecExceptionConvert

class SEHException

{

private :

             unsigned int nSE;

public :

             SEHException() {}

    SEHException( unsigned int n ) : nSE( n ) {}

             ~SEHException() {}

    unsigned int getSeNumber() { return nSE; }

};

class ExceptionConvert

{

public :

         ExceptionConvert(){OldFanc = _set_se_translator(trans_func); }

         ~ExceptionConvert(){_set_se_translator(OldFanc); }

private :

                  static void trans_func( unsigned int u, EXCEPTION_POINTERS* pExp )

         {

                      throw SEHException(u);

         }

         _se_translator_function OldFanc;

};

使用上面 INSTALL_SEHCONVERT 宏後就可以使用如下代碼來捕獲SHE異常了

INSTALL_SEHCONVERT();

Try

{

         …

}

catch(SEHException &seh){

   …

}

7、 同步異常與異步異常

1) VC的C++ Exception 採用兩種模式捕獲異常:同步模式和異步模式。VC的工程的調試版本缺省使用異步模式,工程的發佈版本缺省使用同步模式。在同步模式下,VC的編譯器假定代 碼中只有在顯示使用throw和調用函數的時候纔會引發異常,因此,在同步模式下,VC編譯出的代碼比較小,但在這種模式下,try-catch對不能捕 獲內存訪問異常與算術除零異常等。在異步模式下,VC的編譯器爲try塊內的每一條語句生成異常捕獲代碼,在這種情況下,他能夠捕獲全部的異常,還能保證 棧上對象在解棧中正確釋放。爲了要在發行版本中也能夠捕獲全部異常就需要打開異步模式,但代價是程序編譯出代碼變大,運行速度變慢。

2)編譯選項:

同步模式的編譯選項爲/EHs或者/GX(等同於/EHsc)

異步模式的編譯選項爲/EHa

8、 多線程下的異常捕獲

在創建線程並運行線程的函數中把創建線程的代碼放在try塊中並不會捕獲到線程函數中發生的異常,線程函數中發生的異常只能在線程函數中捕獲。並且每一個線程都需要自己的SHE轉換宏。轉換宏可以放在線程函數的開始部分


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章