在Windows应用程序开发中,异常处理是程序稳定性的重要保障。然而,有时异常处理的行为可能并不如所预期的那样工作。本文将探讨一个具体的案例,其中异常处理的行为与预期不符,导致程序继续执行而不是立即终止。
在一次开发过程中,遇到了一个异常情况:尽管程序抛出了一个异常,但该异常并没有被预期的异常处理器捕获,程序也没有立即终止。最初,怀疑可能存在某些代码在异常栈中注册了一个异常处理器,该处理器会无条件地处理所有异常。因此,首先搜索了异常栈中的try-catch模式,但并未发现任何异常处理器。
异常栈的分析结果显示,存在一个之前未见过的异常处理器:
0012fd14: USER32!_except_handler3+0 (7e440457)
[...]
0012ffe0: kernel32!_except_handler3+0 (7c839af0)
[...]
Invalid exception stack at ffffffff
这个异常处理器的出现让感到困惑,因为它似乎没有意义,但却被操作系统用来处理异常,而不是报告严重问题并终止程序。这种行为无疑是奇怪且危险的。
为了弄清楚为什么操作系统会用这个异常处理器来处理异常,设置了断点来跟踪USER32!DispatchMessageWorker+113指令。发现,DispatchMessageWorker的异常过滤器会根据_teb.Win32ClientInfo.dwCompatFlags2标志的第17位来决定是否处理异常。如果该位未设置,过滤器将返回EXCEPTION_EXECUTE_HANDLER。在案例中,dwCompatFlags2没有任何位被设置,因此过滤器报告说DispatchMessageWorker的处理器可以处理任何异常。
以下是相关的代码片段:
USER32!GetAppCompatFlags2:
7e418ed6 8bff mov edi,edi
[...]
USER32!DispatchMessageWorker+0x113:
7e440712 6800040000 push 400h
[...]
7e440724 c3 ret
从代码中可以看出,如果_teb.Win32ClientInfo.dwCompatFlags2的第17位未设置,那么异常过滤器将允许DispatchMessageWorker的处理器来处理异常。
进一步分析DispatchMessageWorker+0x126的代码,发现这个异常处理器实际上什么也没做,它只是简单地从DispatchMessageWorker返回。
USER32!DispatchMessageWorker+0x126:
7e44072a 8b65e8 mov esp,dword ptr [ebp-18h]
[...]
7e419794 834dfcff or dword ptr [ebp-4],0FFFFFFFFh
[...]
7e4189e5 e816fcffff call USER32!_SEH_epilog (7e418600)
[...]
这种异常处理方式显然是不合适的,因为它允许程序在遇到异常时继续执行,而不是立即终止。
在深入分析了DispatchMessageWorker函数的异常处理机制后,意识到这种行为可能是由于某些特殊的条件触发的。推测,只有在特定的情况下,DispatchMessageWorker才会注册这个异常处理器。
通过进一步的分析,发现只有在处理WM_TIMER消息时,DispatchMessageWorker才会注册这个异常处理器。
USER32!DispatchMessageWorker+0xbf:
7e419742 393d8c00477e cmp dword ptr [USER32!gfServerProcess (7e47008c)],edi
[...]
7e419761 897dfc mov dword ptr [ebp-4],edi
[...]