In addition to structured exception handling (SEH) and C++ exception handling, Managed Extensions for C++ extend this feature by supporting the handling of managed exceptions. A managed exception is any exception thrown by a managed type. The System::Exception class provides many useful methods for processing managed exceptions and is recommended as a base class for user-defined exception classes.
The C++ throw expression is extended to throw a pointer to any managed object. The following example creates a custom exception type and then throws an instance of that type:
#using <mscorlib.dll> __gc struct MyStruct : public System::Exception { int i; }; void GlobalFunction() { MyStruct* pMyStruct = new MyStruct; throw pMyStruct; }
Value classes must be boxed before they can be thrown:
#using <mscorlib.dll> __value struct MyValueStruct { int i; }; void GlobalFunction() { MyValueStruct v = {11}; throw __box(v); }
The same try/catch block structure can be used for catching both managed and unmanaged exceptions. The following example demonstrates a simple try/catch block with managed and unmanaged structures:
#using <mscorlib.dll> __gc struct MyStruct : public System::Exception { int i; }; struct CMyClass { double d; }; void GlobalFunction() { MyStruct * pMyStruct = new MyStruct; pMyStruct->i = 11; throw pMyStruct; } void GlobalFunction2() { MyClass c = {2.0}; throw c; } void main() { for(int i = 1; i >= 0; --i) { try { if( i==1) GlobalFunction2(); if( i==0) GlobalFunction(); } catch(CMyClass& catchC) { Console::WriteLine(catchC.d); } catch(MyStruct* catchException) { Console::WriteLine(catchException->i); } } }
2 11
As usual, unwinding occurs for any C++ objects with destructors that may be on the run-time stack between the throwing function and the handling function. Because garbage-collected classes are allocated on the heap, unwinding does not apply to them.
When an unmanaged C++ object type is thrown, it is wrapped with a NGWS runtime exception of type System::Runtime::InteropServices::SEHException. When searching for the appropriate catch clause, there are two possibilities.
If an unmanaged C++ type is encountered, the exception is unwrapped and compared to the type encountered. This comparison allows an unmanaged C++ type to be caught in the normal way.
However, if a catch clause of type SEHException or any of its base classes is examined first, the clause will intercept the exception. Therefore, you should place all catch clauses that catch unmanaged C++ types first before any catch clauses of managed types.
In addition to try and catch clauses, Managed Extensions support a __finally clause. The semantics are identical to the __finally block in structured exception handling (SEH). A __finally block may follow a try or catch block.
The purpose of the __finally block is to clean up any resources left after the exception occurred. Note that the __finally block is always executed, even if no exception was thrown. The catch block is only executed if a managed exception is thrown within the associated try block.
The following example demonstrates a simple __finally block:
#using <mscorlib.dll> __gc class MyException : public System::Exception { }; void ThrowMyException() { throw new MyException; } void main() { try { ThrowMyException(); } catch (MyException *e) { Console::WriteLine("in catch"); Console::WriteLine(e->GetType()); } __finally { Console::WriteLine("in finally"); } }
in catch MyException in finally
Introduction to Managed Extensions for C++ | _try_cast | Exception Handling