In the previous section, we saw the basic architecture of exception handling in managed extensions. Here, we will deal with the following strategies of exception handling: | -
Exception handling in the case when the exception is thrown by a managed class and is handled in the managed class only. In this we will deal with the __ gc and __value classes. This code is in the directory Migrationstrategy_1 . //Declare an unmanaged class class UnmanagedException{ private: char* m_Description; public: char* GetDescription(){ return m_Description; } void SetDescription(char* f_Description){ m_Description = (char *) malloc (strlen(f_Description)*sizeof(char)); strcpy(m_Description, f_Description); } }; //Declare a value class __value class ValueException{ private: String* m_Description; public: __property String* get_Description(){ return m_Description; } __property void set_Description(String* f_Description){ m_Description = f_Description; } }; //Declare a gc class __gc class GCException: public Exception{ private: String* m_Description; public: __property String* get_Description(){ return m_Description; } __property void set_Description(String* f_Description){ m_Description = f_Description; } }; void main(){ //Instantiate the classes UnmanagedException objUnmgdException; ValueException objValException; GCException * pGCException = new GCException; try{ for (int i=0; i<4; i++){ try{ switch (i){ //Throw exception of the unmanaged class case 0 : objUnmgdExcetion.SetDescription ("This is an exception of an unmanaged class"); throw objUnmgdException; //Throw exception of the value class case 1 : objValException.Description = S"This is an exception of a value class"; throw __box (objValException); //Throw exception of the gc class case 2 : pGCException->Description = S"This is an exception of a managed GC class type"; throw pGCException; //Throw any other exception default: throw new Exception; } // switch } //Catching exception thrown by the unmanaged class catch (UnmanagedException & objUnmgd){ Console::WriteLine (objUnmgd.GetDescription()); } //Catching exception thrown by the value class catch (__box ValueException * pVal){ Console::WriteLine(pVal->Description); } //Catching exception thrown by the gc class catch (GCException * pGC){ Console::WriteLine(pGC->Description); } //Catching other exceptions thrown catch (Object *ex){ Console::WriteLine("Generic exception."); } } // try } // for __finally{ Console::WriteLine("The application is now in the finally block"); } // finally } Output: This is an exception of an unmanaged class This is an exception of a value class This is an exception of a managed GC class type Generic exception. The application is now in the finally block -
The second case is where a function in an unmanaged class throws an exception and it is caught in the managed class. Let us first declare an unmanaged class in a new solution: //Declare an unmanaged class #pragma unmanaged class UnmanagedException{ private: char* m_Description; public: void ThrowUnmanagedException(); char* GetDescription(){ return m_Description; } void SetDescription(char* f_Description){ m_Description = (char*) malloc(strlen(f_Description)*sizeof(char)); strcpy(m_Description, f_Description); } }; In this code, you may be wondering what the #pragma unmanaged means. When you declare #pragma unmanaged before a class, the entire class and its member functions will be compiled as unmanaged. Because in this case we are declaring an unmanaged class in the .NET environment and we want it to be compiled as an unmanaged class, the #pragma has been used. The just-declared unmanaged class has a member function called ThrowUnmanagedException that will throw an object of the unmanaged class. The function implementation is as follows : void UnmanagedException::ThrowUnmanagedException(){ throw new UnmanagedException; } //Now declare an managed class. #pragma managed //Declare a managed class __gc class GCException{ private: String* m_Description; public: void ThrowManagedException(); __property String* get_Description(){ return m_Description; } __property void set_Description(String* f_Description){ m_Description = f_Description; } }; Declaring #pragma managed before a class indicates that the class and all its member functions will be compiled as a managed class. This managed class contains a member function ThrowManagedException that will call the ThrowUnmanagedException of the unmanaged class and catch the exception thrown by that function: void GCException::ThrowManagedException(){ UnmanagedException objUnmgdException; GCException *pGCException = new GCException; try{ pGCException->Description = S"This is an exception of the unmanaged class caught in the managed class"; objUnmgdException.ThrowUnmanagedException(); } catch(Exception* pEx){ Console::WriteLine(pGCException->Description); } } Instantiate the managed class and call its member function. When the member function of the managed class is called, it will internally call the member function of the unmanaged class that in turn will throw an object of the unmanaged class. This object is caught in the function of the managed class, thus indicating that unmanaged exceptions can be caught in the managed class. The following code is in the directory Migrationstrategy_2 . void main(){ //Instantiate the classes GCException *pGCException = new GCException; pGCException->ThrowManagedException(); } Output: This is an exception of the unmanaged class caught in the managed class -
The third case that should be considered is the handling of managed exceptions in an unmanaged class. For this, create a simple .NET component. The code snippet for the .NET component follows: namespace NetCom{ public __gc __interface IGCException{ public: void ThrowManagedException(); }; public __gc class CGCException:public IGCException{ private: char* m_Description; public: CGCException() {} void ThrowManagedException(){ CGCException *pGCException = new CGCException; Console::WriteLine("This is a managed exception that is being caught in an unmanaged class"); throw new Exception; } }; }//namespace NetCom This code snippet will create a .NET component that will output a message when called from the client. It will also throw an exception. For this .NET component we will code a Visual C++ 6.0 client. The client will catch this exception. The client contains two catch blocks. One handles an unmanaged object, and the other catch block is the catchall block, which is generic. The code snippet for the client follows: class UnmanagedException { private: char* m_Description; public: char* GetDescription(){ return m_Description; } void SetDescription(char* f_Description){ m_Description = (char*) malloc(strlen(f_Description)*sizeof(char)); strcpy(m_Description, f_Description); } }; Here we have declared an unmanaged class. Use this unmanaged class to access the .NET component in main. The following code is in the directory Migrationstrategy_3 . #import "NetCom.tlb" using namespace NetCom; int main(int argc, char* argv[]){ UnmanagedException objUnmgdException; CoInitialize(NULL); try{ char* l_Description = "This is a property of the unmanaged class"; objUnmgdException.SetDescription(l_Description); IGCExceptionPtr pNetCom(__uuidof(CGCException)); pNetCom->ThrowManagedException(); } catch(UnmanagedException &objUnmgdException){ cout<< objUnmgdException.GetDescription(); } catch(...) { cout<<"Throwing managed exception"<<endl; } CoUninitialize(); return 0; } Output: This is a managed exception that is being caught in an unman- aged class Throwing managed exception This piece of code will terminate abnormally after showing the message if the catch ( ) block is not included. This indicates that an unmanaged catch cannot handle an exception thrown by a managed class. | This is one way of catching an exception thrown by a managed class in an unmanaged catch handler, thus preventing abnormal termination of the handler. The order of events for a thrown exception are as followed: -
The stack is first searched for the correct catch clause by the runtime. In the case of structured exception handling (SEH), an SEH except filter is searched for. The search for the catch clause is first in the lexical order and then moves dynamically down the stack. -
The stack is unwound at the point where the correct handler is found. The local objects are destructed for each function call on the stack. __finally blocks are executed from most nested outwards. -
The catch clause is executed once the stack is unwound. We must specify one of the compiler options (/GX, /Ehs, or /Eha) while throwing and catching unmanaged exceptions. Now that we understand the basic concepts of exception handling in managed exceptions, we will deal with the restrictions and the deviation from the standard behavior of exception handling as far as managed extensions is concerned . In managed code a jump from the __ finally block results in a compilation error. A return , goto , continues , or break in a __finally block will evaluate to a compilation error. The same applies to C++ exception handling in managed extensions. Such a piece of code when compiled will give a compilation error indicating that a jump statement in the __finally block is not possible. Disassociated Rethrows Rethrowing an exception outside a catch handler is not supported in managed extensions. Managed extensions treats these types of exceptions as C++ rethrows. When a disassociated rethrow is encountered in a managed exception, it is wrapped in the same manner as a C++ exception and then rethrown. Hence, exceptions of this type are caught only by the exceptions of type System::SEHException . This will be illustrated in the following example, which is in the directory disassociated_rethrow . //Declaring a managed class __gc class GCException{ private: String* m_Description; public: __property String* get_Description() { return m_Description; } __property void set_Description(String* f_Description){ m_Description = f_Description; } }; void rethrow(void){ throw; } void main(){ GCException *pGCException = new GCException; try{ try{ pGCException->Description = S"Throwing an exception of the managed class"; throw pGCException; } catch(GCException *ex){ rethrow(); } } catch(GCException *ex){ Console::WriteLine(ex->Description); Console::WriteLine("Exception caught in the managed exception catch block"); } catch(Runtime::InteropServices::SEHException* pEx){ Console::WriteLine("Exception caught in the Structured Excep- tion Handler"); } } Output: Exception caught in the Structured Exception Handler In this example, in the catch block we are calling another function, rethrow , that is not associated with the managed class in any way. Hence, the throw from that function will not be recognized by the catch block of the managed code and it will be caught by the SEH, as is evident from the output. This is a disassociated rethrow. If instead of rethrow , throw , throw(ex) , or rethrow(GCException* ex) is used, the throw will be recognized as a managed throw and will be caught by the managed catch block. An example follows, which is in the directory disassociated_rethrow : //Declaring a managed class __gc class GCException{ private: String* m_Description; public: __property String* get_Description() { return m_Description;} __property void set_Description(String* f_Description){ m_Description = f_Description; } }; void rethrow(GCException *ex){ throw(ex); } void main(){ GCException *pGCException = new GCException; try{ try{ pGCException->Description = S"Throwing an exception of the managed class"; throw pGCException; } //Catching a managed exception catch(GCException *ex){ rethrow(ex); } } //Catching a managed exception catch(GCException *ex){ Console::WriteLine(ex->Description); Console::WriteLine("Exception caught in the managed exception catch block"); } //Catching Structured Exceptions catch(Runtime::InteropServices::SEHException* pEx){ Console::WriteLine("Exception caught in the Structured Excep- tion Handler"); } } Output: Throwing an exception of the managed class Exception caught in the managed exception catch block |