Repost from my original blog at the dotnetjunkies (03 March 2004)
One of the problems when throwing a new exception from with-in a catch handler is that the stack trace information captured for the initial exception is lost and a new stack trace is built from the point of the throw. Of course when throwing the new exception all that is required to circumvent this problem is to set the inner exception of the new exception to the initial exception that was caught by the catch handler.
However, on occasion, you want to throw the InnerException it self. Recently on the SADeveloper forum exactly this scenario arose. A poster on the forum was using reflection to invoke a method. The poster realized that exceptions thrown by methods invoked through reflection were wrapped in the InnerException of a TargetInvocationException. Now what the poster wanted to do was to catch the TargetInvocationException and then re-throw the InnerException. I assume the reason for this was that the caller of the method, which performed the reflection invoke, was not concerned with the current implementation detail and was only required to deal with the original exceptions. The only problem with this solution is that once the InnerException is rethrown, the stack trace no longer contained the original stack trace information.
Naturally curiosity got the better of me and I set out to see if I could find a solution. After all, this is what I enjoy most about my free time (between 2-3 in the morning), I get to do things I enjoy and that is learn how the internals work. On this occasion, I got lucky and the first idea I had worked. I new that the remoting infrastructure in fact did something very similar. When an exception is throw from the remoting server, the server side stack trace is preserved on the client side. This is done by setting the private member _remoteStackTraceString to the server side stack trace. Then when the StackTrace property of the exception is called, the _remoteStackTraceString is concatenated with the _stackTraceString and returned to the caller. So I figured that all that was required was the set the _remoteStackTraceString of the InnerException to the current StackTrace of the InnerException. Then when the InnerException is re thrown, the stack trace is replaced with the new stack trace, but the _remoteStackTraceString is preserved. And the end result is a complete stack trace as if the original exception being throw never even saw a TargetInvocationException. So the question is how do we go about manipulating the private _remoteStackTraceString, well you guessed it, reflection.
As usual, before I present the code for this I have to say, just because something works does not mean it is the right thing to do. But is was Fun... And now I know a little more about how the remoting mechanism and exceptions fit together.
public void test1()
{
// Throw an exception for testing purposes
throw new ArgumentException( "test1" );
}
void test2()
{
try
{
MethodInfo mi = typeof(Form1).GetMethod( "test1" );
mi.Invoke( this, null );
}
catch( TargetInvocationException tiex )
{
// NB: Error checking etc. excluded
// Get the _remoteStackTraceString of the Exception class
FieldInfo remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString",
BindingFlags.Instance | BindingFlags.NonPublic );
// Set the InnerException._remoteStackTraceString to the current InnerException.StackTrace
remoteStackTraceString.SetValue( tiex.InnerException,
tiex.InnerException.StackTrace + Environment.NewLine );
// Throw the new exception
throw tiex.InnerException;
}
}
...
// Test the effect of the above code by calling test2() and handling
// the exception that is thrown
try
{
test2(); // Call the method that uses reflection to call another method
}
catch ( Exception ex )
{
Console.WriteLine( ex.ToString() );
}
No comments:
Post a Comment