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