A termination handler serves much the same
purpose as an exception handler, but it is executed when a thread leaves
a block as a result of normal program flow as well as when an exception
occurs. On the other hand, a termination handler cannot diagnose an
exception.
Construct a termination handler using the __finally
keyword in a try-finally statement. The structure is the same as for a
try-except statement, but there is no filter expression. Termination
handlers, like exception handlers, are a convenient way to close
handles, release resources, restore masks, and otherwise restore the
process to a known state when leaving a block. For example, a program
may execute return statements in the middle of a block, and the
termination handler can perform the cleanup work. In this way, there is
no need to include the cleanup code in the code block itself, nor is there a need for goto or other control flow statements to reach the cleanup code.
__try {
/* Code block. */
}
__finally {
/* Termination handler (finally block). */
}
Leaving the Try Block
The termination handler is executed whenever the control flow leaves the try block for any of the following reasons:
Abnormal Termination
Termination for any reason other than reaching the end of the try block and falling through or performing a __leave statement is considered an abnormal termination. The effect of __leave is to transfer to the end of the __try block and fall through. Within the termination handler, use this function to determine how the try block terminated.
BOOL AbnormalTermination (VOID)
|
The return value will be TRUE for an abnormal termination or FALSE for a normal termination.
Note
The termination would be abnormal even if, for example, a return statement were the last statement in the try block.
Executing and Leaving the Termination Handler
The termination handler, or __finally
block, is executed in the context of the block or function that it
monitors. Control can pass from the end of the termination handler to
the next statement. Alternatively, the termination handler can execute a
flow control statement (return, break, continue, goto, longjmp, or __leave). Leaving the handler because of an exception is another possibility.
Combining Finally and Except Blocks
A single try block must have a single finally or
except block; it cannot have both, even though it might be convenient.
Therefore, the following code would cause a compile error.
__try {
/* Block of monitored code. */
}
__except (filter_expression) {
/* Except block. */
}
__finally {
/* Do not do this! It will not compile. */
}
It is possible, however, to embed one block
within another, a technique that is frequently useful. The following
code is valid and ensures that the temporary file is deleted if the loop
exits under program control or because of an exception. This technique
is also useful to ensure that file locks are released. There is also an
inner try-except block with some floating-point processing.
__try { /* Outer try-except block. */
while (...) __try { /* Inner try-finally block. */
hFile = CreateFile(tempFile, ...);
if (...) __try { /* Inner try-except block. */
/* Enable FP exceptions. Perform computations. */
...
}
__except (fp-filter-expression) {
... /* Process FP exception. */ _clearfp();
}
... /* Non-FP processing. /*
}
__finally { /* End of while loop. */
/* Executed on EVERY loop iteration. */
CloseHandle(hFile); DeleteFile(tempFile);
}
}
__except (filter-expression) {
/* Exception handler. */
}
Global and Local Unwinds
Exceptions and abnormal terminations will cause a global stack unwind to search for a handler.
For example, suppose an exception occurs in the monitored block of the
example at the end of the preceding section before the floating-point
exceptions are enabled. The termination handler will be executed first,
followed by the exception handler at the end. There might be numerous
termination handlers on the stack before the exception handler is
located.
Recall that the stack structure is dynamic, and that it contains, among other things, the exception and termination handlers. The contents at any time depend on:
Termination Handlers: Process and Thread Termination
Termination handlers do not execute if a process or thread terminates, whether the process or thread terminates itself by using ExitProcess or ExitThread, or whether the termination is external, caused by a call to TerminateProcess or TerminateThread
from elsewhere. Therefore, a process or thread should not execute one
of these functions inside a try-except or try-finally block.
Notice also that the C library exit function or a return from a main function will exit the process.
SEH and C++ Exception Handling
C++ exception handling uses the keywords catch and throw
and is implemented using SEH. Nonetheless, C++ exception handling and
SEH are distinct. They should be mixed with care, or not at all, because
the user-written and C++-generated exception handlers may interfere
with expected operation. For example, an __except handler may
be on the stack and catch a C++ exception so that the C++ handler will
never receive the exception. The converse is also possible, with a C++
handler catching, for example, an SEH exception generated with RaiseException.
The Microsoft documentation recommends that Windows exception handlers
not be used in C++ programs at all but instead that C++ exception
handling be used exclusively.
Normally, a Windows exception or termination handler will not call destructors to destroy C++ object instances. However, the /EHa
compiler flag (setable from Visual Studio) allows C++ exception
handling to include asynchronous exceptions and “unwind” (destroy) C++
objects.