?? 結構化異常處理.htm
字號:
<br> };
<br>
<br> 明白了handler的任務以后,看它實際的代碼吧.
<br> int __except_handler3(_EXCEPTION_RECORD * pExceptionRecord,EXCEPTION_REGISTRATION * pRegistrationFrame,_CONTEXT
<br> *pContextRecord,void * pDispatcherContext )
<br> {
<br> LONG filterFuncRet
<br> LONG trylevel
<br> EXCEPTION_POINTERS exceptPtrs
<br> PSCOPETABLE pScopeTable
<br>
<br> CLD // Clear the direction flag (make no assumptions!),這個是c語言編譯器默認的操作方式
<br>
<br> // if neither the EXCEPTION_UNWINDING nor EXCEPTION_EXIT_UNWIND bit
<br> // is set... This is true the first time through the handler (the
<br> // non-unwinding case)
<br>
<br> // 檢查是不是要進行unwind
<br> if ( ! (pExceptionRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)) )
<br> {
<br> // 設置[ebp-14]的值,記得上面說的[ebp-14]放的是什么么,這里的ExceptionRecord是在handler的堆棧里面的
<br> // 所以它的生存期是有限的,handler函數返回了,這個就不存在了,[ebp-14]這個指針也就指向了未知區域了,所以msdn里面限制
<br> // 了GetExceptionXXX函數的調用地點,明白了么?
<br> // Build the EXCEPTION_POINTERS structure on the stack
<br> exceptPtrs.ExceptionRecord = pExceptionRecord;
<br> exceptPtrs.ContextRecord = pContextRecord;
<br>
<br> // Put the pointer to the EXCEPTION_POINTERS 4 bytes below the
<br> // establisher frame. See ASM code for GetExceptionInformation
<br> // 想想看,-4指向了什么地方?
<br> *(PDWORD)((PBYTE)pRegistrationFrame - 4) = &exceptPtrs;
<br>
<br> // Get initial "trylevel" value,看看布局再看看上面那個結構的定義
<br> trylevel = pRegistrationFrame->trylevel
<br>
<br> // Get a pointer to the scopetable array
<br> scopeTable = pRegistrationFrame->scopetable;
<br>
<br>search_for_handler:
<br>
<br> if ( pRegistrationFrame->trylevel != TRYLEVEL_NONE/*-1*/ )
<br> {
<br> // 如果是空,就表示這個是一個finally語句,finally是用來作unwind的
<br> if ( pRegistrationFrame->scopetable[trylevel].lpfnFilter )
<br> {
<br> PUSH EBP // Save this frame EBP
<br>
<br> // !!!Very Important!!! Switch to original EBP. This is
<br> // what allows all locals in the frame to have the same
<br> // value as before the exception occurred.
<br> // ebp是一個函數的frame pointer,對于一個函數的執行非常的重要,這里既然是要執行filter(except后面小括號
<br> // 里面的語句),就必須要恢復ebp的值,ebp是怎么恢復的呢?,上面的代碼里面可以看到是一個mov ebp,esp,這個esp又
<br> // 是什么呢?對比下上面的內存布局,好好體會這句話的含義,看清楚前面有個取地址符.
<br> EBP = &pRegistrationFrame->_ebp
<br>
<br> // Call the filter function調用except小括號里面的語句,檢查這個返回值
<br> filterFuncRet = scopetable[trylevel].lpfnFilter();
<br>
<br> POP EBP // Restore handler frame EBP
<br>
<br> if ( filterFuncRet != EXCEPTION_CONTINUE_SEARCH )
<br> {
<br> if ( filterFuncRet < 0 ) // EXCEPTION_CONTINUE_EXECUTION
<br> return ExceptionContinueExecution; // 依靠操作系統完成continue execution
<br>
<br> // If we get here, EXCEPTION_EXECUTE_HANDLER was specified
<br> scopetable == pRegistrationFrame->scopetable
<br>
<br> // Does the actual OS cleanup of registration frames
<br> // Causes this function to recurse
<br> // 進行unwind,操作系統會變量當前registration record以前的handler一一調用他們,然后斷開這些record鏈表
<br> __global_unwind2( pRegistrationFrame );
<br>
<br> // Once we get here, everything is all cleaned up, except
<br> // for the last frame, where we'll continue execution
<br> EBP = &pRegistrationFrame->_ebp
<br>
<br> // 操作系統幫我們完成前面的unwind,當前record的unwind要自己來完成
<br> __local_unwind2( pRegistrationFrame, trylevel );
<br>
<br> // 這里是setjmp/longjmp支持代碼
<br> // NLG == "non-local-goto" (setjmp/longjmp stuff)
<br> __NLG_Notify( 1 ); // EAX == scopetable->lpfnHandler
<br>
<br> // Set the current trylevel to whatever SCOPETABLE entry
<br> // was being used when a handler was found
<br> // 修改當前的trylevel為prevtrylevel,很顯然,從當前的try block出來了自然就到了上一個try block
<br> pRegistrationFrame->trylevel = scopetable->previousTryLevel;
<br>
<br> // Call the _except {} block. Never returns.
<br> // goto except語句,這里并不返回,因為編譯器并沒有在except語句最后生成一個ret代碼
<br> pRegistrationFrame->scopetable[trylevel].lpfnHandler();
<br> }
<br> }
<br>
<br> scopeTable = pRegistrationFrame->scopetable;
<br> trylevel = scopeTable->previousTryLevel
<br>
<br> goto search_for_handler;
<br> }
<br> else // trylevel == TRYLEVEL_NONE
<br> {
<br> retvalue == DISPOSITION_CONTINUE_SEARCH;
<br> }
<br> }
<br> }
<br> else // EXCEPTION_UNWINDING or EXCEPTION_EXIT_UNWIND flags are set
<br> {
<br> // 進行unwind(由__global_unwind2函數觸發)
<br> PUSH EBP // Save EBP
<br> EBP = &pRegistrationFrame->_ebp // Set EBP for __local_unwind2
<br>
<br> __local_unwind2( pRegistrationFrame, TRYLEVEL_NONE )
<br>
<br> POP EBP // Restore EBP
<br>
<br> retvalue == DISPOSITION_CONTINUE_SEARCH;
<br> }
<br> }
<br>
<br> 這里也不能不提到編譯器為你生成代碼的樣子
<br>
<br> __try
<br> {
<br> i = 0;
<br> }
<br> __except(EXCEPTION_EXECUTE_HANDLER)
<br> {
<br> i = 1;
<br> }
<br>
<br> 這里假設i放在 ebp-20的地方,同時省略fs:[0]的設置
<br>
<br>__try:
<br> mov [ebp-4],0 ; trylevel = 0
<br> mov [ebp-18h],esp ; 保存esp
<br> mov [ebp-20h],0 ; 執行i = 0
<br> jmp __finish ; 跳出try語句
<br>__except_filter:
<br> mov eax,EXCEPTION_EXECUTE_HANDLER ; 返回
<br> ret
<br>__except_body:
<br> mov esp,[ebp-18h] ; 首先恢復esp值,也就是回復運行棧
<br> mov [ebp-20h],1 ; 執行 i = 1;
<br>__finish:
<br> mov [ebp-4],0ffffffffh ; trylevel = -1
<br>
<br> 到這里差不多我要講的就結束了,更為詳細的可以參考我多次提到的msj里面的那個文章.
<br> http://www.microsoft.com/msj/0197/exception/exception.aspx
<br> 如果你對vc生成的代碼更加的感興趣,你可以使用ida+softice動態靜態跟蹤看看.
<br>
<br></td>
</tr>
</table>
</td>
<td bgcolor="#CCCCCC"><img src="image/001.gif" width="1" height="1"></td>
</tr>
</table>
</body>
</html>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -