Click here to Skip to main content
Click here to Skip to main content

Managed Exception Handling using VC++.NET

, 8 Aug 2003
Rate this:
Please Sign up or sign in to vote.
An introduction to managed exception handling in MC++ programs

Introduction

The managed extensions to C++ extends traditional C++ exception handling capabilities and adds support for managed exception handling. This article does not cover traditional exception handling or structured exception handling, and is only meant as a guide to handling managed exceptions in an application that makes use of the managed extensions to C++. Managed exceptions are simply exceptions thrown by managed types (typically by classes in the BCL) and it's recommended that you use the System::Exception class as your exception object, either directly or by creating your custom exception classes using this class as the base class. While there is nothing preventing you from throwing managed objects that are not derived from System::Exception; one slight point to note is that you can only throw __gc objects, which basically means you cannot throw __value objects directly (but you can box them and throw the boxed object).

Note - In all the example code snippets, you'll see a Show function which is not really a function, but a preprocessor definition I added as follows :-

#define Show Console::WriteLine

Throwing exceptions

Managed extensions are thrown using the throw statement.

void ThrowFunction()
{
    Show("in ThrowFunction");
    throw new Exception("asdf");
}

In the above example I have thrown an exception of type System::Exception. System::Exception is the recommended base class for all managed exception classes and the .NET BCL classes exclusively use this class or one of several derived classes for their exception handling requirements. The output of calling the above function would be something very similar to :-

in ThrowFunction

Unhandled Exception: System.Exception: asdf
at ThrowFunction() in c:\...\exceptionsdemo.cpp:line 23
at main() in c:\...\exceptionsdemo.cpp:line 138

Since we haven't handled the exception the CLR has handled it for us and it displays the exception text message that we passed through the constructor overload that we used to create the exception.  Essentially the Exception constructor that accepts a System::String just saves the passed string to a member variable called _message and this is then exposed through a read-only property called Message. In addition the CLR also prints out a stack trace by using the StackTrace property of the Exception  class. So we can imagine that the CLR puts a catch-all exception handler for all IL executables that run under it, and for caught exceptions it just prints the text in the Message and StackTrace properties of the Exception object that was thrown. Of course this is an over-simplistic projection of the CLR's activities, and the actual CLR processes might be extensively more complicated than this humble visualization.

Handling exceptions

We handle exceptions by using try-catch blocks.

void TryFunction()
{
    try
    {
        Show("in TryFunction");
        ThrowFunction(); 
        Show("this won't come");
    }
    catch( Exception* e)
    {
        Show("in TryFunction exception block {0}",e->Message);
    }
}

The output of the above program will be similar to :-

in TryFunction
in ThrowFunction
in TryFunction exception block asdf

What happens is that the code inside the try block gets executed till an exception occurs or the end of the try block is reached, and if an exception occurs then control jumps to the catch block. Any code remaining in the try block will not get executed as is obvious from the output of the above code snippet. The ThrowFunction function is the same as in the earlier code snippet and we display the exception message using the Message property of the System::Exception class. 

One issue that might creep up with the above scenario is when you need to perform clean-up operations. Let's say you have opened a disk file and while you are writing to the disk-file an exception is raised, which means control has jumped to the catch block. Now the code that closes the disk-file does not get executed. A rather inelegant solution is to close the file outside the try-catch block :-

if( file_is_open )
{
    //close file
}

Now this might seem okay at first glance, but assume that you have nested try-catch blocks (which is quite normal when you write some proper code), now you'll need to be careful where exactly you free your resources and you might also have to increase the scope of temporary resource handle variables to allow them to be accessed outside the try-catch blocks.  The solution to this issue is the __finally keyword used to create __finally blocks which get executed irrespective of whether or not an exception has been raised.

void TryFunction()
{
    try
    {
        Show("in TryFunction");
        ThrowFunction(); 
    }
    catch( Exception* e)
    {
        Show("in TryFunction exception block {0}",e->Message);
    }
    __finally
    {
        Show("in TryFunction Finally block");
    }
}

Now try running the above code, as such, once, and then run it again after commenting out the call to the ThrowFunction function in the try block. You'll see that in  both cases the code inside the __finally block does get executed.

Rethrowing exceptions

When you catch an exception inside a try-catch block there is a small issue - any try-catch block that is higher up in the try-catch nested hierarchy will not see this exception. Luckily it's quite easy to rethrow an exception.

void TryFunction()
{
    try
    {
        Show("in TryFunction");
        ThrowFunction(); 
    }
    catch( Exception* e)
    {
        Show("in TryFunction exception block {0}",e->Message);
        throw;//rethrow the exception
    }
    __finally
    {
        Show("in TryFunction Finally block");
    }
}

void TryFunction2()
{
    try
    {
        Show("in TryFunction2");
        TryFunction();
    }
    catch(Exception* e)
    {
        Show("in TryFunction2 exception block {0}",e->Message);
    }
    __finally
    {
        Show("in TryFunction2 Finally block");
    }
}

We simply use the throw statement without any parameters inside the inner catch block. This will propagate the same exception object to the outer try-catch block as is evident from the output of calling the TryFunction2 method.

in TryFunction2
in TryFunction
in ThrowFunction
in TryFunction exception block asdf
in TryFunction Finally block
in TryFunction2 exception block asdf
in TryFunction2 Finally block

If you comment out the throw statement in the inner catch block, you'll see that the outer catch block is never reached because the exception was handled in the inner try-catch block, but both __finally blocks do get executed as expected.

Using custom exception classes

You can create your own exception classes with extra properties and methods to suite your requirements and it's strongly recommended that you derive them from System::Exception. You must also remember to explicitly specify the corresponding base class constructor for each overload of the constructor that you are going to implement for your custom exception class.

public __gc class MyException : public Exception
{
public:
    MyException() : Exception()
    {
        //stuff
    }
    MyException(String* str) : Exception(str)
    {
        //stuff
    }
private:
    //custom members and functions
};

void ThrowFunction()
{
    Show("in ThrowFunction");
    throw new MyException("asdf");
}

void TryFunction()
{
    try

        //...snipped...

    catch( MyException* e)
    {
        Show("in TryFunction exception block {0}",e->Message);
        throw;
    }

    //...snipped...
}

void TryFunction2()
{
    try

        //...snipped...

    catch(MyException* e)
    {
        Show("in TryFunction2 exception block {0}",e->Message);
    }

    //...snipped...
}

I have simply derived a class called MyException from System::Exception and have overridden two of the overloaded constructors of the base class. And in my catch blocks I now specify MyException as the type of exception object that is to be caught.

Multiple catch blocks

It's possible to have multiple catch blocks in any try-catch-__finally block of code, which we can take advantage to specifically handle multiple exception types that get thrown. Let's write a function that throws different types of exceptions based on a parameter we pass to it :-

void ThrowFunction2(int i)
{
    Show("in ThrowFunction2");
    if( i == 0 )
        throw new MyException("asdf");
    else 
        if( i == 1 ) 
            throw new Exception("asdf");
        else
            throw new Object();
}

Now we can individually handle these separate exception scenarios as follows :-

void TryFunction3(int i)
{
    try
    {
        Show("in TryFunction3");
        ThrowFunction2(i); 
    }
    catch( MyException* e)
    {
        Show("in TryFunction3 my exception block {0}",e->Message); 
    }
    catch( Exception* e)
    {
        Show("in TryFunction3 exception block {0}",e->Message); 
    }
    catch( Object* o)
    {
        Show("in TryFunction3 exception block {0}",o->ToString());
    }
    __finally
    {
        Show("in TryFunction3 Finally block");
    }
}

Remember to put the catch blocks in reverse order of inheritance hierarchy, as otherwise the upper-classes in the inheritance chain will get swallowed up by the lowest class in the hierarchy or any classes lower than itself in the inheritance chain.  For example, in the above code snippet, had we put the Exception catch block above the MyException catch block it would then catch both Exception type exceptions and MyException type exceptions, which would be wholly undesirable, because the specific MyException catch block never gets executed.

You'll find quite frequently that several of the derived exception classes in the .NET Base Class Library do not add any functionality to the System::Exception class. This is because these derived classes serve only to isolate specific exceptions rather than actually add any extra methods or properties to the Exception class. Let's assume that we are going to open a file and then write some information into it. Now assuming that exception scenarios might arise either when opening a file or when writing to the opened file, we would obviously need to handle them differently, as in the slightly contrived code snippet below.

public __gc class FileOpenException : public Exception
{

};

public __gc class FileWriteException : public Exception
{

};

try
{
    //allocate buffer and fill it

    //open file (might throw FileOpenException)

    while( condition )
    {
        //write buffer to file (might throw FileWriteException)
    }
}
catch ( FileOpenException* foe )
{
    //show file open error message 
}
catch ( FileWriteException* fwd )
{
    //close file (because it's currently open)
    //delete file (as it's not been written into properly)
    //show file write error message 
}
__finally
{
    //deallocate buffer
}

Throwing __value objects

Let's assume that for some reason we want a custom exception object that is a __value class or __value struct (not derived from System::Exception obviously)

public __value class MyValueClass
{
public:
    int u;
};

If we tried to throw an object of this class, we'd get the following compiler error message [broken into two lines to prevent scrolling]:-

c:\...\ExceptionsDemo.cpp(107): error C2715: 'MyValueClass' : 
  unable to throw or catch an interior __gc pointer or a value type

So we need to box the __value object before throwing it, as shown below :-

void Test()
{
    MyValueClass m;
    m.u = 66;
    //throw m; (won't compile)
    throw __box(m);
}

And we can catch it as an exception of type System::Object and then unbox it to access the actual exception object.

void TryTest()
{
    try
    {
        Test();
    }
    catch(Object* m)
    {
        MyValueClass v = *dynamic_cast<__box MyValueClass*>(m);
        Show(v.u);
    }
}

__try_cast and System::InvalidCastException

While this is not directly pertaining to a general discussion on managed exceptions, I thought I'd mention __try_cast here which is an additional keyword supported by the managed extensions to C++. The casting operator works exactly like the dynamic_cast operator except that if the cast fails it throws a System::InvalidCastException exception.

void TestCast()
{
    try
    {
        Object* obj = new Object();
        String* str = __try_cast<String*>(obj);
    }
    catch(InvalidCastException* ice)
    {
        Show(ice->Message);
    }
}

Conclusion

I hope this article has thrown light on managed exception handling in VC++.NET for programmers from a native C++ background, who are gradually moving to .NET and Managed C++. For further information about .NET exception handling I recommend Tom Archer's and Andrew Whitechapel's Inside C# (2nd edition)[^]  - specifically Chapter 12 (Pages 411 - 448).

History

  • August 09th 2003 - First published

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Nish Sivakumar

United States United States
Nish is a real nice guy who has been writing code since 1990 when he first got his hands on an 8088 with 640 KB RAM. Originally from sunny Trivandrum in India, he has been living in various places over the past few years and often thinks it’s time he settled down somewhere.
 
Nish has been a Microsoft Visual C++ MVP since October, 2002 - awfully nice of Microsoft, he thinks. He maintains an MVP tips and tricks web site - www.voidnish.com where you can find a consolidated list of his articles, writings and ideas on VC++, MFC, .NET and C++/CLI. Oh, and you might want to check out his blog on C++/CLI, MFC, .NET and a lot of other stuff - blog.voidnish.com.
 
Nish loves reading Science Fiction, P G Wodehouse and Agatha Christie, and also fancies himself to be a decent writer of sorts. He has authored a romantic comedy Summer Love and Some more Cricket as well as a programming book – Extending MFC applications with the .NET Framework.
 
Nish's latest book C++/CLI in Action published by Manning Publications is now available for purchase. You can read more about the book on his blog.
 
Despite his wife's attempts to get him into cooking, his best effort so far has been a badly done omelette. Some day, he hopes to be a good cook, and to cook a tasty dinner for his wife.

Comments and Discussions

 
QuestionDetect Stack Trace in MFC Visual C++ .NET Pinmembercb0206123-Jun-08 21:29 
QuestionProblem with database exception....... PinmemberSujitmatrix25-May-06 1:50 
I hv developed a database application using Ms Access.
Now i want 2 handle the empty database exception i.e
when first the database contains 0 data and my application throws exception ,i am not able to trace it
Please tell me how to handle this kind of exception!Confused | :confused:

 
!sujitbhai!
!Only For U!
QuestionHow to decide what type of exception in catch(...) block? Pinmembermicrospy10-Nov-05 23:38 
Generalasdf PinsussAnonymous7-Jul-05 3:18 
GeneralRe: asdf PinsussAnonymous7-Jul-05 3:19 
GeneralRe: asdfasdf PinsussAnonymous7-Jul-05 3:20 
GeneralRe: asdfasdfasdf PinsussAnonymous7-Jul-05 3:21 
GeneralRe: asdfasdfasdf PinstaffNishant Sivakumar7-Jul-05 19:49 
General75..Notout...(OT) PinmemberKant9-Aug-03 5:45 
GeneralRe: 75..Notout...(OT) PineditorNishant S9-Aug-03 6:07 
GeneralOn exception handling... PinmemberStephane Rodriguez.9-Aug-03 2:58 
GeneralRe: On exception handling... PineditorNishant S9-Aug-03 3:25 
GeneralRe: On exception handling... PinmemberTim Stubbs26-Aug-03 23:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.1411022.1 | Last Updated 9 Aug 2003
Article Copyright 2003 by Nish Sivakumar
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid