最初发在QQ空间,转到这:http://user.qzone.qq.com/31731705/blog/1314257137
前面从理论(VEH中的陷阱(上))和实际(VEH中的陷阱(下))研究了VEH中存在的问题,那么,VEH的使用过程中有哪些注意事项?
1. 防御性编程
如果异常的目的是和调试器通信,首先应该检测调试器的存在。并且即使调试器存在的情况下,也必须使用SEH去处理自己抛出的异常,不能去指望调试器一定会处理这个异常。代码类似这样,
__try { if( IsDebuggerPresent() ) { ... RaiseException( ... ); } } __except( ... ) { }
只有调试器存在的情况下抛出异常,并且使用SEH去处理之,两只手都要硬。OutputDebugString有SEH块,但没有检测调试器的存在。如果它这样做了,之前的程序将会是完成不同的两个结果。
再说一个初始断点的例子。OS在启动应用程序时,在有调试器的情况下会启动初始断点,xp系统做了检测调试器的逻辑,不过却漏了__except块,如果调试器没有处理这个初始断点,应用程序就crash了。听起来不可思议,自己抛出的异常指望别人一定会收拾。但这就是残酷的事实。幸运的是,win7系统下做了修正,初始断点的逻辑中添加了SEH块。(关于初始断点的例子可以参见:http://bbs.pediy.com/showthread.php?t=137703)
2. VEH的代码应该如何写呢?
防御性编程在VEH中的作用有限,以上面的代码为例,即使调试器在运行,应用程序抛出的异常也没法指望调试器一定会去处理。而且,VEH存在的情况下SEH无法在第一时间起作用。那么到底如何使用VEH?
最好的办法是不使用它,象我前面说的,VEH是个新东西,原有的大量代码使用SEH,而VEH的出现打破了平衡,太危险,你不知道OS里的代码什么时候会来点问题,象OutputDebugString那样。看看Windows平台下的异常处理,浅谈SEH和UEF,你真的需要使用VEH嘛?
如果实在要用,应该这样:只处理你想处理的异常,其它的返回EXCEPTION_CONTINUE_SEARCH,让以前的机制工作。
LONG NTAPI VEHandler(PEXCEPTION_POINTERS pExceptionInfo) { //if( VECount <= 100 ) { /// new part for demo this bug char buf[2048]; sprintf( buf, "This is %d, %x\n", VECount++, pExceptionInfo ); printf( buf ); /// new part ends. ShowExceptionInfo( "VEH", pExceptionInfo ); switch( pExceptionInfo->ExceptionRecord->ExceptionCode ) { case STATUS_INTEGER_DIVIDE_BY_ZERO: global = 1; pExceptionInfo->ContextRecord->Eip -= (0x36-0x2a); // //pExceptionInfo->ContextRecord->Ecx = global; OutputDebugString("Catch an exception by VEH!\r\n"); return EXCEPTION_CONTINUE_EXECUTION; case DBG_PRINTEXCEPTION_C: break; case EXCEPTION_STACK_OVERFLOW: printf( "Stackoverflow. \r\n" ); break; } } return EXCEPTION_CONTINUE_SEARCH; }
将OutputDebugString移动到除0异常的代码里(这是你真正想要处理的异常),其它的直接返回。虽然VEH还是会捕捉到OutputDebugString的异常,但它会继续分发下去。代码的执行结果如下:
This is 0, 12f9ac
Exceuting VEH: code=C0000094, flags=0
This is 1, 12ea38
Exceuting VEH: code=40010006, flags=0
This is 2, 12f6dc
Exceuting VEH: code=40010006, flags=0
exit VEH
通过只处理自己需要处理的异常,继续分发其它的异常,可以一定程序上保证VEH的安全,也保证了VEH和SEH之间的兼容性。但即使如此,VEH就象一座活火山一样,你不知道它什么时候会爆发。
3. VEH能否构造自己的平衡?
现存的大量代码都是基于SEH,VEH的横空出世破坏了现有的格局,有没有办法象SEH那样嵌套的使用VEH?
答案是有,但比较tricky。方法就是在VEHandler开始处再次调用AddVectoredExceptionHandler,结束之前再RemoveVectoredExceptionHandler,在中间添加又一个VEH处理器。看上去比较疯狂,但确实能起作用。新添加的VEH可以处理原来的VEH中的问题。,这在某种程度上类似SEH,也巧妙的构成了一种平衡。
但是这要求原有的大量代码也要修改成这种方式,另外,使用起来不象使用__try,__except这么方便,第三点,VEH的结果选择,要么EXCEPTION_CONTINUE_EXECUTION,要么EXCEPTION_CONTINUE_SEARCH,没有第3种结果EXCEPTION_EXECUTE_HANDLER,这可是SEH中很有杀伤力的利器。第四点,VEH中缺乏终结器,即__finally,SEH中有,但在VEH存在的情况下,只有VEH交出控制权,SEH中的__finally才能起作用。从第三,第四两点来说,VEH比SEH缺乏处理异常的能力。
总之,从和原有的代码合作角度来看,还是从处理异常的能力能看,SEH仍然是当之无愧的首选。VEH的使用则要仔细测试,慎之又慎。