/CLR을 지정하는 경우 예외 처리 동작의 차이점

관리되는 예외 사용의 기본 개념에서는 관리되는 애플리케이션의 예외 처리에 대해 설명합니다. 이 항목에서는 예외 처리의 표준 동작과 일부 제한 사항의 차이점을 자세히 설명합니다. 자세한 내용은 _set_se_translator 함수를 참조 하세요.

Finally 블록에서 점프

네이티브 C/C++ 코드에서는 경고를 생성하지만 SEH(구조적 예외 처리)를 사용하여 __finally 블록 밖으로 점프할 수 있습니다. /clr에서 최종 블록 밖으로 점프하면 오류가 발생합니다.

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

예외 필터 내에서 예외 발생

관리 코드 내에서 예외 필터를 처리하는 동안 예외가 발생하면 예외가 catch되고 필터가 0을 반환하는 것처럼 처리됩니다.

이는 중첩된 예외가 발생하는 네이티브 코드의 동작과는 대조적으로, EXCEPTION_RECORD 구조의 ExceptionRecord 필드(GetExceptionInformation에서 반환됨)가 설정되고 ExceptionFlags 필드는 0x10 비트를 설정합니다. 다음 예제에서는 동작의 이러한 차이를 보여 줍니다.

// 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");
    }
}

출력

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

연결되지 않은 Rethrows

/clr 은 catch 처리기 외부에서 예외를 다시 throw하는 것을 지원하지 않습니다(연결되지 않은 rethrow라고 함). 이 형식의 예외는 표준 C++ 다시 throw로 처리됩니다. 활성 관리되는 예외가 있을 때 연결되지 않은 다시 throw가 발생하면 예외가 C++ 예외로 래핑된 다음 다시 throw됩니다. 이 형식의 예외는 형식 SEHException의 예외로만 catch할 수 있습니다.

다음 예제에서는 C++ 예외로 다시 throw된 관리되는 예외를 보여 줍니다.

// 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" );
    }
}

출력

caught an SEH Exception

예외 필터 및 EXCEPTION_CONTINUE_EXECUTION

관리되는 애플리케이션에서 필터가 반환 EXCEPTION_CONTINUE_EXECUTION 되면 필터가 반환 EXCEPTION_CONTINUE_SEARCH된 것처럼 처리됩니다. 이러한 상수에 대한 자세한 내용은 try-except 문을 참조 하세요.

다음 예제에서는 이러한 차이점을 보여 줍니다.

// 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);
}

출력

Counter=-3

_set_se_translator 함수

호출 _set_se_translator로 설정된 Translator 함수는 관리되지 않는 코드의 catch에만 영향을 줍니다. 다음 예제에서는 이 제한 사항을 보여 줍니다.

// 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();
}

출력

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

참고 항목

예외 처리
safe_cast
MSVC의 예외 처리