This documentation is archived and is not being maintained.

Differences in Exception Handling Behavior Under Managed Extensions for C++

Basic Concepts in Using Managed Exceptions discusses exception handling in managed applications. In this topic, differences from the standard behavior of exception handling and some restrictions are discussed in detail.

Jumping Out of a Finally Block

In native Microsoft C/C++ code, jumping out of a finally block using structured exception handling (SEH) is allowed although it produces a warning. In managed code, the warning becomes an error.

The following code produces a warning in an unmanaged C++ application and an error when you compile with /clr:

void f(void)
{
   __try
   {
   }
   __finally
   {
      return; // also fails with goto, break, continue
   }
}

The same restriction also applies to C++ exception handling in Managed Extensions for C++. Once again, the following code produces a warning in an unmanaged C++ application and an error when you compile with /clr:

void f(void)
{
   try
   {
   }
   __finally
   {
      return; // also fails with goto, break, continue
   }
}

Raising Exceptions Within an Exception Filter

When an exception is raised during the processing of an exception filter within managed code, the exception is caught and treated as if the filter returns 0.

This is in contrast to the behavior in native code where a nested exception is raised, the ExceptionRecord field in the EXCEPTION_RECORD structure (as returned by GetExceptionInformation) is set, and the ExceptionFlags field sets the 0x10 bit. The following example illustrates this difference in behavior:

// raising_exceptions.cpp
#include <windows.h>
#include <stdio.h>
#include <assert.h>

#ifndef false
#define false 0
#endif

int *p;
int filter(PEXCEPTION_POINTERS ExceptionPointers)
{
   PEXCEPTION_RECORD ExceptionRecord = ExceptionPointers->ExceptionRecord;
   if ((ExceptionRecord->ExceptionFlags & 0x10) == 0)
   {
      /* not a nested exception, throw one */
      *p = 0; /* throw another AV */
   }
   else
   {
      printf("Caught a nested exception\n");
      return 1;
   }
   assert(false);
   return 0;
}

void f(void)
{
   __try
   {
      *p = 0; /* throw an AV */
   }
   __except(filter(GetExceptionInformation()))
   {
      printf("We should execute this handler if compiled to native\n");
   }
}

int main(void)
{
   __try
   {
      f();
   }
   __except(1)
   {
      printf("The handler in main caught the exception\n");
   }
   return 0;
}

Output when compiled without /clr:

Caught a nested exception
We should execute this handler if compiled to native

Output when compiled with /clr:

The handler in main caught the exception

Disassociated Rethrows

Managed Extensions for C++ do not support rethrowing an exception outside of a catch handler (known as a disassociated rethrow). Exceptions of this type are treated as a standard C++ rethrow. If a disassociated rethrow is encountered when there is an active managed exception, the exception is wrapped as a C++ exception and then rethrown. Exceptions of this type can only be caught as an exception of type System::SEHException.

The following example demonstrates a managed exception rethrown as a C++ exception:

// rethrows.cpp
// compile with: /clr
#using <mscorlib.dll>
using namespace System; // Needed to access the .NET Framework classes

#include <assert.h>
#include <stdio.h>

void rethrow(void)
{
    throw; // This rethrow is a a dissasociated rethrow.
           // The exception would be masked as SEHException.
}

int main(void)
{
   try
   {
      try
      {
         throw new ApplicationException;
      }
      catch(ApplicationException*)
      {
         rethrow();
/* If the call to rethrow() is replaced with a throw statement within the 
   catch handler, the rethrow would be a managed rethrow and the 
   exception type would remain System::ApplicationException */
       }
    }
    catch(ApplicationException*)
    {
       assert(false);
/* This will not be executed since the exception will be 
   masked as SEHException. */
    }
    catch(Runtime::InteropServices::SEHException*)
    {
        printf("caught an SEH Exception\n");
    }
    return 0;
}

Output

caught an SEH Exception

Exception Filters and EXCEPTION_CONTINUE_EXECUTION

If a filter returns EXCEPTION_CONTINUE_EXECUTION in a managed application, it is treated as if the filter returned EXCEPTION_CONTINUE_SEARCH. For more information on these constants, see try-except Statement.

The following example demonstrates this difference:

// exception_filters.cpp
#include <windows.h>
#include <stdio.h>
#include <assert.h>

int main(void)
{
   int Counter = 0;
   __try
   {
      __try
      {
         Counter -= 1;
         RaiseException (0xe0000000|'seh', 0, 0, 0);
         Counter -= 2;
      }
       __except (Counter)
       {
          // Counter is negative, indicating "CONTINUE EXECUTE"
          Counter -= 1;
       }
   }
   __except(1)
   {
       Counter -= 100;
   }
   printf("Counter=%d\n", Counter);
   return 0;
}

Output when compiled without /clr:

Counter=-3

Output when compiled with /clr:

Counter=-101

The _set_se_translator Function

The translator function, set by a call to _set_se_translator, affects only catches in unmanaged code. The following example demonstrates this limitation:

// set_se_translator.cpp
// compile with: /clr /EHa
#include <iostream>
#include <windows.h>
#include <eh.h>
using namespace std;

#using <mscorlib.dll>
using namespace System;

#define MYEXCEPTION_CODE 0xe0000101

class CMyException
{
   public:
      unsigned int m_ErrorCode;
      EXCEPTION_POINTERS* m_pExp;
      CMyException() : m_ErrorCode(0), m_pExp(NULL) {}
      CMyException(unsigned int i, EXCEPTION_POINTERS* pExp) 
         : m_ErrorCode(i), m_pExp(pExp) { }
      CMyException(CMyException& c) 
         : m_ErrorCode(c.m_ErrorCode), m_pExp(c.m_pExp) { }
      friend ostream& operator<<(ostream& out, const CMyException& inst)
      { 
          return out << "CMyException[\n" << "Error Code: " << inst.m_ErrorCode << "]";
      }
};

void my_trans_func(unsigned int u, PEXCEPTION_POINTERS pExp)
{
   cout << "In my_trans_func.\n";
   throw CMyException( u, pExp );
}

#pragma managed
void managed_func(void)
{
   try
   {
      RaiseException(MYEXCEPTION_CODE, 0, 0, 0);
   }
   catch(CMyException x)
   {
   }
   catch(...)
   {
      printf("This is invoked since _set_se_translator is not supported when /clr is used\n");
   }
}

#pragma unmanaged
void unmanaged_func(void)
{
   try
   {
      RaiseException(MYEXCEPTION_CODE, 0, 0, 0);
   }
   catch(CMyException x)
   {
      printf("Caught an SEH exception with exception code: %x\n", 
             x.m_ErrorCode);
   }
   catch(...) {}
}

#pragma managed
int main(int argc, char** argv)
{
   _set_se_translator(my_trans_func);
   // It does not matter whether the translator function
   // is registered in managed or unmanaged code
   managed_func();
   unmanaged_func();
   return 0;
}

Output

This is invoked since _set_se_translator is not supported when /clr is used
In my_trans_func.
Caught an SEH exception with exception code: e0000101

See Also

Handling Exceptions Using Managed Extensions for C++ | __try_cast | Exception Handling | Exceptions Samples

Show: