日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

VC中的異常處理

系統 1767 0

在讀《軟件調試》的十一章時,感受到異常處理在VC中是十分重要的。以前自己寫代碼或者是看身邊的人寫的代碼都很少用到異常處理,但最近在工作中會接觸到老外牛人寫的代碼,幾乎在每個關鍵的代碼塊都提供了異常處理,雖然在這些異常處理代碼中只是簡單的將異常的相關信息寫入Event Viewer,但這已經對我們找到bug和了解系統運行情況提供了很大的幫助。于是乎我把學習這一章的心得總結出來,供大家分享。

首先我們看window為描述異常定義的數據結構 EXCEPTION_RECORD,具體如下

typedef struct _EXCEPTION_RECORD{

DWORD ExceptionCode; // 異常代碼

DWORD ExceptionFlags; // 異常標志

struct _EXCEPTION_RECORD* ExceptionRecord; // 相關的另外一個異常

PVOID ExceptionAddress; // 異常發生地址

DWORD NumberParameters; // 參數數組中的元素個數

ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; // 參數數組

}

其中的ExcptionCode,我們應當非常熟悉了,最常見的為EXCEPTION_ACCESS_VIOLATION, 值為0xC0000005L, 就是非法訪問,我們遇到的大多數Crash問題都是來源于此。


了解了異常的結構,下面我們看異常是如何分發的:

異常分發包括用戶態的異常分發和內核態的異常分發,這里只介紹用戶態的異常分發。KiDispatchException是個內核函數,它是分發windows異常的樞紐,它的函數原型為KiDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,

IN PKEXCEPTION_FRAME ExceptionFrame,

IN PKTRAP_FRAME TrapFrame,

IN KPPROCESSOR_MODE PreviousMode,

IN BOOLEN FirstChange);

其中的參數ExceptionRecord指的是上面我介紹的_EXCEPTION_RECORD結構。其他的參數我就不詳細介紹了,我重點介紹一下最后的一個參數FirstChange,相信使用過windbg的同學們都會發現總能看到FirstChange這個參數吧,其實異常在分發過程中最多可以分發兩次,下圖是摘自《軟件調試》,清楚的顯示了kiDispatchException的分發異常的過程。其中左側是用戶態的,右側是內核態的。

如上圖所示,當變量FirstChange為TRUE時,調用DbgkForwardException分發給調試子系統,也就是當存在調試器時,分首先分發給調試器,所以當使用windbg時我們常會發現First Change字段。如果函數DbgForwardException返回FALSE,那么就說明沒有發現調試器,會調用DbgkForwardException進行第二次分發,這次分發會尋找異常的處理塊,一般就是__exception{}字段的代碼或是catch{}字段的代碼,等一會在介紹。如果這次仍然返回FALSE,就會調用ZwTerminateThread終止當前的線程,會彈出對話框,里面包含一些異常的信息,這個也是很常見的。如果出現在內核狀態,那么就直接出現藍屏(BSOD)。

下面介紹一下結構化異常處理(SEH)和向量化異常處理(VEH)

結構化異常處理,從系統的角度來看,SEH是對windows操作系統中異常分發和處理機制的總稱,其實現遍布在windows系統的很多模塊和數據結構中。從編程的角度來看,SEH是一套規范,利用這套規范,程序員可以編寫處理代碼來復用系統的異常處理設施。可以理解為是操作系統的異常機制的對外接口,也就是如何在windows程序中使用windows系統的異常處理機制。(摘自《軟件調試》291頁)

結構化異常處理提供了終結處理(Termination Handling)和異常處理(Exception Handling)兩種功能。以VC++為例,終結處理的結構如下

__try

{

// 被保護體

}

__finally

{

// 終結處理代碼
}

終結處理的用途是只要被保護體內的代碼執行了,除非是調用了ExitThread或ExitProcess,那么終結處理的代碼就一定會執行。所以終結處理的這種特性,適合做資源的釋放工作,包括內存的釋放,避免內存泄露,以及狀態的恢復,包括信號設置等,避免多線程出現死鎖。

終結處理的執行過程有一點需要說明,如果被保護體內存在代碼return,那意味著退出函數,這時終結塊是如何被執行的呢?其實編譯器會定義一個局部變量來保存return的值,然后在目標代碼中出入指令調用名為__local_unwind2的局部展開函數,來進行調用finally塊的代碼。注:加雙下劃線的關鍵字是編譯器定義的。

下面我們看異常的處理方式:

__try

{

// 被保護的代碼塊

}

__except( 過濾表達式)

{

// 異常處理塊

}

是不是覺得眼熟,它與C++的異常處理很相似,C++的try-catch-throw結構如下:

try {
// code that could throw an exception
}
[ catch (exception-declaration) {
// code that executes when exception-declaration is thrown
// in the try block
}
[catch (exception-declaration) {
// code that handles another exception type
} ] . . . ]
// The following syntax shows a throw expression:
throw [expression]

本想將兩個特點與不同詳細介紹一下,但偶然間發現前輩已經總結了,它博客的地址為 http://blog.csdn.net/lingqinghua/archive/2005/12/21/558182.aspx

所以SEH與C++異常的不同我就不介紹了,感興趣的可以直接查看他的博客。

現在我們看SEH的過濾表示式,這個表達式可以是常量,條件運算符,逗號表達式或者其他的函數。但其結果必須為0,1,-1這三個值。其中EXCEPTION_CONTIONUE_SERARCH的值為0,表示本保護塊不處理該異常,讓系統尋找其他異常處理塊。EXCEPTION_CONTINUE_EXECUTION的值為1,表式已經處理了該異常,讓程序返回到異常發生點,繼續執行。這種情況是如果該異常沒有清除,可能還會發生異常。該過程存在很大的風險,因為對于實際問題,很難找到完美的異常解決方案,一旦返回異常點繼續執行,再次發生異常,因此VC編譯器不準返回EXCEPTION_CONTINUE_EXECUTION。如果強行返回,將會導致EXECPTION_NONCONTINUABLE_EXCEPTION異常。EXCEPTION_EXECUTE_HANDLER的值為-1。它的含義是這是本保護塊預計到的異常,當發生該異常后,會執行本塊的異常代碼,執行完成后會繼續向下執行。所以可以在異常處理塊中進行記錄異常的一些信息,包括寄存器的信息。

下面我們看VEH(向量化結構處理)

VEH的基本思想是通過注冊回調函數的方式來接收和處理異常。回調函數的原型為LONG CALLBACK VectoredHandler ( PEXCEPTION_POINTERS ExceptionInfo );

對應的有兩個windows API函數AddVectoredExceptionHandler(ULONG FirstHandler, PVECTORD_EXCEPTION_HANDLER vectoredHandler)和RemoveVectoredExceptionHandler分別用來注冊和注銷回調函數。參數FirstHandler用來指定該回調函數的調用順序, 為0表示希望最后被調用,為1表示希望最先被調用。如果注冊多個回調函數,而且FirstHandler都為非零值,那么最后注冊的會最先被調用。

VEH的示例可以參考《軟件調試》的304頁。

最后總結一下VEH與SEH的區別。

1. SEH既可以用在用戶態的代碼中,也可以用在內核態的代碼中。但VEH只能用在用戶態的代碼中。VEH需要在windows XP以上的版本中使用

2. 如果同時注冊了VEH和SEH,那么VEH優先處理

3. SEH的注冊信息是以固定的結構存儲在線程棧中的, VEH的注冊信息是存儲在進程的內存堆中的。這讓我想到了一個問題,能夠在進程的內存中手動添加VEH信息,當有異常發生時就會執行VEH的代碼,這是不是可以破壞一些程序。純屬異想天開,望高人指點!

4. VEH對整個進程都有效,而SEH是動態建立在函數的棧幀上的,會隨著函數的返回而被注銷。

5. SEH是依賴編譯器編譯時生成的數據結構和代碼。而VEH的注冊和注銷都是通過系統的API顯式完成的,靈活~

謝謝,大部分都是參考的《軟件調試》,僅一點點心得,希望對不是很了解的人有些幫助!

VC中的異常處理


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 台北市| 堆龙德庆县| 墨脱县| 威信县| 阜宁县| 双桥区| 宁城县| 武邑县| 台南市| 龙川县| 乐安县| 舞钢市| 凤山市| 华安县| 广南县| 时尚| 沁源县| 内江市| 华宁县| 桐庐县| 修文县| 女性| 伊金霍洛旗| 武胜县| 巩留县| 新巴尔虎左旗| 尼勒克县| 怀来县| 万荣县| 芮城县| 峨边| 通道| 马公市| 义马市| 凌海市| 海南省| 北海市| 旅游| 焦作市| 铜川市| 上虞市|