Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C++/CLI
Article

Managed Exception Handling using VC++.NET

Rate me:
Please Sign up or sign in to vote.
4.91/5 (25 votes)
8 Aug 20037 min read 125.4K   16   13
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 :-

MC++
#define Show Console::WriteLine

Throwing exceptions

Managed extensions are thrown using the throw statement.

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

In the above example I have thrown an exception of type

MC++
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

MC++
Exception
constructor that accepts a
MC++
System::String
just saves the passed string to a member variable called
MC++
_message
and this is then exposed through a read-only property called
MC++
Message
. In addition the CLR also prints out a stack trace by using the
MC++
StackTrace
property of the
MC++
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
MC++
Message
and
MC++
StackTrace
properties of the
MC++
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.

MC++
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

MC++
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
MC++
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 :-

MC++
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.

MC++
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.

MC++
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

MC++
throw
statement without any parameters inside the inner
MC++
catch
block. This will propagate the same exception object to the outer
MC++
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

MC++
catch
block, you'll see that the outer catch block is never reached because the exception was handled in the inner
MC++
try
-catch block, but both
MC++
__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

MC++
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.

MC++
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

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

Multiple catch blocks

It's possible to have multiple

MC++
catch
blocks in any
MC++
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 :-

MC++
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 :-

MC++
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

MC++
catch
block above the MyException
MC++
catch
block it would then catch both Exception type exceptions and MyException type exceptions, which would be wholly undesirable, because the specific MyException
MC++
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

MC++
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.

MC++
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

MC++
__value
MC++
class
or
MC++
__value
struct
(not derived from
MC++
System::Exception
obviously)

MC++
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

MC++
__value
object before throwing it, as shown below :-

MC++
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

MC++
System::Object
and then unbox it to access the actual exception object.

MC++
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

MC++
System::InvalidCastException
exception.

MC++
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


Written By
United States United States
Nish Nishant is a technology enthusiast from Columbus, Ohio. He has over 20 years of software industry experience in various roles including Chief Technology Officer, Senior Solution Architect, Lead Software Architect, Principal Software Engineer, and Engineering/Architecture Team Leader. Nish is a 14-time recipient of the Microsoft Visual C++ MVP Award.

Nish authored C++/CLI in Action for Manning Publications in 2005, and co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his WordPress blog. Nish is experienced in technology leadership, solution architecture, software architecture, cloud development (AWS and Azure), REST services, software engineering best practices, CI/CD, mentoring, and directing all stages of software development.

Nish's Technology Blog : voidnish.wordpress.com

Comments and Discussions

 
QuestionDetect Stack Trace in MFC Visual C++ .NET Pin
cb0206123-Jun-08 20:29
cb0206123-Jun-08 20:29 
QuestionProblem with database exception....... Pin
Sujitmatrix25-May-06 0:50
Sujitmatrix25-May-06 0:50 
QuestionHow to decide what type of exception in catch(...) block? Pin
microspy10-Nov-05 22:38
microspy10-Nov-05 22:38 
Generalasdf Pin
Anonymous7-Jul-05 2:18
Anonymous7-Jul-05 2:18 
GeneralRe: asdf Pin
Anonymous7-Jul-05 2:19
Anonymous7-Jul-05 2:19 
GeneralRe: asdfasdf Pin
Anonymous7-Jul-05 2:20
Anonymous7-Jul-05 2:20 
GeneralRe: asdfasdfasdf Pin
Anonymous7-Jul-05 2:21
Anonymous7-Jul-05 2:21 
GeneralRe: asdfasdfasdf Pin
Nish Nishant7-Jul-05 18:49
sitebuilderNish Nishant7-Jul-05 18:49 
General75..Notout...(OT) Pin
Kant9-Aug-03 4:45
Kant9-Aug-03 4:45 
GeneralRe: 75..Notout...(OT) Pin
Nish Nishant9-Aug-03 5:07
sitebuilderNish Nishant9-Aug-03 5:07 
GeneralOn exception handling... Pin
Stephane Rodriguez.9-Aug-03 1:58
Stephane Rodriguez.9-Aug-03 1:58 
GeneralRe: On exception handling... Pin
Nish Nishant9-Aug-03 2:25
sitebuilderNish Nishant9-Aug-03 2:25 
GeneralRe: On exception handling... Pin
Tim Stubbs26-Aug-03 22:39
Tim Stubbs26-Aug-03 22:39 
...unless of course you pass in a value by reference which is returned to the callee Wink | ;)


Tim Stubbs

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

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