Différences du comportement de gestion des exceptions dans /CLR

Les concepts de base de l’utilisation des exceptions managées traitent de la gestion des exceptions dans les applications managées. Dans cette rubrique, les différences entre le comportement standard de la gestion des exceptions et certaines restrictions sont abordées en détail. Pour plus d’informations, consultez la fonction _set_se_translator.

Saut d’un bloc final

Dans le code C/C++ natif, le saut d’un bloc __finally à l’aide de la gestion des exceptions structurées (SEH) est autorisé bien qu’il génère un avertissement. Sous /clr, le saut d’un bloc final provoque une erreur :

// clr_exception_handling_4.cpp
// compile with: /clr
int main() {
   try {}
   finally {
      return 0;   // also fails with goto, break, continue
    }
}   // C3276

Déclenchement d’exceptions dans un filtre d’exceptions

Lorsqu’une exception est levée pendant le traitement d’un filtre d’exception dans le code managé, l’exception est interceptée et traitée comme si le filtre retourne 0.

Contrairement au comportement dans le code natif où une exception imbriquée est levée, le champ ExceptionRecord de la structure EXCEPTION_RECORD (tel que retourné par GetExceptionInformation) est défini et le champ ExceptionFlags définit le bit 0x10. L’exemple suivant illustre cette différence de comportement :

// clr_exception_handling_5.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_s("We should execute this handler if "
                 "compiled to native\n");
    }
}

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

Sortie

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

Rethrows dissociés

/clr ne prend pas en charge la rethrowing d’une exception en dehors d’un gestionnaire catch (appelé rethrow dissocié). Les exceptions de ce type sont traitées comme une nouvelle croissance C++ standard. Si une nouvelle croissance dissociée est rencontrée lorsqu’il existe une exception managée active, l’exception est encapsulée en tant qu’exception C++, puis se réactive. Les exceptions de ce type ne peuvent être interceptées qu’en tant qu’exception de type SEHException.

L’exemple suivant illustre une nouvelle croissance d’exception managée en tant qu’exception C++ :

// clr_exception_handling_6.cpp
// compile with: /clr
using namespace System;
#include <assert.h>
#include <stdio.h>

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

int main() {
   try {
      try {
         throw gcnew 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_s("caught an SEH Exception\n" );
    }
}

Sortie

caught an SEH Exception

Filtres d’exceptions et EXCEPTION_CONTINUE_EXECUTION

Si un filtre retourne EXCEPTION_CONTINUE_EXECUTION dans une application managée, il est traité comme si le filtre retourné EXCEPTION_CONTINUE_SEARCH. Pour plus d’informations sur ces constantes, consultez l’instruction try-except.

L’exemple suivant illustre cette différence :

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

int main() {
   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_s("Counter=%d\n", Counter);
}

Sortie

Counter=-3

Fonction _set_se_translator

La fonction translator, définie par un appel à _set_se_translator, affecte uniquement les captures dans du code non managé. L’exemple suivant illustre cette limitation :

// clr_exception_handling_8.cpp
// compile with: /clr /EHa
#include <iostream>
#include <windows.h>
#include <eh.h>
#pragma warning (disable: 4101)
using namespace std;
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 <<  "]";
    }
};

#pragma unmanaged
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() {
   try  {
      RaiseException( MYEXCEPTION_CODE, 0, 0, 0 );
   }
   catch ( CMyException x ) {}
   catch ( ... ) {
      printf_s("This is invoked since "
               "_set_se_translator is not "
               "supported when /clr is used\n" );
    }
}

#pragma unmanaged
void unmanaged_func() {
   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();
}

Sortie

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

Voir aussi

Gestion des exceptions
safe_cast
Gestion des exceptions dans MSVC