PRB: SEH with return in the finally Body Preempts Unwind

Last reviewed: November 2, 1995
Article 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, and 3.51
        - Microsoft Windows 95 version 4.0
    

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 reference words: 3.10 3.50 4.00 95
KBCategory: kbprg kbprb
KBSubcategory: BseExcept


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: November 2, 1995
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.