Set Se Translator Implementation Example

The lesson is simple: don't use catch (... ) ! This particular company had already wasted weeks of work (and tons of money) attempting to track down a bug that was completely solvable but not reproducible because catch (...) was involved.

Don't Use _set_se_translator

In the first edition of this book, I covered the use of an interesting API named _set_se_translator. It has the magical ability to turn your SEH errors into C++ exceptions by calling a translation function that you define, which simply calls throw on the type you want to use for the conversion. I might as well confess now that although I was well intentioned, my advice was wrong. When you use _set_se_translator, you quickly find out that it doesn't work in release builds.

The first problem with _set_se_translator is that it isn't global in scope; it works only on a per-thread basis. That means you probably have to redesign your code to ensure that you call _set_se_translator at the beginning of each thread. Unfortunately, doing that isn't always easy. Additionally, if you're writing a component used by other processes you don't control, using _set_se_translator can and will completely mess up those processes' exception handling if they are expecting SEH exceptions and getting C++ exceptions instead.

The bigger problem with _set_se_translator has to do with the arcane implementation details of C++ exception handling. C++ exception handling can be implemented in two ways: asynchronous and synchronous. In asynchronous mode, the code generator assumes that every instruction can throw an exception. In synchronous mode, exceptions are generated explicitly only by a throw statement. The differences between asynchronous and synchronous exception handling don't seem that great, but they certainly are.

The drawback of asynchronous exceptions is that the compiler must generate what's called object lifetime tracking code for every function. Since the compiler is assuming that every instruction can throw an exception, every function that puts a C++ class onto the stack has to have code in it to hook up the destructor calls for each object in case an exception is thrown. Since exceptions are supposed to be rare or nearly impossible events, the downside to asynchronous exceptions is that you're paying quite a performance cost for all that object lifetime tracking code you'll never use.

Synchronous exception handling, on the other hand, solves the overhead problem by generating the object lifetime tracking code only when a method in the call tree for that method has an explicit throw. In fact, synchronous exception handling is such a good idea that it's the exception type the compiler uses. However, with the compiler assuming that exceptions occur only with an explicit throw in the call stack, the translator function does the throw, which is outside the normal code flow and is thus asynchronous. Consequently your carefully constructed C++ exception wrapper class never gets handled and your application crashes anyway. If you want to experiment with the differences between asynchronous and synchronous exception handling, add /EHa to the compiler command line to turn on asynchronous and remove any /gx or /EHs switches.

What makes using _set_se_translator even worse is that it works correctly in debug builds. Only in release builds will you experience the problems. That's because debug builds use synchronous exception handling instead of the release-build default of asynchronous. Because of the inherent problems with _set_se_translator, you'll definitely want to look for it in your code reviews to ensure that no one is using it.

The SetUnhandledExceptionFilter API Function

Interestingly, crashes have a habit of never happening where you expect them. Unfortunately, when your users experience crashes in your program, they just see the Application Error dialog box, and then maybe Dr. Watson gives them a little information to send to you to figure out the problem. As I mentioned earlier in this chapter, you can devise your own dialog boxes and handlers to get the information you really need to solve the crash. I've always referred to these exception handlers along with their corresponding exception filters as crash handlers.

In my experience, crash handlers have excellent debugging capabilities. I've worked on numerous projects where we'd gain control right as the application died, gather all the information about the crash (including the state of the user's system) into a file, and if the project was a client application, pop up a dialog box with a technical support number. In some cases, we architected the application so that we could iterate through the program's main objects, enabling us to report down to the class level which objects were active and what they contained. We were logging almost too much information about the program's state. With a crash report, we had a 90 percent chance of duplicating the user's problem. If that isn't proactive debugging, I don't know what is!

You create crash handlers through the magic of the SetUnhandledExceptionFilter API function. Amazingly, this functionality has been in Win32 since Microsoft Windows NT 3.5, but it's almost never mentioned in the documentation. At the time I wrote this chapter, in MSDN Online, this function was mentioned only eight times.

Needless to say, I find SetUnhandledExceptionFilter powerful. Just by looking at the function name—SetUnhandledExceptionFilter—you can probably guess what the function does. SetUnhandledExceptionFilter allows you to specify an unhandled exception filter function that should be called when an unhandled exception occurs in a process. The one parameter to SetUnhandledExceptionFilter is a pointer to an exception filter function that is called in the final_except block for the application. This exception filter returns the same value that any exception filter would return: EXCEPTION_EXECUTE_HANDLER, EXCEPTION_CONTINUE_EXECUTION, or exception_continue_search. You can do any exception handling you want in the exception filter, but you need to be careful about blown stacks in your exception filter. To be on the safe side, you might want to avoid any C run-time library calls as well as MFC. Although I'm obligated to warn you about these possibilities, I can assure you that the vast majority of your crashes will be access violations—you shouldn't have any problems if you write a complete crash handling system in your exception filter and exception handler, provided you check the exception reason first and avoid function calls when the stack is blown.

Your exception filter also gets a pointer to an exception_pointers structure. In Listing 13-4, I'll present several routines that translate this structure for you. Because each company has different crash handler needs, I'll let you write your own.

You need to keep in mind a couple of issues when you're using SetUnhandledExceptionFilter. The first is that you can't use standard user-mode debuggers to debug any unhandled exception filter you set. This restriction actually makes sense, because the operating system needs to take over the final exception filter when running under a debugger so that the operating system can properly report the final crash to the debugger. However, it can make debugging your final crash handler a bit of a pain. One workaround you can use to debug your unhandled exception filter is to call it from a regular SEH exception filter. You can find an example of this workaround in the Baz function in BugslayerUtil\Tests\CrashHandler\CrashHandler.CPP, which is part of this book's source code.

Another issue is that the exception filter you specify by calling SetUnhandledExceptionFilter is global to your process. If you build the coolest crash handler in the world for your ActiveX control and the container crashes—even if it's not your fault—your crash handler will be executed. Don't let this possibility keep you from using SetUnhandledExceptionFilter, though. I have some code that might help you out.

+1 0

Post a comment