Click here to Skip to main content
15,885,156 members
Articles / Desktop Programming / Win32

Win32 Exceptions – OS Level Point of View

Rate me:
Please Sign up or sign in to vote.
4.71/5 (18 votes)
21 May 2010CPOL8 min read 52K   602   36   2
Describes Win32 exception layering and discusses Structured Exception Handling (SEH) on OS level and on C++ compiler level.

Introduction

One of the widely used and not well documented feature of 32-bit Windows Operating Systems is Structured Exception Handling. This common exception service provided by the Operating Systems is used by C++ compilers.

This article describes the Win32 exception layering, and discusses structured exceptions handling (SEH) on OS level and on C++ compiler level.

Describing exceptions from the OS level point of view helps to understand the functionality and the performance cost of exceptions. Then, it will not be difficult to reveal how it is possible to mix SEH and C++ exceptions, and how it is possible to catch SEH in C++ exceptions, which could be the main motivation to read this article.

The best way to understand the topic is to play with the attached examples.

Win32 Exception Layering

The following diagram shows how the common exception service provided by the OS (OS Level SEH) is used by C++ compilers for structured exceptions handling (C++ Compiler Level SEH) and for well-known C++ exceptions (C++ Exceptions Handling).

Image 1

It is important to understand that both C++ Compiler Level SEH and C++ Exception Handling use the same OS Level SEH.

OS Level SEH

The common exception service provided by OS handles:

  • Software (Synchronous) Exceptions - explicitly passes control to the Operating System through software interrupt.
  • Hardware (Asynchronous) Exceptions - e.g., access violation, integer division by 0, illegal instruction, etc...

Exception Callback Function

Whenever some exception occurs, the OS calls the Exception Callback Function within the current thread context.

This function can be defined by the user and the prototype is as follows (defined in the excpt.h header):

C++
EXCEPTION_DISPOSITION __cdecl _except_handler
(
  struct _EXCEPTION_RECORD* _ExceptionRecord,
  void*                     _EstablisherFrame,
  struct _CONTEXT*          _ContextRecord,
  void*                     _DispatcherContext
);

According to the returned value, the OS will perform a certain action (defined in excpt.h):

C++
typedef enum _EXCEPTION_DISPOSITION
{
  ExceptionContinueExecution, // tells OS to restart faulting instruction
  ExceptionContinueSearch,    // tells OS to continue Exception
                              // Callback Function searching
  ExceptionNestedException,
  ExceptionCollidedUnwind
} EXCEPTION_DISPOSITION;

The first parameter _ExceptionRecord describes the exception (defined in WinNT.h):

C++
typedef struct _EXCEPTION_RECORD
{
  DWORD     ExceptionCode;    // which exception occurred
  DWORD     ExceptionFlags;   // additional exception info
  struct _EXCEPTION_RECORD *ExceptionRecord;
  PVOID     ExceptionAddress; // where the exception occurred
  DWORD     NumberParameters;
  ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;

The third parameter _ContextRecord is a pointer to the CONTEXT structure which represents the register values of a particular thread at the time of the exception. This structure is defined in WinNT.h, and it is the same structure which is used for the Get/SetThreadContext API methods.

Exception Callback Function Registration

The Operating System stores the registered Exception Callback Functions in a linked list of EXCEPTION_REGISTRATION_RECORD structures:

C++
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
  struct _EXCEPTION_REGISTRATION_RECORD* prev;
  DWORD                                  handler;
} _EXCEPTION_REGISTRATION_RECORD;

The element prev points to the previous record in the linked list, or is 0xFFFFFFFF for the last record. The element handler is a pointer to an _except_handler callback function.

The linked list of Exception Callback Functions is registered in the Thread Information Block (TIB) defined in WinNT.h:

C++
typedef struct _NT_TIB
{
  struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
  ...
} NT_TIB;

On the Intel Win32 platform, the register FS always points to the TIB of the current thread. Thus, at FS:[0] is the pointer ExceptionList (see the following diagram).

Image 2

Exception Callback Function Searching

When an exception occurs, the Operating System walks the linked list of registered exception callback functions, and searches for the callback which agrees to handle the exception (which does not return ExceptionContinueSearch).

The Operating System stores at the end of the linked list the default callback which displays the general-protection-fault message box. Thus, if the user does not change the end of the linked list, the searching stops on the default callback.

If the Operating System does not find any callback for the exception, it terminates the application.

Example 1

Example 1 registers its own Exception Callback Functions using only the common exception service provided by the Operating System. The first one does not handle any exception, and always returns the value which tells the OS to go to the next exception handler. The second one handles an access violation exception and corrects bad writing address and tells the OS to restart the failing instruction.

This example further shows searching of the callback which agrees to handle the exception, and shows the continuation of the execution after the exception correction.

C++ Compiler Level SEH

The C++ Compiler Level SEH is a compiler wrapper around the OS Level SEH.

The Microsoft C++ compilers define the new keywords __try, __finally, and __except to handle SEH. These keywords work for both C and C++ source files.

A very important thing to note for C++ code with compiler level SEH is that the compiler does not care about C++ object destructors.

C++ SEH Exception Callback

The C++ SEH Exception Callback is the C++ compiler implementation of the Exception Callback Function for SEH. It calls the C/C++ user code (called exception filter) to check if the exception should be handled or not. If an exception should not be handled, it returns the processing back to the OS. If an exception should be handled, it never returns back to the OS.

If the user code decides that the exception should be handled, the C++ SEH Exception Callback Function:

  • executes exception unwind
  • calls the C/C++ user code (called except block) to handle the exception
  • cleans up the stack and sets the frame pointers
  • transfers control back to the C/C++ user code immediately after the except block

Exception Unwind

During exception unwind, the C++ SEH Exception Callback Function:

  • walks the linked list of registered callbacks again, up to itself
  • calls each callback the second time with different exception flags (EXCEPTION_UNWINDING)

During this second traversal, the callbacks call the C/C++ user code (called finally block) allowing to do cleanup (e.g., releasing of resources).

Try-except Block

To handle C++ Compiler Level SEH exceptions, the try-except block is used:

C++
__try
{
  // guarded body of code
}
__except (filter_expression)
{
  // exception handler block
}

The __try block is protected by the C++ SEH Exception Callback Function which is registered by EXCEPTION_REGISTRATION_RECORD allocated on the stack.

__except defines an except block (called exception handler as well).

filter_expression defines an exception filter which is any C expression or even function call having the following integer result:

  • EXCEPTION_CONTINUE_EXECUTION - continue execution where exception occurred
  • EXCEPTION_CONTINUE_SEARCH - continue searching in the callback linked list
  • EXCEPTION_EXECUTE_HANDLER - exception has been recognized and continue with the execution of the exception handler

Try-finally Block

To handle user cleanup when an exception occurs, the try-finally block is used:

C++
__try
{
  // guarded body of code
}
__finally
{
  // termination handler block
}

__finally defines a finally block (called termination handler as well).

In case of no exception, the code in the finally block will be executed as well.

SEH API Functions

The Win32 supports API functions for SEH. The following is a quick overview of the most used functions:

  • GetExceptionCode() - Returns a code that identifies the exception. This function can be called only from the filter expression or the exception handler block.
  • GetExceptionInformation() - Returns a pointer to the exception record structure and a pointer to the exception context structure. This function can be called only from the filter expression.
  • AbnormalTermination() - Indicates whether the try-block of a termination handler terminated normally. This function can be called only from the termination handler.
  • RaiseException() - Raises an exception in the calling thread (creates a software synchronous exception).

Example 2

Example 2 demonstrates:

  • the handling of access violation exception by the C++ compiler level SEH
  • usage of the try-except block
  • sample implementation of the exception filter
  • usage of the try-finally block

C++ Exception handling (C++ EH)

C++ Exception Handling is the C++ compiler wrapper around the OS Level SEH. C++ compilers defined the new keywords try, catch, and throw to handle C++ exceptions.

The big advantage of C++ code compared to the compiler level SEH is that C++ EH properly calls the destructors.

The C++ EH is very well described in many C++ language books. Therefore, we will not describe C++ EH in more details here.

Mixing SEH and C++ EH

It is important to understand that it is not possible to mix SEH and C++ EH in one C++ method, because both register their own Exception Callback Function. However, it is possible to mix SEH and C++ EH in different C++ methods. But be very careful about C++ object destructors, SEH does not call them!

The second important thing is that SEH cannot handle C++ exceptions because SEH comes from the C world. However, C++ EH can handle SEH. But again, be very careful because this feature is compiler dependent, and it is usually supported by a special compiler switch. Furthermore, this feature has a performance impact because the compiler must trace each C++ object to call its destructor.

Consider the following C++ construction:

C++
try
{
  ...
}
catch (...)
{
}

The question is: does this C++ block catch an SEH exception? The answer depends on the compiler:

  • VC++ 6.0 - yes (by default)
  • VC++ 8.0 - no (by default), yes with /EHa
  • EVC 4.0 - no (by default), yes with /EHa

But, a C++ try-catch block cannot distinguish a Structured Exception type. To do so, you must use the try-except block from SEH, or transform the Structured Exception to a C++ typed exception using the _set_se_translator() API method (not supported by WinCE).

Conclusion

This article shows how layers of exception handling implementation are stacked one upon others, and what are the common points between OS Level SEH, C++ Compiler Level SEH, and C++ EH.

Understanding the low level details is a key point for proper judging of program behaviour and its performance during exception raising and during exception mixing.

Acknowledgement and Links

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Eccam s.r.o.
Czech Republic Czech Republic
Senior Sofware Developer focused on embedded systems.
Company website: http://www.eccam.com

Comments and Discussions

 
QuestionMy vote of 5 Pin
Manikandan106-Jun-14 21:46
professionalManikandan106-Jun-14 21:46 
Questionnice article Pin
梅文龙20-Jan-14 22:23
梅文龙20-Jan-14 22:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.