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.
- The program installs its Win32 exception handler.
- The program runs for a while.
- At a crucial, deeply-nested point in the program, an access violation occurs.
- The Win32 exception handler is called, with details of the exception.
- The Win32 exception handler creates a new C++ object, initialising it with detailed information about the exception.
- The Win32 exception handler throws the C++ object as a C++ exception.
- 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.
- 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.
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!
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.
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)
Excellent article, a must!
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
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
Hi Raghavan, there is a way to know where the bad thing occurred: search for crashfidner. I remember it was in an article of ‘bugslayer’…
Hi Raghavan,
to do so, I#m using the predefined macros __FILE__ respectively __LINE__ .
See MSDN library:
http://msdn.microsoft.com/en-us/library/b0084kay(VS.71).aspx
Cheers,
Moni
Very Nice Article.
I have an .exe and around 15 dlls. Some of these dlls are loaded using loadlibrary.
Would it be enough if i build the .exe with /EHa or perhaps build one of the dlls(which is actually a listening server and does the main work) with /EHa. Or do i need to build all dlls and the .exe with /EHa to be able to catch an exception occuring anywhere in the application.
Thomas, it would be safest to build everything with /EHa unless you have some specific reason you need to disable it.
The proposed solution to throw a C++ exception from the _set_se_translator registered handler is not a safe operation when compiling with /EHs – occasionally, you’ll get crashes in the stack unwinding code because of this. The reason is that all the instructions necessary for properly unwinding the stack at any given execution point are omitted when compiling with /EHs. Additionally, when compiling with optimizations, the compiler may elide C++ catch handlers that it determines cannot be entered (the compiler cannot infer that a Win32 exception may invoke a _set_se_translator registered handler that may throw a C++ exception) – which means the exception may not get caught where you expected it to.
At the point that a Win32 exception occurs, the stack and objects on the stack may not be in a consistent state. Without the instructions added when compiling with /EHa, stack unwinding may result in destructors being called for partially constructed/destructed objects – which is a very bad thing. Additionally, partially constructed objects may not be unwound properly (or at all) leading to resource leaks.
Win32 exceptions and exception handlers have all the same challenges as signals and signal handlers have on POSIX platforms. I once read about someone advocating throwing C++ exceptions from signal handlers – which is a bad idea for exactly the same reasons as described above.
I recommend that catching of Win32 exceptions be limited to regions of code which do not require stack unwinding (ie, catch the exception in the stack frame that generated it and don’t place any C++ objects in the try/catch block). Or, use the async-exception safe longjmp/setjmp APIs (the ones in setjmp.h, NOT setjmpex.h). These longjmp/setjmp APIs will not call destructors for objects on the stack, but will ensure that the stack is unwound safely. Unfortunately, MS developers messed this up by not providing distinct interfaces to the async-exception safe setjmp/longjmp APIs (On POSIX systems, sigsetjmp and siglongjmp exist for this exact reason). You need to be very careful to ensure that setjmpex.h was not included prior to setjmp.h
Thanks for your comment Tom. Just in case it wasn’t clear, I agree that _set_se_translator does not play well with /EHs. That’s why I said:
The dire consequences you describe can indeed happen in code compiled with /EHs. But using /EHa pretty much solves all these problems. Following the approach I describe in the article should yield well-behaved and useful C++ exceptions. I’d like to know about any problems with this approach.
Hi Bennett,
I missed that you did mention that compilation with /EHa is required – sorry, my bad.
There are two negative side effects with the use of /EHa. First, the overhead of the asynchronous unwind code affects performance and generates larger binaries. Second, compilation with /EHa causes catch(…) handlers to be entered for Win32 exceptions. Your post states that “This problem does not occur in Visual C++ 2005, where catch(…) never catches Win32 exceptions.”. This isn’t correct as the following code demonstrates:
#include
int main()
{
try
{
volatile int* pi = NULL;
volatile int i = *pi;
}
catch(…)
{
std::cerr << “Caught exception” << std::endl;
}
return 0;
}
Compile this with “cl /O2 /EHa /Zi /MD main.cpp /Femain.exe” and run main.exe and you’ll see that the access violation does cause the catch(…) handler to be invoked.
For all the reasons enumerated in your excellent post, this is disastrous for code quality and in my opinion sufficient reason to not compile with /EHa.
Unfortunately, I was mistaken in my previous comment regarding async-exception safe setjmp/longjmp. I have since discovered that neither the “setjmp.h” or “setjmpex.h” setjmp/longjmp implementations are async-exception safe since both attempt to unwind the stack. The Microsoft documentation is confused about this – see http://msdn.microsoft.com/en-us/library/yz2ez4as.aspx. This documentation currently contradicts itself with regard to whether or not C++ stack objects are destroyed when longjmp is called. In our environment (compiling with /EHsc), we have seen crashes with call stacks like this:
msvcr80!raise+0×187
msvcr80!abort+0×3d
msvcr80!terminate+0×4d
msvcr80!_inconsistency+0×22
msvcr80!__FrameUnwindToState+0×4b
msvcr80!__CxxLongjmpUnwind+0×14
msvcr80!longjmp+0×76
The fact that longjmp called a function named CxxLongjmpUnwind suggests that it does in fact destroy C++ stack objects while unwinding.
This has left me in a bind. We need to do something like what you are suggesting – return to a previous execution point when a Win32 exception occurs, but compiling with /EHa is too burdensome since it will cause uses of catch(…) to catch Win32 exceptions. I had turned to setjmp/longjmp hoping to solve the problem that way (I don’t care about C++ stack objects being destroyed when returning to the previous execution point). Unfortunately, I’m probably going to have to implement my own setjmp/longjmp routines to do this safely without performing C++ stack unwinding.
Ideally, Microsoft would provide versions of setjmp/longjmp that do not attempt to perform C++ stack unwinding – similar to how the Single UNIX Spec provides sigsetjmp/siglongjmp to handle asynchronous signals.
This is one of the most informative articles I have seen on Exception Handling. It is definitly a must read for anyone who wants to know how to handle a Win32 exception in C++.
Good article about EH. To avoid the side effects with /EHa, we can write code as following in VC 2005 with /EHs:
void appGo()
{
try
{
// application run….
}
catch(…)
{
}
}
void main()
{
__try
{
appGo();
}
__except(1)
{
// do something
}
We put all application code into appGo() function. Thus __try/__except can handle the win32 exception, and all c++ exception can be hanlded in appGo().
I dont know whether this method is practice.
Please correct me.