现在的位置: 首页 > 综合 > 正文

Chapter 23: Termination Handlers(2)Understanding Termination Handlers by Example(2)

2018年04月10日 ⁄ 综合 ⁄ 共 6424字 ⁄ 字号 评论关闭

Funcfurter1

Now let's look at another scenario in which termination handling really proves its value. Look at this function:

DWORD Funcfurter1() {
   DWORD dwTemp;

   // 1. Do any processing here.
   ...
   __try {
      // 2. Request permission to access
      //    protected data, and then use it.
      WaitForSingleObject(g_hSem, INFINITE);

      dwTemp = Funcinator(g_dwProtectedData);
   }
   __finally {
      // 3. Allow others to use protected data.
      ReleaseSemaphore(g_hSem, 1, NULL);
   }

   // 4. Continue processing.
   return(dwTemp);
}

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?

DWORD FuncaDoodleDoo() {
   DWORD dwTemp = 0;

   while (dwTemp < 10) {

      __try {
         if (dwTemp == 2)
            continue;

         if (dwTemp == 3)
            break;
      }
      __finally {
         dwTemp++;
      }

      dwTemp++;
   }

   dwTemp += 10;
   return(dwTemp);
}

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中的代码将不会执行。

抱歉!评论已关闭.