Visual C++ exception handling

Solution overview

So the basic problem is this. In a pre-2005 Visual C++ program with default settings, Win32 exceptions can sometimes be caught with catch(...) and will sometimes fail to unwind the stack properly. Here I describe three solutions to this, each requiring different actions on the part of the programmer. They are:

1. Don’t catch Win32 exceptions. If you never catch Win32 exceptions, they will always cause the program to crash. This is perhaps the most obvious result, and is how most computer programs work. When a Win32 exception occurs, you will be able to use your normal debugging tools to isolate and correct the error that led to the exception.

2. Ensure the stack is unwound correctly when catching Win32 exceptions. This approach ensures that Win32 exceptions are always caught by catch(...) and will unwind the stack properly when caught. The disadvantage here is that no information about the exception is available at the point where it is caught.

3. Turn Win32 exceptions into C++ exceptions. This approach fully integrates Win32 exceptions into the normal C++ exception handling mechanism. When a Win32 exception occurs, it is automatically turned into a rich C++ exception object and handled according to the normal C++ exception rules. This is perhaps the most elegant approach, but it requires the most work on the part of the programmer.

Solution 1: Don’t catch Win32 exceptions

If you never catch Win32 exceptions, they will always cause the program to crash. This is perhaps the most obvious result, and is how most computer programs work. In Visual C++ 2005, you can’t catch Win32 exceptions directly, so there’s no problem. However, in pre-2005 Visual C++, you must avoid using catch(...) anywhere.

The problem with this is, of course, that catch(...) can be quite useful. Even when the developer knows which types of exception can be thrown, and therefore writes:

catch (exceptionBase&) {
    // clean up
    throw;
}

It may better express the programmer’s intention to write this instead:

catch (...) {
    // clean up
    throw;
}

In other words, “if any exception occurs, clean up and rethrow it”. It’s a shame to have to avoid this.

Solution 2: Ensure the stack is unwound correctly

In pre-2005 Visual C++, it is possible to ensure that Win32 exceptions are always caught by catch(...) and will unwind the stack properly. However, no information about the exception will be available. This happens if you compile with the “asynchronous” exception-handling model.

In the asynchronous exception-handling model, the compiler assumes that any code can cause an exception. This is actually (more-or-less) true of any pre-2005 Visual C++ code that has a catch(...) handler, since catch(...) always catches Win32 exceptions.

To enable the asynchronous exception-handling model, compile with the /EHa compiler switch.

Listing 4: Unwinding the stack

 1  class thing
 2  {
 3  public:
 4      thing() { std::cerr << "hello "  ; }
 5      ~thing() { std::cerr << "goodbye "; }
 6  };
 7
 8  int main() {
 9      try {
10          std::cerr << "try ";
11          thing t1;
12          char* cp = 0;
13          *cp = 'a';
14      }
15      catch (...) {
16          std::cerr << "catch";
17      }
18  }

In Visual C++ 2005, this simply prints “try hello” and then crashes. Nice and simple.

Compiled with the default /GX switch on pre-2005 Visual C++, Listing 4 outputs “try hello catch“. It fails to unwind the stack correctly; the t1 object destructor does not get called. If you compile with /EHa, the stack will unwind properly and you get “try hello goodbye catch” as expected.

Therefore, if a pre-2005 Visual C++ program contains catch(...), then it should be compiled with asynchronous exception handling enabled (/EHa). Otherwise, if the catch(...) catches a Win32 exception, “some of the unwindable objects in the function where the exception occurs may not get unwound” (Visual C++ Programmer’s Guide), which could lead to resource leaks or worse.

Note that this approach requires the compiler to insert complete cleanup code in every try block, even if it could normally determine that no C++ exception can be thrown from within it. This may increase overall code size.

Solution 3: Turn Win32 exceptions into C++ exceptions

Perhaps the best solution, especially in a large program, is to transform Win32 exceptions into C++ objects that can be handled according to the normal C++ exception rules. This approach works equally well, and is equally useful, on Visual C++ 2005 and on earlier versions.

To get this behaviour, you must create your own class and write an exception handler to “catch” Win32 exceptions and throw C++ exceptions. You must also compile with the /EHa compiler option to enable asynchronous exception handling. At runtime, you install your handler by calling the global function _set_se_translator().

Background information on _set_se_translator() is available in Microsoft’s MSDN Library. There’s also a bare-bones example of this approach to exception handling.

The nice thing about this scheme is that the C++ exception object created can contain quite thorough information. For example, it could contain the kind of exception (divide by zero, access violation, etc.), the address at which the exception occurred, and even a stack trace.

So here is the sequence of events when a Win32 exception occurs in a program using this scheme.

  1. The program installs its Win32 exception handler.
  2. The program runs for a while.
  3. At a crucial, deeply-nested point in the program, an access violation occurs.
  4. The Win32 exception handler is called, with details of the exception.
  5. The Win32 exception handler creates a new C++ object, initialising it with detailed information about the exception.
  6. The Win32 exception handler throws the C++ object as a C++ exception.
  7. The call stack unwinds to the nearest catch block for the C++ exception type. All destructors are called correctly, and in general everything is cleaned up as expected.
  8. The catch block has access to full information about the Win32 exception, though the C++ exception. It writes a log message for the programmers, whose debugging task is now much easier.
Share This | Comments | Permalink | Trackback | Comments feed

26 responses

Other comment pages: « 1 [2]

  • nice article,

    could_be_a_bunch_of_rubbish(

    you always can mix old c try - except exception handling with c++ using inline asm, in this way you can guard sections of code wich can only produce win32 exceptions in a safe way and being able to get crash information. This type of exceptions have nothing to do with c++ exceptions so there is no problem.Later you could pack those inline assemblies into macros. In this way it would be very easy, i mean something like this:
    _TRYEXCEPT(handler)
    code
    _END

    );

    that idea come to me from a far place on my memory so.. isnt very reliable, an intuition.. dont hit me if in fact its rubbish :)
    anyway, I need all this things to do some stuff so i have to take a closer look at it, i will post again whenever i have more info ;). happy christmas.

    simkin | 23 December 2005
  • Good article
    Don’t know if you’ve come across this before…
    It would appear that you need to register the handler for each new thread you create. I tried doing it once in main(), but my other threads still showed the default behaviour until I explicitly registered the handler for each one as it started.

    Sandy McPherson | 14 June 2006
  • A very good and helpful article. Solved my problem, thanks for the article. (c) Ashish

    Fischer | 30 June 2006
  • I think you wrong about Listing 3 in “How optimisation affects exception handling” part. In my Visual Studio 2005 both listings give same result: unhandled exception. In a real sense, this additional statement should not affect on exception handling mechanism, so it is.

    Noid | 5 September 2006
  • Noid, this article was originally about VC++ versions 6 and 7, not VC++ 2005. You’re right that the behaviour has changd in VC++ 2005. I have updated the article appropriately. I also fixed some formatting problems, and I now mention threading as suggested by Sandy above.

    Thanks for your help in making the article more useful and up-to-date.

    Bennett | 5 September 2006
  • I’ll never say anything geekier, but your article was an absolute joy to read.

    It will make SEH 10 times easier in the unit testing system I’m working on. Many thanks for taking the time to put this all together.

    Edd | 19 October 2006
  • I have one more question : In the below piece of code, though the exception is caught, the destructor for t1 is not getting called. I know using the /EHa compiler option will solve this. But I don’t want to use this as it adds a overhead and reduces the speed of the program. Is there any other solution for this?

    #include "stdio.h"
    class thing
    {
      public:
          thing() { printf("hello ");  }
          ~thing()       {           printf("goodbye ");       }
    };
    
    int main(int argc, char* argv[])
    {
        try {
          thing t1;
          printf(”try “);
          char* cp = 0;
          *cp = ‘a’;
        }
        catch (…)
        {
            printf(”catch “);
        }
        printf(”exiting “);
        return 0;
    }
    
    Sunil | 9 November 2006
  • stdio.h? printf? You’re obviously a C programmer at heart. :)

    /EHa is the only reliable way to solve this. Anything less will not guarantee that destructors will be called in all situations.

    In the unlikely event that the speed overhead actually makes any measurable difference, you can turn off /EHa for the speed-critical parts of your program. Most programs spend most of their time executing only a few small segments of code. Use a profiler to find out which segments these are, debug them until you’re sure that there won’t be any asynchronous exceptions, and then compile (only) those parts without /EHa.

    Bennett | 10 November 2006
  • “Furthermore, no information will be available about the Win32 exception.”

    This is not true. There is the GetExceptionCode Macro which one can use to determine the type of exception.

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/getexceptioncode.asp

    Markus Klinik | 7 December 2006
  • Hmm. I thought that GetExceptionCode only works within an SEH exception handler. I would be surprised if it works inside a C++ catch(...) block — but then I have been surprised before.

    Bennett | 9 December 2006
  • Yes, you are right. Sorry.

    Markus Klinik | 12 December 2006
  • Good article. Helps me!

    Please provide information about getting stack trace

    Packia | 29 December 2006
  • Packia, as I said: “The EXCEPTION_POINTERS structure that gets passed to win32_exception::translate() contains a lot of other information. In particular, you can use it to get a stack trace…”

    Try looking up EXCEPTION_POINTERS on MSDN Library.

    Bennett | 30 December 2006
  • Thanks for the information. The Microsoft documents are very misleading to an exception handling newbie.

    Jason Powell | 8 February 2007
  • Jason, it can take a bit of investigation and preparation to set everything up, but once you get it working it’s really nice. Now you too can recover from null pointer references, just like all your fancy C# friends!

    Bennett | 8 February 2007
  • Great description of a potentially tricky issue. I rarely read such well written pieces.

    The C++ boost (v 1.33.1) unit testing library pre-built didn’t get built with the ‘async exception model’ and couple of the self-tests broke in a way which I think you’ve just explained for me.

    Also, good font and layout choice, which made it not only worthwhile to read, but also pleasant to read.

    John McM | 1 May 2007
  • VS8 allows you to keep the (bad) old behaviour by using the /EHa flag (project properties -> C/C++ -> Enable C++ Exceptions = Yes with SEH Exceptions)

    Motti Lanzkron | 28 May 2007
  • Excellent article, a must!

    Dominic C | 8 November 2007
  • There is a pblm with me…that I want to upgrade a project that is successfully compiling in C++ compiler 5.02…I want to compile it in C++ builer 6.0..but it gives error..as in this project try and except is used …error is for except()…and error is “catch expected”…what I do to resolve this error…help me

    Sushil | 8 December 2007
  • Hi,

    This article well describes the Exception Handling in C++. I am able to understand it very well. Is there any way we can get the File and line no where exception occured ?

    Raghavan

    Raghavan | 28 June 2008

Other comment pages: « 1 [2]

Post a comment

(required)
(required)
Close
E-mail It