异常处理程序和软件异常——Windows核心编程学习手札之二十四
異常處理程序和軟件異常
——Windows核心編程學習手札之二十四
CPU負責捕捉無效內存訪問和用0除一個數值這種錯誤,并相應引發一個異常作為對錯誤的反應,CPU引發的異常稱為硬件異常(hardware exception),操作系統和應用程序引發的異常,稱為軟件異常(software exception)。當出現一個硬件或軟件異常時,操作系統向應用程序提供機會來考察是什么類型的異常被引發,并能夠讓應用程序自己來處理異常。下面是異常處理程序的文法:
?????? __try{
??????????????????? //guarded body
????????????? ?????? 。。。
?????? }
?????? __except(exception filter){
???????????????????? //Exception handler
???????????????????? 。。。
?????? }
每建一個try塊,就必須跟隨一個finally或一個except塊,一個try塊之后不能既有finally塊又有except塊,但可以在try-except塊中嵌套try-finally塊,反過來也可以。
與結束處理程序不同,異常過濾器(exception filter)和異常處理程序是通過操作系統直接執行的,編譯程序在計算異常過濾器表達式和執行異常處理程序方面不做什么事。
當引發異常時,系統將定位到except塊的開頭,并計算異常過濾器表達式的值,過濾表達式的值只能是EXCEPTION_EXECUTE_HANDLER、EXCEPTION_CONTINUE_SEARCH、EXCEPTION_CONTINUE_EXECUTION三個標識符,定義在Windows的Excpt.h文件中。當try塊中沒有引發異常,則except塊中的代碼將永遠不會被執行。
標識符EXCEPTION_EXECUTE_HANDLER
異常過濾器表達式的值是EXCEPTION_EXECUTE_HANDLER告訴系統執行一個全局展開,然后執行except塊中的代碼(異常處理程序代碼)的跳轉。在except塊中代碼執行完之后,系統考慮這個要被處理的異常并允許應用程序繼續執行。這個機制使Windows應用程序可以處理錯誤并讓程序繼續運行。
全局展開(global unwind)使所有那些在處理異常的try_except塊之后開始執行但未完成的try_finally塊恢復執行。通過在finally塊里放入一個return語句,可以阻止系統完成一個全局展開,應避免使用。
標識符EXCEPTION_CONTINUE_EXECUTION
在異常過濾器里可以直接寫上標識符,也可以調用一個函數來返回標識符。過濾器值是標識符EXCEPTION_CONTINUE_EXECUTION時,系統跳回到產生異常的指令,試圖再執行一次。當一個線程序試圖去存取并不存在的棧存儲區時,就會產生一個異常,系統的異常過濾器可以確定這個異常是源于試圖存取棧的保留地址空間,異常過濾器調用VirtualAlloc向線程的棧提交更多的存儲區,然后過濾器返回EXCEPTION_CONTINUE_EXECUTION,這時,試圖存取棧存儲區的CPU指令可以成功執行,線程可以繼續執行。
標識符EXCEPTION_CONTINUE_SEARCH
標識符EXCEPTION_CONTINUE_SEARCH告訴系統去查找前面與一個except塊相匹配的try塊,并調用跟這個try塊的異常處理器。
函數GetExceptionCode
一個異常過濾器在確定返回什么值之前,要分析具體情況,指出所發生異常的類別:
代碼例子:
?????? __try{
???????????????????? X=0;
???????????????????? Y=4/x;
?????? }????
?????? __except( (GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO)?
??????????????????????????????????????????????????????? EXCEPTION_EXECUTE_HANDLER:
??????????????????????????????????????????????????????? EXCEPTION_CONTINUE_SEARCH){
???????????????????? //handle divide by zero exception
?????? }
內部函數GetExceptionCode返回一個值,指出所發生異常的種類:
?????? DWORD GetExceptionCode();
這些異常標識符在WinBase.h文件中。該函數只能在一個過濾器中條用(__except之后的括號里),或在一個異常處理程序中被調用。
函數GetExceptionInformation
當一個異常發生時,操作系統要向異常的線程的堆棧里壓入三個結構:EXCEPTION_RECORD結構、CONTEXT結構和EXCEPTION_POINTERS結構。EXCEPTION_RECORD結構包含有關已發生異常的獨立于CPU的信息,CONTEXT結構包含已發生異常的依賴于CPU的信息,EXCEPTION_POINTERS結構只有兩個數據成員,二者都是指針,分別指向被壓入棧的EXCEPTION_RECORD結構和CONTEXT結構:
?????? Typedef struct _EXCEPTION_POINTERS{
??????????????????????????? PEXCEPTION_RECORD ExceptionRecord,
??????????????????????????? PCONTEXT ContextRecord;
?????? }EXCEPTION_POINTERS,*PEXCEPTION_POINTERS;
取得這些信息并在程序中使用這些信息,要調用:
?????? PEXCEPTION_POINTERS GetExceptionInformation();
該函數只能在異常過濾器中調用,因為僅僅在處理異常過濾器(__except后面的括號里)時,上面三個結構才是有效的。
軟件異常
?程序捕獲軟件異常所采取的方法與捕獲硬件異常相同。如何讓自己的函數引發軟件異常,作為指出失敗的方法,可調用RaiseException函數::
?????? VOID RaiseException(
??????????????????????????? DWORD dwExceptionCode,
??????????????????????????? DWORD dwExceptionFlags,
??????????????????????????? DWORD nNumberOfArguments,
??????????????????????????? CONST ULONG_PTR *pArguments);
在自己程序中產生自己的軟件異常,如向系統的事件日志發送通知消息,每當程序中的一個函數發現某種問題,可以調用RaiseException函數并讓某些異常處理程序上溯調用樹查看特定的異常,或者將異常寫到日志里或彈出一個消息框。建立軟件異常來傳達程序內部致使錯誤的信息。
總結
以上是生活随笔為你收集整理的异常处理程序和软件异常——Windows核心编程学习手札之二十四的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 结束处理程序——Windows核心编程学
- 下一篇: 未处理异常和C++异常——Windows