Exceptions and Their Handlers
Without some form of exception handling, an unintended program exception, such as dereferencing a NULL pointer or division by zero, will terminate a program immediately. This could be a problem, for example, if the program has created a temporary file that should be deleted before program termination. SEH allows specification of a code block, or exception handler, that can delete the temporary file when an exception occurs.SEH is supported through a combination of Windows functions, language support provided by the compiler, and run-time support. The exact language support may vary; the examples here were all developed for Microsoft C.
Try and Except Blocks
Start by determining which code blocks to monitor and provide them with exception handlers, as described next. It is possible to monitor an entire function or to have separate exception handlers for different code blocks and functions.A code block is a good candidate for an exception handler in situations that include the following.
- Detectable errors, including system call errors, might occur, and you need to recover from the error rather than terminate the program.
- Pointers are used extensively, so there is a possibility of dereferencing pointers that have not been properly initialized.
- There is extensive array manipulation, because it is possible for array indices to go out of bounds.
- The code performs floating-point arithmetic, and there is concern with zero divides, imprecise results, and overflows.
- The code calls a function that might generate an exception, either intentionally or because the function has not been well tested.
In the examples in this chapter and throughout the book, once you have decided to monitor a block, create the try and except blocks as follows:
__try {
/* Block of monitored code */
}
__except (filter_expression) {
/* Exception handling block */
}
Notice that __TRy and __except are keywords recognized by the compiler.The try block is part of normal application code. If an exception occurs in the block, the OS transfers control to the exception handler, which is the code in the block associated with the __except clause. The actions that follow are determined by the value of the filter_expression.Notice that the exception might occur within a block embedded in the try block, in which case the run-time support "unwinds" the stack to find the exception handler and then gives control to the handler. The same thing happens when an exception occurs within a function called within a try block.Figure 4-1 shows how an exception handler is located on the stack when an exception occurs. Once the exception handler block completes, control passes to the next statement after the exception block unless there is some other control flow statement in the handler.
Figure 4-1. SEH, Blocks, and Functions
[View full size image]

Filter Expressions and Their Values
The filter_expression in the __except clause is evaluated immediately after the exception occurs. The expression is usually a literal constant, a call to a filter function, or a conditional expression. In all cases, the expression should return one of three values.
- EXCEPTION_EXECUTE_HANDLER
The system executes the except block as shown in Program 4-1). This is the normal case. - EXCEPTION_CONTINUE_SEARCH
The system ignores the exception handler and searches for an exception handler in the enclosing block, continuing until it finds a handler. - EXCEPTION_CONTINUE_EXECUTION
The system immediately returns control to the point at which the exception occurred. It is not possible to continue after some exceptions, and another exception is generated immediately if the program attempts to do so.
GetTempFileName (TempFile, ...);
while (...) __try {
hFile = CreateFile (TempFile, ..., OPEN_ALWAYS, ...);
SetFilePointer (hFile, 0, NULL, FILE_END);
...
WriteFile (hFile, ...);
i = *p; /* An addressing exception could occur. */
...
CloseHandle (hFile);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
CloseHandle (hFile);
DeleteFile (TempFile);
/* The loop will now execute the next iteration .*/
}
/* Control passes here after normal loop termination.
The file handle is always closed and the temp file
will not exist if an exception occurred. */
The logic of this code fragment is as follows.
- Each loop iteration appends new data to the end of the temporary file.
- If an exception occurs in any loop iteration, all data accumulated in the temporary file is deleted, and the next iteration, if any, starts to accumulate data in the temporary file again.
- If an exception occurs on the last iteration, the file will not exist. In any case, the file will contain all data generated since the last exception.
- The example shows just one location where an exception could occur, although the exception could occur anywhere within the loop body.
- The file handle is assured of being closed when exiting the loop or starting a new loop iteration.
Exception Codes
The except block or the filter expression can determine the exact exception using this function:
DWORD GetExceptionCode (VOID)
The exception code must be obtained immediately after an exception. Therefore, the filter function itself cannot call GetExceptionCode (the compiler enforces this restriction). A common usage is to invoke it in the filter expression, as in the following example, where the exception code is the argument to a user-supplied filter function.
__except (MyFilter (GetExceptionCode ())) {
}
In this situation, the filter function determines and returns the filter expression value, which must be one of the three values enumerated earlier. The function can use the exception code to determine the function value; for example, the filter may decide to pass floating-point exceptions to an outer handler (by returning EXCEPTION_CONTINUE_SEARCH) and to handle a memory access violation in the current handler (by returning EXCEPTION_EXECUTE_HANDLER).A large number of possible exception code values can be returned by GetExceptionCode, and the codes are in several categories.
- Program violations such as the following:
- -
EXCEPTION_ACCESS_VIOLATION
An attempt to read or write a virtual address for which the process does not have access. - -
EXCEPTION_DATATYPE_MISALIGNMENT
Many processors insist, for example, that DWORDs be aligned on four-byte boundaries. - -
EXCEPTION_NONCONTINUABLE_EXECUTION
The filter expression was EXCEPTION_CONTINUE_EXECUTION, but it is not possible to continue after the exception that occurred.Chapter 5). The value will be either STATUS_NO_MEMORY or EXCEPTION_ACCESS_VIOLATION.
- -
- A user-defined exception code generated by the RaiseException function, which is explained in the User-Generated Exceptions subsection.
A large variety of arithmetic (especially floating-point) codes such as EXCEPTION_INT_DIVIDE_BY_ZERO and EXCEPTION_FLT_OVERFLOW. - Exceptions used by debuggers, such as EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP.
The GetExceptionInformation function is an alternative function, callable only from within the filter expression, which returns additional information, some of which is processor-specific.
LPEXCEPTION_POINTERS GetExceptionInformation (VOID)
The EXCEPTION_POINTERS structure contains both processor-specific and processor-independent information organized into two other structures.
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS;
EXCEPTION_RECORD contains a member for the ExceptionCode, with the same set of values as returned by GetExceptionCode. The ExceptionFlags member of the EXCEPTION_RECORD is either 0 or EXCEPTION_NONCONTINUABLE, which allows the filter function to determine that it should not attempt to continue execution. Other data members include a virtual memory address, ExceptionAddress, and a parameter array, ExceptionInformation. In the case of EXCEPTION_ACCESS_VIOLATION, the first element indicates whether the violation was a memory write (1) or read (0). The second element is the virtual memory address.ContextRecord, the second EXCEPTION_POINTERS member, contains processor-specific information. There are different structures for each type of processor, and the structure can be found in <winnt.h>.
Summary: Exception Handling Sequence
Figure 4-2 shows the sequence of events that takes place when an exception occurs. The code is shown on the left side, and the circled numbers on the right show the steps carried out by the language run-time support. The steps are as follows.
1. | The exception occurs, in this case a division by zero. |
2. | Control transfers to the exception handler, where the filter expression is evaluated. GetExceptionCode is called first, and its return value is the argument to the function Filter. |
3. | The filter function bases its actions on the exception code value. |
4. | The exception code is EXCEPTION_INT_DIVIDE_BY_ZERO in this case. |
5. | The filter function determines that the exception handler should be executed, so the return value is EXCEPTION_EXECUTE_HANDLER. |
6. | The exception handler, which is the code associated with the __except clause, executes. |
7. | Control passes out of the try-except block. |
Figure 4-2. Exception Handling Sequence
[View full size image]
