|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionThe 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 Note - In all the example code snippets, you'll see a #define Show Console::WriteLine
Throwing exceptionsManaged extensions are thrown using the void ThrowFunction()
{
Show("in ThrowFunction");
throw new Exception("asdf");
}
In the above example I have thrown an exception of type 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 Handling exceptionsWe handle exceptions by using 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 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
if( file_is_open )
{
//close file
}
Now this might seem okay at first glance, but assume that you have nested
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 Rethrowing exceptionsWhen you catch an exception inside a 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 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 Using custom exception classesYou 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 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 Multiple catch blocksIt's possible to have multiple 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 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 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 objectsLet's assume that for some reason we want a custom exception object that is a 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 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 void TryTest()
{
try
{
Test();
}
catch(Object* m)
{
MyValueClass v = *dynamic_cast<__box MyValueClass*>(m);
Show(v.u);
}
}
__try_cast and System::InvalidCastExceptionWhile this is not directly pertaining to a general discussion on managed
exceptions, I thought I'd mention void TestCast()
{
try
{
Object* obj = new Object();
String* str = __try_cast<String*>(obj);
}
catch(InvalidCastException* ice)
{
Show(ice->Message);
}
}
ConclusionI 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
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||