PRB: SEH with return in the finally Body Preempts Unwind (91147)
The information in this article applies to:
- Microsoft Win32 Application Programming Interface (API), when used with:
- the operating system: Microsoft Windows NT 3.1
- the operating system: Microsoft Windows NT 3.5
- the operating system: Microsoft Windows NT 3.51
This article was previously published under Q91147 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.
Modification Type: | Minor | Last Reviewed: | 12/3/2003 |
---|
Keywords: | KB91147 |
---|
|