Visual C++ 6 Unleashed

Visual C++ 6 Unleashed

By MICKEY WILLIAMS and David Bennett

Structured Exception Handling

Structured exception handling is a way to handle exceptions within a program. It is a mechanism for handling both hardware and software exceptions. An exception is an event (most likely, an error) that occurs while the program is running. To prevent (or at least try to prevent) the program from crashing, the event may be captured, processed, reported, and hopefully handled properly so that the program can continue in a workable state. If exceptions are handled successfully, it allows for consistent reliable applications.

Note that although you can use SEH with C++, C++ exception handling should be used for C++, and MFC exception handling should be used for MFC programs. The SEH covered in this chapter is designed mainly for C programs written for Windows NT or Windows 95.

There are two types of exceptions: hardware exceptions and software exceptions. Hardware exceptions are triggered by the CPU. These exceptions usually are caused by illegal operations such as dividing by zero, floating-point overflow, disk read errors, null pointer accesses, array subscript range errors, and out-of-bounds memory addressing. Software exceptions always result from the operating system or the programs themselves, such as array subscript range errors, null pointer accesses, or invalid parameter specified.

Structured exception handling manages both hardware exceptions and software exceptions in the same manner. This makes it easier to handle exceptions, because either exception can be handled in the same manner. The advantage of this capability is that code used for recovery can remain the same without having to satisfy two different types of exceptions.

The Microsoft C/C++ Optimizing Compiler supports three major keywords to help use SEH:

__try Identifies a guarded body of code
__except Identifies an exception handler
__finally Identifies a termination handler

The Structured Exception Method

The old way of handling exceptions was to pass error codes. A function would detect an error and pass that error to its calling function. This error then would be passed around until it reached a function that could properly handle it. What made this method weak was that the whole function would fail if there was a break in the series of function calls; therefore, the error code was unable to surface, and the error could never be handled properly.

Structured exception handling is different and more reliable in that once an exception handler is installed, the exception can be handled regardless of how many other functions are called.

The structure for the exception handlers follows:

__try {

guarded-code-block-1

}

__except ( filter ){

guarded-code-block-2

}

The statements that would exist in guarded-code-block-1 would be executed unconditionally. The exception handler defined by the filter and guarded-code-block-2 becomes the current exception handler.

If an exception occurs while guarded-code-block-1 is being executed, control is given to the current exception handler.

  __try {
    int number1 = 0;
    int number2 = 0;
    number1 = 100 / number2;   //This is the exception to be handled.
      }
   __except( GetExceptionCode() == STATUS_INTEGER_DIVIDE_BY_ZERO )
     {
      printf( "Error: Divide By Zero!");
     }

One limitation on using exception handlers is that you cannot use a goto statement to jump into a __try code block. The block must always be entered through the normal flow of control. You may jump out of a __try statement block, however.

A Look at Software Exceptions

You raise software exceptions by calling the RaiseException function. Keep in mind that you may treat any condition as an exception by reporting the condition to the RaiseException function. You can flag any type of runtime error this way.

To perform exception reporting with software errors, you must do the following:

  1. Define the exception code for the event.
  2. Call the RaiseException function when the error is detected.
  3. Use exception-handling filters for the defined exception codes.

You can find the format for exception codes in the WINERROR.H header file. When defining your own exception code, make sure to set the third-most-significant bit to 1.

Setting the fourth-most-significant bits is a little more involved. Bits 31 to 30 define the basic status of the code. Bit 00 = success, 11 = error, 01 = informational, and 10 = warning. Bit 29 is the client bit and should be set to 1 for user-defined codes. Bit 28 is a reserved bit and should be set to 0.

Defined exception codes that do not conflict with the exceptions codes of Windows NT may look like the following:

#define STATUS_BAD_ERROR_01 0xE0000001

#define STATUS_BAD_ERROR_01 0xE0000001

#define STATUS_BAD_ERROR_01 0xE0000001

Testing your defined exception is quite simple and may look like the following:

__try {
       //Guarded code is goes here
      }
__except (GetExceptionCode() == STATUS_BAD_ERROR_01) ||
          GetExceptionCode() == STATUS_BAD_ERROR_02) ||
          GetExceptionCode() == STATUS_BAD_ERROR_03))

A Look at Hardware Exceptions

Hardware exceptions make up the majority of the standard exceptions. The hardware exceptions that Windows NT recognizes follow:

Most of the exceptions in the preceding list are intended to be handled by debuggers, low-level code, or the operating system itself. The only types of code you should be handling are the integer and floating-point errors. The most likely line of action for the other exceptions is to ignore the exceptions and evaluate them to zero. By not doing so, lower-level mechanisms may not respond to the exception error as they should.

Structured Exception Handling Functions

The following functions are used in SEH:

You'll examine these functions in the following sections.

AbnormalTermination

The AbnormalTermination function returns zero if the __try block terminated normally and nonzero if the __try block terminated abnormally. This function can be called only from within a __finally block of a termination handler.

GetExceptionCode

The GetExceptionCode function returns a code that identifies the type of exception that occurred. This function may be called only from an exception-handler block of an exception handler or from within the filter expression.

Here is a list of GetExceptionCode return values:

These return values can be passed as a parameter to a filter function.

GetExceptionInformation

The GetExceptionInformation function returns a machine-independent description of an exception, as well as information about the machine state that existed for the thread when the exception occurred.

The return value is a pointer to the structure EXCEPTION_POINTERS. This structure contains pointers to two other structures: EXCEPTION_RECORD and CONTEXT. EXCEPTION_RECORD contains a description of the exception. The CONTEXT structure contains the machine-state information.

RaiseException

The RaiseException function handles private application-defined exceptions that are software-generated.

When an exception is raised, the exception dispatcher goes through the following steps:

  1. If the process is being debugged, the process's debugger is notified.
  2. If the process is not being debugged or the debugger cannot handle the exception, the system searches the stack frames of thread where the exception occurred for a frame-based exception handler. The current stack frame is searched first, and then the previous stack frames are searched.
  3. If no frame-based handler is found, or the frame-based handler cannot handle the exception, a second attempt is made to notify the process's debugger.
  4. If the debugger cannot handle the exception, or the process is not being debugged, the system provides default handling based on the exception type. The default action of most exceptions is to call the ExitProcess function.

The ExitProcess Function

The ExitProcess function terminates a process as well as all its threads. It is the preferred way of ending a processing, because it provides a clean ending of a process.

SetUnhandledExceptionFilter

The SetUnhandledExceptionFilter function allows an application to replace the top-level exception handler that Win32 places at the beginning of each process and thread.

This filter function takes a single parameter type of LPEXCEPTION_POINTERS and returns a value of type LONG. The following list describes the SetUnhandledExceptionFilter return values:

UnhandledExceptionFilter

If the process is being debugged, the UnhandledExceptionFilter function sends unhandled exceptions to the debugger. If the process is not being debugged, the function displays an Application Error message box and executes the exception handler.

The UnhandledExceptionFilter function returns two values:

Remember that SEH is used mainly for C programs, not C++. Although you can use SEH for C++, the next section shows a better method.

Share ThisShare This

Informit Network