Funcfurter1
Now let's look at another scenario in which termination handling really proves its value. Look at this function:
Now imagine that the Funcinator function called in the try block contains a bug that causes an invalid memory access. Without SEH, this situation would present the user with the ever-popular "Application has stopped working" dialog box provided by theWindows Error Reporting (WER) that will be presented in great detail inChapter 25, "Unhandled Exceptions, Vectored Exception Handling, and C++ Exceptions." When the user dismissed the error dialog box, the process would be terminated. When the process is terminated (because of an invalid memory access), the semaphore would still be owned and would never be released—any threads in other processes that were waiting for this semaphore would never be scheduled CPU time. But placing the call to ReleaseSemaphore in a finally block guarantees that the semaphore gets released even if some other function causes a memory access violation. I have to put a damper on this statement: starting with Windows Vista, you have to explicitly protect your try/finally to ensure that the finally block gets executed when an exception is raised. The necessary explanations are provided in "The SEH Termination Sample Application" on page 673, and the next chapter will dig into the details of the try/except protection.
现在设想一下,在本例中的函数Funcinator中有一个bug,会产生非法内存访问。如果不使用SEH,这种形势下,会出现一个WER提供的错误报告对话框(在25章里详细讲解)。如果用户关闭这个对话框,该进程将被终止,此时semaphore没有被释放,等待它的线程将继续等待。但把ReleaseSemaphore放到finally里,会保证semaphore会被正确释放,即使其他函数导致了内存访问冲突。(似从Vista开始)
However, even in prior versions of Windows, the finally blocks are not guaranteed to execute for any exception. For example, in Windows XP, if a stack exhaustion exception occurs in the try block, chances are good that the finally block is not going to get executed, because the WER code is running inside the faulting process possibly without enough stack left to report an error, so the process would be silently terminated. Similarly, if the exception generated a corruption in the SEH chain, the termination handler will not be executed. Last but not least, if another exception happened in the exception filter, the termination handler will not get executed. The rule of thumb to follow is always minimize the action of the code that runs within catch or finally blocks; otherwise, the process just terminates and no more finally blocks execute. This is why the error reporting in Windows Vista runs in a separate process, as detailed inChapter 25.
然而,在以前的Windows中,finally块并不能保证对任何异常都能执行。比如,在XP里,try中的堆栈异常不会导致finally中的代码被执行,因为WER代码在出错的进程里执行,很可能没有足够的stack来报告错误,因此进程会悄然结束。类似的,如果在SEH链中,异常产生了一个冲突,termination hander不会被执行。Last but not least,如果在exception
filter中发生了其他异常,termination hander不会被执行。The rule of thumb to follow is always minimize the action of the code that runs within catch or finally blocks;
otherwise, the process just terminates and no more finally blocks execute. This is why the error reporting in Windows Vista runs in a separate process, as detailed inChapter
25.
If termination handlers are powerful enough to capture a process while terminating because of an invalid memory access, we should have no trouble believing that they will also capture setjump and longjump combinations and, of course, simple statements such as break and continue.
如果 termination handers很强大,可以捕获因为非法内存访问终止的进程,we should have no trouble believing that they will also capture setjump and longjump combinations and, of course, simple statements such as break and continue.
Pop Quiz Time: FuncaDoodleDoo
Now for a test. Can you determine what the following function returns?
Let's analyze what the function does step by step. First dwTemp is set to 0. The code in the try block executes, but neither of the if statements evaluates to TRUE. Execution moves naturally to the code in the finally block, which increments dwTemp to1. Then the instruction after the finally block increments dwTemp again, making it 2.
一步步分析一下这个函数。首先 dwTemp为0,进入try之后,因为没有一个if为TRUE,执行流程自然地进入finally块,在这里dwTemp被自增。finally后的代码再次自增,使之为2
When the loop iterates, dwTemp is 2 and the continue statement in the try block will execute. Without a termination handler to force execution of the finally block before exit from the try block, execution would immediately jump back up to the while test, dwTemp would not be changed, and we would have started an infinite loop. With a termination handler, the system notes that the continue statement causes the flow of control to exit the try block prematurely and moves execution to the finally block. In the finally block, dwTemp is incremented to3. However, the code after the finally block doesn't execute because the flow of control moves back to continue and thus to the top of the loop.
在接下来的循环中,由于dwTemp为2,try中的continue将被执行。Without a termination handler to force execution of the finally block before exit from the try block,执行流程将立即回到while,dwTemp将永远不变,程序将陷入死循环。借助termination hander,系统会注意到continue将导致有提前退出try的情况并执行finally。在finally里,dwTemp被自增到3,然后,finally后的代码此次将不被执行,因为流程跳回到continue,继续循环。
Now we are processing the loop's third iteration. This time, the first if statement evaluates to FALSE, but the second if statement evaluates to TRUE. The system again catches our attempt to break out of the try block and executes the code in the finally block first. Now dwTemp is incremented to 4. Because a break statement was executed, control resumes after the loop. Thus, the code after the finally block and still inside the loop doesn't execute. The code below the loop adds 10 to dwTemp for a grand total of 14—the result of calling this function. It should go without saying that you should never actually write code like FuncaDoodleDoo. I placed the continue and break statements in the middle of the code only to demonstrate the operation of the termination handler.
现在我们执行第三次循环。这次,第二个if被满足,系统再次发现我们要提前退出try,因此先执行finally。现在dwTemp被自增到4.因为执行的是break,finally中的代码还是没有被执行,循环底下的代码被执行,最后的结果变为14.
Although a termination handler will catch most situations in which the try block would otherwise be exited prematurely, it can't cause the code in a finally block to be executed if the thread or process is terminated. A call to ExitThread or ExitProcess will immediately terminate thethread or process without executing any of the code in a finally block. Also, if your thread or process should die because some application called TerminateThread or TerminateProcess, the code in a finally block again won't execute. Some C run-time functions (such as abort) that in turn call ExitProcess again preclude the execution of finally blocks. You can't do anything to prevent another application from terminating one of your threads or processes, but you can prevent your own premature calls to ExitThread and ExitProcess.
如果进程或者线程是被ExitThread或者ExitProcess终止的,finally中的代码将不会执行。