PRB: SEH with return in the finally Body Preempts Unwind

ID: Q91147


The information in this article applies to:
  • Microsoft Win32 Application Programming Interface (API), included with:
    • Microsoft Windows NT, versions 3.1, 3.5, 3.51
    • Microsoft Windows 95


SYMPTOMS

When using Structured Exception Handling (SEH), returning out of a finally body results in a return from the containing procedure scope. For example, in the following code fragment, the return in the finally block results in a return from func():


   int func()
   {
       int status = 0;
       __try {
          ...
          status = test();
          ...
       }
       __finally {
          if (status != 0) {
             status = FAILURE;
             return status;
          }
       }
       return status;
   } 


CAUSE

A return from within a __finally is equivalent to a goto to the closing brace in the enclosing function [for example, func()]. This is allowed, but has consequences that should normally be avoided.

Exception handling has two stages. First, the exception stack is walked, looking for an accepting __except. When an accepting handler has been found, all __finallys between the top-of-exception-stack and the target __except will be called. During this "unwind", the __finallys are assumed to each execute and then return to their caller (the system unwind code).

A return in a finally abnormally aborts this unwinding. Instead of returning to the system unwinder, the __finally returns to the enclosing function's caller [for example, func()'s parent]. The accepting __except filter may set some status or perform an allocation in anticipation of the __except handler being entered. In this case, the intervening __finally with the return will stop the unwind, and the __except handler is never entered.


RESOLUTION

This is by design. It makes it possible for a finally handler to stop an unwind and return a status. This is what is referred to as a collided unwind.

Abnormal termination from try/except or try/finally blocks is not generally recommended because it is a performance hit.

The example can be rewritten so that the unwind chain is not aborted:


   int func()
   {
      int status = 0;
      __try {
         ...
         status = test();
         ...
      }
      __except(status != 0) {

          /* null */ 
      }
      if (status != 0)
         status = FAILURE;
      return status;
   }
  
This does not have identical semantics because the exception filters higher up the exception stack will not be executed. However, ensuring that both phases of exception handling progress to the same depth is a more robust solution.


MORE INFORMATION

Normally this behavior is transparent to any higher-level exception handling code. If, however, a filter function, as a side effect, stores information that it expects to process in an exception handler, then it may or may not be transparent. Storing such information in a filter function should be avoided because it is always possible that the exception handler will not be executed because the unwind is preempted. In the absence of storing such side effects, it will be transparent that an exception occurred and an attempted unwind occurred if one of the descendent functions has a try/finally block with an finally clause that preempts the unwind.

Additional query words: 3.10 3.50 4.00

Keywords : kbNTOS310 kbNTOS350 kbNTOS351 kbWinOS95
Version :
Platform :
Issue type :


Last Reviewed: September 23, 1999
© 2000 Microsoft Corporation. All rights reserved. Terms of Use.