C Exceptions Versus Structured Exceptions

[Previous] [Next]

Developers frequently ask me whether they should use structured exceptions or C++ exceptions when developing their applications. I'd like to offer an answer in this section.

Let me start by reminding you that SEH is an operating system facility available in any programming language, while C++ EH can only be used when writing C++ code. If you're writing a C++ application, you should use C++ exceptions instead of structured exceptions. The reason is that C++ exceptions are part of the language and therefore the compiler knows what a C++ class object is. This means that the compiler automatically generates code to call C++ object destructors in order to guarantee object cleanup.

However, you should know that Microsoft's Visual C++ compiler has implemented C++ exception handling using the operating system's structured exception handling. So, when you create a C++ try block, the compiler is generating an SEH _ _try block. A C++ catch test becomes an SEH exception filter and the code in the catch block becomes the code in the SEH _ _except block. In fact, when you write a C++ throw statement, the compiler generates a call to the Windows RaiseException function. The variable used in the throw statement is passed as an additional argument to RaiseException.

The following code fragment will help make all of this a little clearer. The function on the top uses C++ exception handling and the function on the bottom demonstrates how the C++ compiler generates the equivalent structured exception handling.

 void ChunkyFunky() { try { // Try body  throw 5; } catch (int x) { // Catch body  }  } 

 void ChunkyFunky() { _ _try { // Try body  RaiseException(Code=0xE06D7363, Flag=EXCEPTION_NONCONTINUABLE, Args=5); } _ _except ((ArgType == Integer) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { // Catch body  }  } 

You'll notice a few interesting details about the code above. First, notice that RaiseException is called with an exception code of 0xE06D7363. This is the software exception code selected by the Visual C++ team to be used when throwing C++ exceptions. In fact, you can verify this if you open the debugger's Exceptions dialog box and scroll to the bottom of the exceptions list, shown here.

click to view at full size.

You'll also notice that the EXCEPTION_NONCONTINUABLE flag is always used when a C++ exception is thrown. C++ exceptions can never be re-executed, and it would be an error for a filter diagnosing a C++ exception to return EXCEPTION_CONTINUE_EXECUTION. In fact, if you look at the _ _except filter in the function on the right, you'll see that it is only capable of evaluating to EXCEPTION_EXECUTE_HANDLER or EXCEPTION_CONTINUE_SEARCH.

The remaining parameters to RaiseException are used as the mechanism that actually throws the specified variable. Exactly how the thrown variable information is passed to RaiseException is not documented, but it's not too hard to imagine ways that the compiler team could have implemented this.

The last thing I'd like to point out is the _ _except filter. The purpose of this filter is to compare the throw variables data type with the variable type used in the C++ catch statement. If the data types are the same, the filter returns EXCEPTION_EXECUTE_HANDLER, causing the statements in the catch block (_ _except block) to execute. If the data types are different, the filter returns EXCEPTION_CONTINUE_SEARCH, allowing catch filters farther up the call tree to be evaluated.

NOTE
Since C++ exceptions are implemented internally via structured exceptions, you can use both mechanisms in a single application. For example, I love using virtual memory to commit storage when access violations are raised. The C++ language does not support this type of resumptive exception handling at all. However, I can use structured exception handling in the parts of my code where I want to take advantage of this and have my own _ _except filter return EXCEPTION_CONTINUE_ EXECUTION. For all other parts of my code that do not require resumptive exception handling, I'll stick with C++ exception handling.

Catching Structured Exceptions with C++

Normally, C++ exception handling does not allow an application to recover from a hard exception such as an access violation or a division by 0. However, Microsoft has added this support to their compiler. For example, the following code will prevent the process from terminating abnormally:

 void main() { try { * (PBYTE) 0 = 0; // Access violation } catch (...) { // This code handles the access-violation exception } // The process is terminating normally } 

This is nice, since it allows your application to recover gracefully from hard exceptions. However, it would also be nice if the catch's exception-declaration could somehow distinguish between different exception codes. For example, it would be nice to be able to write code like this:

 void Functastic() { try { * (PBYTE) 0 = 0; // Access violation int x = 0; x = 5 / x; // Division by zero } catch (StructuredException) { switch (StructuredExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: // This code handles an access-violation exception break; case EXCEPTION_INT_DIVIDE_BY_ZERO: // This code handles a division-by-zero exception break; default: // We don't handle any other exceptions throw; // Maybe another catch is looking for this break; // Never executes } } } 

Well, you ll be happy to know that Visual C++ has a mechanism that makes this possible. Here s what you need to do. Create your own C++ class to use in your code to identify structured exceptions. Here is an example:

 #include <eh.h> // For _set_se_translator  class CSE { public: // Call this function for each thread. static void MapSEtoCE() { _set_se_translator(TranslateSEtoCE); } operator DWORD() { return(m_er.ExceptionCode); } private: CSE(PEXCEPTION_POINTERS pep) { m_er = *pep->ExceptionRecord; m_context = *pep->ContextRecord; } static void _cdecl TranslateSEtoCE(UINT dwEC, PEXCEPTION_POINTERS pep) { throw CSE(pep); } private: EXCEPTION_RECORD m_er; // CPU independent exception information CONTEXT m_context; // CPU dependent exception information }; 

Inside each of your thread s entry-point functions, call the static member function MapSEtoCE. This function calls the C run-time function _set_se_translator passing it the address of the CSE class s TranslateSEtoCE function. By calling _set_se_translator, you re telling the C++ run time to call the TranslateSEtoCE function whenever a structured exception is raised. This function constructs a CSE class object and initializes the two data members to contain the CPU-independent and CPU-dependent information about the exception. After the CSE object is constructed, it is thrown just as any normal variable can be thrown. Now, your C++ code can handle structured exceptions by catching a variable of this type.

Here s an example of how to catch this C++ object:

 void Functastic() { CSE::MapSEtoCE(); // Must be called before any exceptions are raised try { * (PBYTE) 0 = 0; // Access violation int x = 0; x = 5 / x; // Division by zero } catch (CSE se) { switch (se) { // Calls the operator DWORD() member function case EXCEPTION_ACCESS_VIOLATION: // This code handles an access-violation exception break; case EXCEPTION_INT_DIVIDE_BY_ZERO: // This code handles a division-by-zero exception break; default: // We don t handle any other exceptions throw; // Maybe another catch is looking for this break; // Never executes } } } 



Programming Applications for Microsoft Windows
Programming Applications for Microsoft Windows (Microsoft Programming Series)
ISBN: 1572319968
EAN: 2147483647
Year: 1999
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net