Structured Exception Handling

An exception is an event that occurs during the execution of a program, and requires the execution of code outside the normal flow of control.

Structured exception handling is a type of exception handling that allows applications to gain control when exceptions occur. The application can then fix the condition and return to the place the exception occurred, unwind the stack (thus terminating execution of the subroutine that raised the exception), or declare back to the system that the exception isn't recognized, and continue searching for an exception handler that might process the exception.

The system also supports termination handling, which enables you to ensure that whenever a guarded body of code is executed, a specified block of termination code (eg. clean-up tasks) is also executed, regardless of how the flow of control leaves the guarded body.

Structured exception handling is made available primarily through compiler support. For example, the Microsoft C/C++ Optimizing Compiler supports the __try keyword that identifies a guarded body of code, the __except keyword that identifies an exception handler, and the __finally keyword that identifies a termination handler.

Exception Handling

Exceptions can be initiated by hardware or software, and can occur in kernel-mode as well as user-mode code. Hardware exceptions are initiated by the CPU such as division by zero or an attempt to access an invalid memory address. Software exceptions are initiated explicitly by applications or the operating system. For example, the system can detect when an invalid parameter value is specified. A thread can initiate an exception explicitly by calling the RaiseException function.

An exception can be continuable or noncontinuable. A noncontinuable exception arises when the event is not continuable in the hardware, or if continuation makes no sense. A noncontinuable exception does not terminate the application. Therefore, an application may be able to catch the exception and run. However, a noncontinuable exception typically arises as a result of a corrupted stack or other serious problem, making it difficult to recover from the exception.

Context Record - Machine state of the thread. Contains processor-specific register data. It enables the system to continue execution at the point of the exception if the exception is successfully handled. Refer to the header file WinNT.h

Exception record - Description of the exception.

The information in both the context and exception records is available by means of the GetExceptionInformation function.

When an exception occurs in user-mode code, the system uses the following search order to find an exception handler:

    1. First-chance notification. If the process is being debugged, the system notifies the debugger. For more information, see Debugger Exception Handling.

    2. If the process is not being debugged, or if the associated debugger does not handle the exception, the system attempts to locate a frame-based exception handler by searching the stack frames of the thread in which the exception occurred. The system searches the current stack frame first, then searches through preceding stack frames in reverse order.

    3. Last-chance notification. If no frame-based handler can be found, or no frame-based handler handles the exception, but the process is being debugged, the system notifies the debugger a second time.

    4. If the process is not being debugged, or if the associated debugger does not handle the exception, the system provides default handling based on the exception type. For most exceptions, the default action is to call the ExitProcess function.

When an exception occurs in kernel-mode code, the system searches the stack frames of the kernel stack in an attempt to locate an exception handler. If a handler cannot be located or no handler handles the exception, the system is shut down as if the ExitWindows function had been called.

The system's handling of user-mode exceptions provides support for sophisticated debuggers. If the process in which an exception occurs is being debugged, the system generates a debug event. If the debugger is using the WaitForDebugEvent function, the debug event causes that function to return with a pointer to a DEBUG_EVENT structure. This structure contains the process and thread identifiers the debugger can use to access the thread's context record. The structure also contains an EXCEPTION_DEBUG_INFO structure that includes a copy of the exception record.

At each notification attempt, the debugger uses the ContinueDebugEvent function to return control to the system. Before returning control, the debugger can handle the exception and modify the thread state as appropriate, or it can choose not to handle the exception. Using ContinueDebugEvent, the debugger can indicate that it has handled the exception, in which case the machine state is restored and thread execution is continued at the point at which the exception occurred. The debugger can also indicate that it did not handle the exception, which causes the system to continue its search for an exception handler.

Frame Based Exception Handling

A frame-based exception handler allows you to deal with the possibility that an exception may occur in a certain sequence of code. A frame-based exception handler consists of the following elements.

    • A guarded body of code

    • A filter expression

    • An exception-handler block

You can call the GetExceptionCode and GetExceptionInformation functions from within a filter expression to get information about the exception being filtered. These functions cannot be called from within a filter function, but their return values can be passed as parameters to a filter function.

The UnhandledExceptionFilter function can be used as a filter function in a filter expression. It returns EXCEPTION_EXECUTE_HANDLER unless the process is being debugged, in which case it returns EXCEPTION_CONTINUE_SEARCH.

Vectored exception handlers are an extension to structured exception handling. An application can register a function to watch or handle all exceptions for the application. Vectored handlers are not frame-based, therefore, you can add a handler that will be called regardless of where you are in a call frame.

Exception Handler

The exception handler receives only exceptions that occur within a single thread. This means that if a __try block contains a call to the CreateProcess or CreateThread function, exceptions that occur within the new process or thread are not dispatched to this handler.

The system evaluates the filter expression of each exception handler guarding the code in which the exception occurred until either the exception is handled or there are no more handlers. A filter expression must be evaluated as one of the three following values.

Termination Handler

The __finally block is not executed if you call any of the following functions within the __try block: ExitProcess, ExitThread, or abort.

Execution of the __finally block can terminate by any of the following means.

    • Execution of the last statement in the block and continuation to the next instruction

    • Use of a control statement (return, break, continue, or goto)

    • Use of longjmp or a jump to an exception handler

The AbnormalTermination function can be used within the __finally block to determine whether the __try block terminated sequentially. Leaving the __try block because of a call to longjmp, or a return, break, continue, or goto statement, is considered an abnormal termination. The __leave statement allows for immediate termination of the __try block without causing abnormal termination and its performance penalty.