Visual C++ exception handling

These are some readers' responses. Read the full article to see what all the fuss is about.

33 responses

Other comment pages: « 1 [2]

  • 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
  • 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’…

    Fulvio | 5 August 2008
  • 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

    Moni | 5 August 2008
  • 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 | 5 August 2008
  • Thomas, it would be safest to build everything with /EHa unless you have some specific reason you need to disable it.

    Bennett | 6 August 2008
  • 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

    Tom Honermann | 23 August 2008
  • 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:

    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().

    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.

    Bennett | 23 August 2008
  • 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.

    Tom Honermann | 12 September 2008

Other comment pages: « 1 [2]

Post your comment | RSS feed for comments
Close
E-mail It