閱讀480 返回首頁    go 阿裏雲 go 技術社區[雲棲]


細說windows的異常處理和實現——結構化異常

異常與C++的異常

C++編程中有一個重要的機製就是異常的處理,標準的C++定義了try...catch..throw的異常處理機製。但是這隻是C++裏的異常。

 

廣義的異常可以分為硬件產生的異常軟件產生的異常,像throw這樣的動作就是在軟件上產生一個異常,但是像寫入一個非法內存、除0這些都是硬件產生的異常;

異常的處理可以分為結構化異常處理(Structured Exception Handling)SEH和向量異常處理(Vectored exception handling)VEH

 

C++裏try...catch..throw屬於SEH,但是他隻能捕捉顯示的throw出來的軟件異常,明確這個問題非常重要。以前一直認為可以catch任何異常,其實這種想法是錯的,比如寫一句int* p=0;*p=1;這樣的語句是catch不到的,直接就會crash了,因為這是內存錯誤,硬件異常。

 

這樣看來,C++的try...catch..throw異常處理機製隻是處理了所有異常中的一個子集而已。

然而在windows上開發的我們,尤其是去開發一些產品,我們希望有方法去處理任何異常,絕不讓用戶突然收到一個崩潰的感歎號,提高用戶的體驗怎麼辦?

原來window在係統層麵上提供了更多的異常的處理機製,而C++的try...catch..throw也隻是window係統的異常處理機製中被整合進去的一小塊。

 

window係統層麵的(結構化)異常處理機製

window是把所有的硬件和軟件的異常都整合起來同等看待,無論是內存錯誤、用throw產生一個異常,都會觸發操作係統對當前的線程的異常處理

每個線程在創建後,都會初始化一個數據結構:

 ;struct _EXCEPTION_REGISTRATION{
 ;     struct _EXCEPTION_REGISTRATION *prev;
 ;     void (*handler)(PEXCEPTION_RECORD,
 ;                     PEXCEPTION_REGISTRATION,
 ;                     PCONTEXT,
 ;                     PEXCEPTION_RECORD);
 ;     struct scopetable_entry *scopetable;
 ;     int trylevel;
 ;     int _ebp;
 ;     PEXCEPTION_POINTERS xpointers;
 ;}

 

很多個EXCEPTION_REGISTATION 連成一個list L

其中的每個節點包含了一個handler成員,他就是具體的對異常進行處理的操作(可以猜測catch後的內容就是最終被整合到這個函數裏的)

當一個異常E發生後,係統會尋找這個線程的L,從頭開始,知道尋找到能對E進行處理的那個節點EXCEPTION_REGISTATION,對其進行處理。

對於C++的try catch其實就是在try的時候將L裏麵建立一個新的節點,用於捕捉某個異常。

 

所以win的異常處理機製的核心就在於每個線程內的這個EXCEPTION_REGISTATION 的list。

 

這裏有個問題,就是當一個異常沒有再list尋找到相應的節點怎麼辦,其實這個list會在尾端放置一個默認的異常處理單元,當查詢到尾端時就一定調用這個處理函數。

所以我們就有一種辦法去截獲所有程序中我們無法catch的異常(硬件或軟件),就是去自定義這個處理函數,使用API  SetUnhandledExceptionFilter

 

萬能的異常捕獲器 SetUnhandledExceptionFilter

記住這個函數隻有在release版本生效!

win程序在release情況下,發生了異常,並且沒有被捕捉處理,就會調用一個默認的UnhandledExceptionFilter函數,這個函數還傳入一個參數_EXCEPTION_POINTERS*,裏麵有crash的一些信息。


如果我們想自定義這個處理方法,就可以自定一個函數如

 long __stdcall MyUnhandledExceptionFilter (_EXCEPTION_POINTERS* ExceptionInfo){。。。

}

然後使用SetUnhandledExceptionFilter set進來。

MyUnhandledExceptionFilter 函數有三種返回值:

EXCEPTION_EXECUTE_HANDLER equ 1 表示我已經處理了異常,可以結束了,這時執行完這個函數程序退出
EXCEPTION_CONTINUE_SEARCH equ 0 表示我不處理,其他人來吧,於是windows調用默認的處理程序顯示一個錯誤框,並結束
EXCEPTION_CONTINUE_EXECUTION equ -1 表示錯誤已經被修複,請從異常發生處繼續執行  ,這時程序會試圖在運行一遍剛才又問題的代碼

 

通過自定義的異常處理函數,等於截獲了程序出錯的最後一道屏障,在程序crash即將關閉前改變了原有的行為,可以做到:

1.可以dump出來當前的信息,上傳給服務器用於技術人員統計分析

2.可以用更加友善的界麵告訴你的用戶程序出錯了,優雅的掛掉



 


最後更新:2017-04-03 19:06:48

  上一篇:go Linq中兩種更新操作
  下一篇:go 一個有問題的驅動程序