|
[求助]SEH学习问题
Unwinding的确是操作系统机制的一部分,因为还有一个重要的部分,UnHandledExceptionFilter,这个部分就是当没有用户异常处理程序处理异常的时候操作系统所做的善后工作,: 1、首先如果是地址错误,首先判断该地址是不是位于资源区块中,如果是,那么就将这个区块的只读属性去掉,然后接着执行。 2、如果是其他错误,那么操作系统查看这个线程是不是正在调试过程中,如果是的话,将建立相应的数据结构,并唤醒调试器。 3、如果没有调试器,它将查找是否用户是否安装了UnHandleException回调函数(就是VC或Delphi编译器中发生异常时候弹出的对话框,这个回调函数可以用SetUnhandledExceptionFilter来安装),否则的话就弹出那个对话框(按确定退出,按Canceal调试的那个)。 4、如果按了确定线程将被结束,在结束前系统将再次遍历用户安装的异常处理链表。(Unwinding)这个你可以将我测试用的那个SEHChain例子中的3个异常处理函数都改成不处理异常,这样就会弹出那个对话框,在你“确定”之后你会发现的确系统再次对链表进行了遍历。这就说明Unwinding操作系统也会用到。 5、如果按了Canceal,操作系统就建立一个调试进程,进行调试。 |
|
[求助]SEH学习问题
我想我在1楼时想知道的问题我已经知道了。谢谢了! |
|
[求助]SEH学习问题
上面的都是操作系统的机制,实际应用中编译器对SEH进行了封装,VC++对SEH的封装是引入了Exception-Frame,支持_try _except 嵌套结构。 3、在VC++中所有的_try _except都被封装在同一个异常处理回调函数中_exception_handler3函数(2中Call ECX 中ECX所指向的函数),我想这就是为什么 [QUOTE=sessiondiy;459243] 在执行到 [0]<- 0 前, Exception_Handler 是如下的变化: OEP 时 VC_EH - END main()的try时 VC_EH - VC_EH - END HomeGrownFrame()的try时 My_EH - VC_EH - VC_EH - END 所以有二个 VC_EH, 位址是一样的. 不用管他, 不影响分析, 下列我只写一个 VC_EH 来代表 [/QUOTE] 中只有一个地址的原因,这是因为所有的_try _except都指向了 _exception_handler3。 3.1 编译器在操作系统最小的EXCEPTION_REGISTRATION后面有添加了几个变量构成了新的结构VC_EXCEPTION_REGISTRATION这个结构对操作系统是透明的,但是对VC++来说是用来实现Exception_Frame的关键所在。 // The basic, OS defined exception frame struct EXCEPTION_REGISTRATION { EXCEPTION_REGISTRATION* prev; FARPROC handler; }; // Data structure(s) pointed to by Visual C++ extended exception frame struct scopetable_entry { DWORD previousTryLevel; FARPROC lpfnFilter; FARPROC lpfnHandler; }; // The extended exception frame used by Visual C++ struct VC_EXCEPTION_REGISTRATION : EXCEPTION_REGISTRATION { scopetable_entry * scopetable; int trylevel; int _ebp; }; 当发生异常的时候,_exceptio_handler3首先检查内层一场一场处理函数,如果不处理将寻找外层的异常处理函数。这个可以这样理解 _try _try Something; _except ExceptionHandler1(); end; _except ExceptionHandler2(); end; 当Something发生异常后,如果ExceptionHandler1()不处理,那么就该找ExceptionHandler2来处理。这就是_exception_handler3的大部分的工作内容,当前一场处理函数由VC_EXCEPTION_Handler中的tryLevel做索引在scopetable表中指出。如果当前trylevel异常处理函数不处理那么就由scopetable中的previousTryLevel指出下一个异常处理函数,也就是上层的异常处理函数。当前trylevel的数值是由编译器隐性给出的。 int __except_handler3( struct _EXCEPTION_RECORD * pExceptionRecord, struct EXCEPTION_REGISTRATION * pRegistrationFrame, struct _CONTEXT *pContextRecord, void * pDispatcherContext ) { LONG filterFuncRet LONG trylevel EXCEPTION_POINTERS exceptPtrs PSCOPETABLE pScopeTable CLD // Clear the direction flag (make no assumptions!) // if neither the EXCEPTION_UNWINDING nor EXCEPTION_EXIT_UNWIND bit // is set... This is true the first time through the handler (the // non-unwinding case) if ( ! (pExceptionRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)) ) { // Build the EXCEPTION_POINTERS structure on the stack exceptPtrs.ExceptionRecord = pExceptionRecord; exceptPtrs.ContextRecord = pContextRecord; // Put the pointer to the EXCEPTION_POINTERS 4 bytes below the // establisher frame. See ASM code for GetExceptionInformation *(PDWORD)((PBYTE)pRegistrationFrame - 4) = &exceptPtrs; // Get initial "trylevel" value trylevel = pRegistrationFrame->trylevel // Get a pointer to the scopetable array scopeTable = pRegistrationFrame->scopetable; search_for_handler: if ( pRegistrationFrame->trylevel != TRYLEVEL_NONE ) { if ( pRegistrationFrame->scopetable[trylevel].lpfnFilter ) { PUSH EBP // Save this frame EBP // !!!Very Important!!! Switch to original EBP. This is // what allows all locals in the frame to have the same // value as before the exception occurred. EBP = &pRegistrationFrame->_ebp // Call the filter function filterFuncRet = scopetable[trylevel].lpfnFilter(); POP EBP // Restore handler frame EBP if ( filterFuncRet != EXCEPTION_CONTINUE_SEARCH ) { if ( filterFuncRet < 0 ) // EXCEPTION_CONTINUE_EXECUTION return ExceptionContinueExecution; // If we get here, EXCEPTION_EXECUTE_HANDLER was specified scopetable == pRegistrationFrame->scopetable // Does the actual OS cleanup of registration frames // Causes this function to recurse __global_unwind2( pRegistrationFrame ); // Once we get here, everything is all cleaned up, except // for the last frame, where we'll continue execution EBP = &pRegistrationFrame->_ebp __local_unwind2( pRegistrationFrame, trylevel ); // NLG == "non-local-goto" (setjmp/longjmp stuff) __NLG_Notify( 1 ); // EAX == scopetable->lpfnHandler // Set the current trylevel to whatever SCOPETABLE entry // was being used when a handler was found pRegistrationFrame->trylevel = scopetable->previousTryLevel; // Call the _except {} block. Never returns. pRegistrationFrame->scopetable[trylevel].lpfnHandler(); } } scopeTable = pRegistrationFrame->scopetable; trylevel = scopeTable->previousTryLevel goto search_for_handler; } else // trylevel == TRYLEVEL_NONE { retvalue == DISPOSITION_CONTINUE_SEARCH; } } else // EXCEPTION_UNWINDING or EXCEPTION_EXIT_UNWIND flags are set { PUSH EBP // Save EBP EBP = pRegistrationFrame->_ebp // Set EBP for __local_unwind2 __local_unwind2( pRegistrationFrame, TRYLEVEL_NONE ) POP EBP // Restore EBP retvalue == DISPOSITION_CONTINUE_SEARCH; } } 3.2 _except_handler3的另外一个重要工作就是Unwind,当_except_handler3找到了处理该异常的异常处理函数,那么它将把该异常处理函数前面所有的异常处理函数都卸载掉,然后再执行该异常处理函数。(这就是我前面没有搞懂的Unwind),这个机制由调用_global_unwind2来实现。_global_unwind2只是对RtlUnwind的简单封装: __global_unwind2(void * pRegistFrame) { _RtlUnwind( pRegistFrame, &__ret_label, 0, 0 ); __ret_label: } RtlUnwind检查堆栈,建立新的EXCEPTION_REGISTRATION,然后再次遍历异常处理链,调用前面提到的RtlpExecuteHandlerForUnwind执行异常处理函数,调用RtlpUnlinkHandler移除不处理该异常的异常处理函数。 void _RtlUnwind( PEXCEPTION_REGISTRATION pRegistrationFrame, PVOID returnAddr, // Not used! (At least on i386) PEXCEPTION_RECORD pExcptRec, DWORD _eax_value ) { DWORD stackUserBase; DWORD stackUserTop; PEXCEPTION_RECORD pExcptRec; EXCEPTION_RECORD exceptRec; CONTEXT context; // Get stack boundaries from FS:[4] and FS:[8] RtlpGetStackLimits( &stackUserBase, &stackUserTop ); if ( 0 == pExcptRec ) // The normal case { pExcptRec = &excptRec; pExcptRec->ExceptionFlags = 0; pExcptRec->ExceptionCode = STATUS_UNWIND; pExcptRec->ExceptionRecord = 0; // Get return address off the stack pExcptRec->ExceptionAddress = RtlpGetReturnAddress(); pExcptRec->ExceptionInformation[0] = 0; } if ( pRegistrationFrame ) pExcptRec->ExceptionFlags |= EXCEPTION_UNWINDING; else pExcptRec->ExceptionFlags|=(EXCEPTION_UNWINDING|EXCEPTION_EXIT_UNWIND); context.ContextFlags = (CONTEXT_i486 | CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS); RtlpCaptureContext( &context ); context.Esp += 0x10; context.Eax = _eax_value; PEXCEPTION_REGISTRATION pExcptRegHead; pExcptRegHead = RtlpGetRegistrationHead(); // Retrieve FS:[0] // Begin traversing the list of EXCEPTION_REGISTRATION while ( -1 != pExcptRegHead ) { EXCEPTION_RECORD excptRec2; if ( pExcptRegHead == pRegistrationFrame ) { _NtContinue( &context, 0 ); } else { // If there's an exception frame, but it's lower on the stack // then the head of the exception list, something's wrong! if ( pRegistrationFrame && (pRegistrationFrame <= pExcptRegHead) ) { // Generate an exception to bail out excptRec2.ExceptionRecord = pExcptRec; excptRec2.NumberParameters = 0; excptRec2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET; excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; _RtlRaiseException( &exceptRec2 ); } } PVOID pStack = pExcptRegHead + 8; // 8==sizeof(EXCEPTION_REGISTRATION) if ( (stackUserBase <= pExcptRegHead ) // Make sure that && (stackUserTop >= pStack ) // pExcptRegHead is in && (0 == (pExcptRegHead & 3)) ) // range, and a multiple { // of 4 (i.e., sane) DWORD pNewRegistHead; DWORD retValue; retValue = RtlpExecutehandlerForUnwind( pExcptRec, pExcptRegHead, &context, &pNewRegistHead, pExceptRegHead->handler ); if ( retValue != DISPOSITION_CONTINUE_SEARCH ) { if ( retValue != DISPOSITION_COLLIDED_UNWIND ) { excptRec2.ExceptionRecord = pExcptRec; excptRec2.NumberParameters = 0; excptRec2.ExceptionCode = STATUS_INVALID_DISPOSITION; excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; RtlRaiseException( &excptRec2 ); } else pExcptRegHead = pNewRegistHead; } PEXCEPTION_REGISTRATION pCurrExcptReg = pExcptRegHead; pExcptRegHead = pExcptRegHead->prev; RtlpUnlinkHandler( pCurrExcptReg ); } else // The stack looks goofy! Raise an exception to bail out { excptRec2.ExceptionRecord = pExcptRec; excptRec2.NumberParameters = 0; excptRec2.ExceptionCode = STATUS_BAD_STACK; excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; RtlRaiseException( &excptRec2 ); } } // If we get here, we reached the end of the EXCEPTION_REGISTRATION list. // This shouldn't happen normally. if ( -1 == pRegistrationFrame ) NtContinue( &context, 0 ); else NtRaiseException( pExcptRec, &context, 0 ); } PEXCEPTION_REGISTRATION RtlpGetRegistrationHead( void ) { return FS:[0]; } _RtlpUnlinkHandler( PEXCEPTION_REGISTRATION pRegistrationFrame ) { FS:[0] = pRegistrationFrame->prev; } void _RtlpCaptureContext( CONTEXT * pContext ) { pContext->Eax = 0; pContext->Ecx = 0; pContext->Edx = 0; pContext->Ebx = 0; pContext->Esi = 0; pContext->Edi = 0; pContext->SegCs = CS; pContext->SegDs = DS; pContext->SegEs = ES; pContext->SegFs = FS; pContext->SegGs = GS; pContext->SegSs = SS; pContext->EFlags = flags; // __asm{ PUSHFD / pop [xxxxxxxx] } pContext->Eip = return address of the caller of the caller of this function pContext->Ebp = EBP of the caller of the caller of this function pContext->Esp = Context.Ebp + 8 } |
|
[求助]SEH学习问题
谢谢解答。 我把你写的和我理解的总结一下(帮忙看看是不是有错误): 当异常发生的时候: 0、从用户态(User mode)转向系统核心态(Kernel mode),系统内核从中断描述符表中找到相应的处理程序,进行处理……………………。 1、系统核心态处理的结果是生成两个结构参数?(EXCEPTION_RECORD,CONTEXT)。这两个结构参数的地址被当作参数传递给ntdll.KiUserExceptionDispatcher。(所有的异常都是调用这个函数,无一例外)。KiUserExceptionDispatcher主要调用RtlExceptionDispatcher,如果用户异常处理函数把异常处理掉了,那么RtlExceptionDispatcher永远都不返回。(这个是不是就是Caugth Exception in Main时没有还原异常链表的原因?),如果RtlExceptionDispatcher有返回值,那么要么程序继续执行,要么引发另外一个异常,这个异常不可处理标志着程序必须结束。 KiUserExceptionDispatcher( PEXCEPTION_RECORD pExcptRec, CONTEXT * pContext ) { DWORD retValue; // Note: If the exception is handled, RtlDispatchException() never returns if ( RtlDispatchException( pExceptRec, pContext ) ) retValue = NtContinue( pContext, 0 ); else retValue = NtRaiseException( pExceptRec, pContext, 0 ); EXCEPTION_RECORD excptRec2; excptRec2.ExceptionCode = retValue; excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; excptRec2.ExceptionRecord = pExcptRec; excptRec2.NumberParameters = 0; RtlRaiseException( &excptRec2 ); } 1.1 RtlDispatchException的作用就是检查用户安装(registration)的异常处理链表(是否在线程的堆栈之内,是否比前一个EXCEPTION_HANDLER在堆栈中的位置要高?是否在堆栈中4字节对齐?),然后遍历这个异常链表搜索用户异常处理函数。RtlDispatchException并不直接调用用户异常处理回调函数而是调用RtlpExecuteHandlerForException函数来处理用户异常处理函数,并且根据这个RtlpExecuteHandlerException函数的处理返回值决定是继续搜索链表还是引发另外一个异常(如果用户异常处理函数中也发生了异常的话)即DISPOSITION_NESTED_EXCEPTION(异常中的异常)。(!!!!!如果RtlpExecuteHandlerForException直接处理了异常就不再返回,而是在异常发生的地方接着执行!!!!!)。 int RtlDispatchException( PEXCEPTION_RECORD pExcptRec, CONTEXT * pContext ) { DWORD stackUserBase; DWORD stackUserTop; PEXCEPTION_REGISTRATION pRegistrationFrame; DWORD hLog; // Get stack boundaries from FS:[4] and FS:[8] RtlpGetStackLimits( &stackUserBase, &stackUserTop ); pRegistrationFrame = RtlpGetRegistrationHead(); while ( -1 != pRegistrationFrame ) { PVOID justPastRegistrationFrame = &pRegistrationFrame + 8; if ( stackUserBase > justPastRegistrationFrame ) { pExcptRec->ExceptionFlags |= EH_STACK_INVALID; return DISPOSITION_DISMISS; // 0 } if ( stackUsertop < justPastRegistrationFrame ) { pExcptRec->ExceptionFlags |= EH_STACK_INVALID; return DISPOSITION_DISMISS; // 0 } if ( pRegistrationFrame & 3 ) // Make sure stack is DWORD aligned { pExcptRec->ExceptionFlags |= EH_STACK_INVALID; return DISPOSITION_DISMISS; // 0 } if ( someProcessFlag ) { // Doesn't seem to do a whole heck of a lot. hLog = RtlpLogExceptionHandler( pExcptRec, pContext, 0, pRegistrationFrame, 0x10 ); } DWORD retValue, dispatcherContext; retValue= RtlpExecuteHandlerForException(pExcptRec, pRegistrationFrame, pContext, &dispatcherContext, pRegistrationFrame->handler ); // Doesn't seem to do a whole heck of a lot. if ( someProcessFlag ) RtlpLogLastExceptionDisposition( hLog, retValue ); if ( 0 == pRegistrationFrame ) { pExcptRec->ExceptionFlags &= ~EH_NESTED_CALL; // Turn off flag } EXCEPTION_RECORD excptRec2; DWORD yetAnotherValue = 0; if ( DISPOSITION_DISMISS == retValue ) { if ( pExcptRec->ExceptionFlags & EH_NONCONTINUABLE ) { excptRec2.ExceptionRecord = pExcptRec; excptRec2.ExceptionNumber = STATUS_NONCONTINUABLE_EXCEPTION; excptRec2.ExceptionFlags = EH_NONCONTINUABLE; excptRec2.NumberParameters = 0 RtlRaiseException( &excptRec2 ); } else return DISPOSITION_CONTINUE_SEARCH; } else if ( DISPOSITION_CONTINUE_SEARCH == retValue ) { } else if ( DISPOSITION_NESTED_EXCEPTION == retValue ) { pExcptRec->ExceptionFlags |= EH_EXIT_UNWIND; if ( dispatcherContext > yetAnotherValue ) yetAnotherValue = dispatcherContext; } else // DISPOSITION_COLLIDED_UNWIND { excptRec2.ExceptionRecord = pExcptRec; excptRec2.ExceptionNumber = STATUS_INVALID_DISPOSITION; excptRec2.ExceptionFlags = EH_NONCONTINUABLE; excptRec2.NumberParameters = 0 RtlRaiseException( &excptRec2 ); } pRegistrationFrame = pRegistrationFrame->prev; // Go to previous frame } return DISPOSITION_DISMISS; } 1。1。1 RtlpExecuteHandlerForException有一个姐妹函数RtlpExecuteHandlerForUnwind。(也就是Unwind机制,就像sessiondiy说的这机制是操作系统的机制)这两个函数其实是同一个函数(ExecuteHandler)的不同封装: _RtlpExecuteHandlerForException: // Handles exception (first time through) MOV EDX,XXXXXXXX JMP ExecuteHandler RtlpExecutehandlerForUnwind: // Handles unwind (second time through) MOV EDX,XXXXXXXX JMP ExecuteHandler 这个是meseh2.exe中的一段代码,似乎就是这个。 7C923753 BA D837927C mov edx, 7C9237D8 7C923758 EB 0D jmp short 7C923767 7C923767 53 push ebx 7C923768 56 push esi 7C923769 57 push edi 7C92376A 33C0 xor eax, eax 7C92376C 33DB xor ebx, ebx 7C92376E 33F6 xor esi, esi 7C923770 33FF xor edi, edi 7C923772 FF7424 20 push dword ptr [esp+20] 7C923776 FF7424 20 push dword ptr [esp+20] 7C92377A FF7424 20 push dword ptr [esp+20] 7C92377E FF7424 20 push dword ptr [esp+20] 7C923782 FF7424 20 push dword ptr [esp+20] 7C923786 E8 0E000000 call 7C923799 7C92378B 5F pop edi 7C92378C 5E pop esi 7C92378D 5B pop ebx 7C92378E C2 1400 retn 14 ExecuteHandler本身安装了一个异常处理函数(完完全全操作系统级别的最小安装),当用户异常处理函数又发生异常的时候,将返回DISPOSITION_NESTED_ EXCEPTION 或者 DISPOSITION_COLLIDED_UNWIND(这就是RtlpExecuteHandlerException返回值的源泉)。 7C923799 55 push ebp 7C92379A 8BEC mov ebp, esp 7C92379C FF75 0C push dword ptr [ebp+C] 7C92379F 52 push edx ;EDX指向ExecuteHandler的异常处理函数 7C9237A0 64:FF35 00000000 push dword ptr fs:[0] ;安装异常处理函数 7C9237A7 64:8925 00000000 mov dword ptr fs:[0], esp 7C9237AE FF75 14 push dword ptr [ebp+14] 7C9237B1 FF75 10 push dword ptr [ebp+10] 7C9237B4 FF75 0C push dword ptr [ebp+C] 7C9237B7 FF75 08 push dword ptr [ebp+8] 7C9237BA 8B4D 18 mov ecx, dword ptr [ebp+18] 7C9237BD FFD1 call ecx ;《加密解密第二版》中362页中的Call ECX? 7C9237BF 64:8B25 00000000 mov esp, dword ptr fs:[0] ;卸载ExecuteHandler的异常处理函数 7C9237C6 64:8F05 00000000 pop dword ptr fs:[0] 7C9237CD 8BE5 mov esp, ebp 7C9237CF 5D pop ebp 7C9237D0 C2 1400 retn 14 ExecuteHandler和他的异常处理函数列于下面: int ExecuteHandler( PEXCEPTION_RECORD pExcptRec PEXCEPTION_REGISTRATION pExcptReg CONTEXT * pContext PVOID pDispatcherContext, FARPROC handler ) // Really a ptr to an _except_handler() // Set up an EXCEPTION_REGISTRATION, where EDX points to the // appropriate handler code shown below PUSH EDX PUSH FS:[0] MOV FS:[0],ESP // Invoke the exception callback function EAX = handler( pExcptRec, pExcptReg, pContext, pDispatcherContext ); // Remove the minimal EXCEPTION_REGISTRATION frame MOV ESP,DWORD PTR FS:[00000000] POP DWORD PTR FS:[00000000] return EAX; } Exception handler used for _RtlpExecuteHandlerForException: { // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else // assign pDispatcher context and return DISPOSITION_NESTED_EXCEPTION return pExcptRec->ExceptionFlags & EXCEPTION_UNWIND_CONTEXT ? DISPOSITION_CONTINUE_SEARCH : *pDispatcherContext = pRegistrationFrame->scopetable, DISPOSITION_NESTED_EXCEPTION; } Exception handler used for _RtlpExecuteHandlerForUnwind: { // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else // assign pDispatcher context and return DISPOSITION_COLLIDED_UNWIND return pExcptRec->ExceptionFlags & EXCEPTION_UNWIND_CONTEXT ? DISPOSITION_CONTINUE_SEARCH : *pDispatcherContext = pRegistrationFrame->scopetable, DISPOSITION_COLLIDED_UNWIND; } |
|
[求助]SEH学习问题
谢谢解答!其实我想知道的是如何自己产生Unwind,即在ExceptionHandler3中做些处理,使得当找到这个异常处理程序的时候,再次遍历列表。也就是模拟系统默认的那个处理机制。 现在我在看编译器(VC++)的异常处理机制的实现(_exception_handler3和frame-exception),大致有点模糊的概念了,假如我没有理解错误的话(myseh2.cpp中的Unwinding由_exception_handler3产生),而我测试用的那个例子中Unwinding则是由 在BaseProcessStart中安装的系统默认ExceptionHandler产生,不知道是不是这样? 我想我还得多跟几个例子才能一点一点搞明白。 |
|
[求助]SEH学习问题
谢谢!我得慢慢消化消化。 |
|
[求助]SEH学习问题
编译器不可能知道一个Handler是不是合适的处理程序,比如我在一个Handler中判断,是不是发生了XXX错误,是的话就处理不是的话就不处理,这样编译器还怎么能预先知道这个Handler是不是合适的处理程序?我想还是有其他的机制在里面起作用…… |
|
[求助]SEH学习问题
看来要启用Unwinding的话还得使用一些手段,继续学习中…… |
|
[求助]SEH学习问题
不对啊,这虽然是Compiler实现的,但是还是操作系统的一种机制。9楼我的测试结果显示操作系统默认的异常处理Handler也是启用了这个机制的——给异常处理程序第二次执行的机会。 这个机制到底是如何实现的? |
|
[求助]SEH学习问题
当我将最后一个处理Handler的返回值改变为ExceptionContinueSearch(即改为不处理该异常)时,操作系统调用了默认的异常处理方案:(弹出个常见的内存不能写的对话框),此时输出结果变为: Exception Handler 1 : Exception Code C0000005 Exception Flags 00000000 Exception Handler 2 : Exception Code C0000005 Exception Flags 00000000 Exception Handler 3 : Exception Code C0000005 Exception Flags 00000000 Exception Handler 1 : Exception Code C0000027 Exception Flags 00000002 EH_UNWIND ING Exception Handler 2 : Exception Code C0000027 Exception Flags 00000002 EH_UNWIN DING Exception Handler 3 : Exception Code C0000027 Exception Flags 00000002 EH_UNWIND ING UnWinding机制出现了。看来这个机制的确是操作系统的机制? 现在又有问题了,这个机制到底是如何启动的? |
|
|
|
[求助]SEH学习问题
这个是文中的程序输出的结果,他用了一个自己构造的Handler和一个_try _exception结构。 我测试用的代码是下面那个,是全部用的自己构造的Handlers。(其中前两个不处理异常,直到第三个才进行异常处理并返回ExceptionContinueExection。) 输出结果为: Exception Handler 1 : Exception Code C0000005 Exception Flags 00000000 Exception Handler 2 : Exception Code C0000005 Exception Flags 00000000 Exception Handler 3 : Exception Code C0000005 Exception Flags 00000000 After writing! 并没有第二次的遍历过程。 |
|
[求助]SEH学习问题
首先,谢谢回答。 如果是操作系统的机制,我测试的例子中,EXCEPTION_DISPOSITION链表并没有第二次执行,只执行了一遍(也就是少了Unwinding机制中的第二次遍历过程,这个就是我不明白的地方。详细的你可以看看我测试用的那个例子的代码)。(文中的例子(或者说是Unwinding机制)是,系统找到处理异常后的程序,还要再一次遍历 EXCEPTION_DISPOSITION链表) |
操作理由
RANk
{{ user_info.golds == '' ? 0 : user_info.golds }}
雪币
{{ experience }}
课程经验
{{ score }}
学习收益
{{study_duration_fmt}}
学习时长
基本信息
荣誉称号:
{{ honorary_title }}
能力排名:
No.{{ rank_num }}
等 级:
LV{{ rank_lv-100 }}
活跃值:
在线值:
浏览人数:{{ visits }}
最近活跃:{{ last_active_time }}
注册时间:{{ user_info.create_date_jsonfmt }}
勋章
兑换勋章
证书
证书查询 >
能力值