tags:

views:

3319

answers:

7

I am calling, through reflection, a method which may cause an exception. How can I pass the exception to my caller without the wrapper reflection puts around it? I am rethrowing the InnerException, but this destroys the stack trace. Example code:

    public void test1()
    {
        // Throw an exception for testing purposes
        throw new ArgumentException("test1");
    }

    void test2()
    {
        try
        {
            MethodInfo mi = typeof(Program).GetMethod("test1");
            mi.Invoke(this, null);
        }
        catch (TargetInvocationException tiex)
        {
            // Throw the new exception
            throw tiex.InnerException;
        }
    }
+17  A: 

I think your best bet would be to just do:

throw;

And then extract the innerexception later.

Geoffrey Chetwood
Or remove the try/catch altogether.
Daniel Earwicker
@Earwicker. Removing the try/catch is not a good solution in general as it ignores cases where cleanup code is required prior to propagating the exception up the call stack.
Jordan
@Jordan - Clean up code should be in a finally block not a catch block
Paolo
@Paolo - If it is supposed to be executed in every case, yes. If it is supposed to be executed only in failure case, no.
chiccodoro
+8  A: 

Even more reflection...

        catch (TargetInvocationException tiex)
        {
            // Get the _remoteStackTraceString of the Exception class
            FieldInfo remoteStackTraceString = typeof(Exception)
                .GetField("_remoteStackTraceString",
                    BindingFlags.Instance | BindingFlags.NonPublic); // MS.Net

            if (remoteStackTraceString == null)
                remoteStackTraceString = typeof(Exception)
                .GetField("remote_stack_trace",
                    BindingFlags.Instance | BindingFlags.NonPublic); // Mono

            // 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;
        }

Keep in mind that this may break at any time, as private fields are not part of API. See further discussion on Mono bugzilla.

skolima
This is a really, really bad idea, as it depends on internal undocumented details about framework classes.
Daniel Earwicker
@Earwicker: +1 this is a reaaaallly bad idea
Richard Szalay
Turns out it's possible to preserve the stack trace without Reflection, see below.
Anton Tykhyy
Calling the internal `InternalPreserveStackTrace` method would be better, since it does the same thing and is less likely to change in the future...
Thomas Levesque
Actually, it would be worse, as InternalPreserveStackTrace does not exist on Mono.
skolima
+4  A: 

First: don't loose the TargetInvocationException - it's valuable information when you will want to debug things.
Second: Wrap the TIE as InnerException in your own exception type and put an OriginalException property that links to what you need (and keep the entire callstack intact).
Third: Let the TIE bubble out of your method.

kokos
A: 

I tried the accepted solution posted above, but it did not work for me. If I look inside the exception, the StackTrace is correct, but Microsoft Visual Studio 2008 does not display the correct Stack Trace in the "Call Stack" tab.

It would be nice for MSVS to take me to where the original exception occured, and display the correct stack trace in the Call Stack tab. Did the previous solution become out-dated?

morrog
The previous solution is a (very) bad hack. Please don't use it. And yes, it's a distinct possibility that it did break.
Matthew Scharley
That's because Visual Studio shows the real stack trace, while the accepted answer hacks the exception's record of the stack trace. The only way to change the real stack trace is to change what calls what.
romkyns
+5  A: 
public static class ExceptionHelper
{
    private static Action<Exception> _preserveInternalException;

    static ExceptionHelper()
    {
        MethodInfo preserveStackTrace = typeof( Exception ).GetMethod( "InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic );
        _preserveInternalException = (Action<Exception>)Delegate.CreateDelegate( typeof( Action<Exception> ), preserveStackTrace );            
    }

    public static void PreserveStackTrace( this Exception ex )
    {
        _preserveInternalException( ex );
    }
}

Call the extension method on your exception before you throw it, it will preserve the original stack trace.

Eric
Be aware that in .Net 4.0, InternalPreserveStackTrace is now a no-op - look in Reflector and you'll see the method is completely empty!
Samuel Jack
Scratch that: I was looking at the RC: in the beta, they've put the implementation back again!
Samuel Jack
+2  A: 

Guys, you are cool.. I'm gonna be a necromancer soon.

    public void test1()
    {
        // Throw an exception for testing purposes
        throw new ArgumentException("test1");
    }

    void test2()
    {
            MethodInfo mi = typeof(Program).GetMethod("test1");
            ((Action)Delegate.CreateDelegate(typeof(Action), mi))();

    }
Boris Treukhov
Nice idea, but you don't always control the code which calls `.Invoke()`.
Anton Tykhyy
And you don't always know the types of the arguments/result at compile time either.
romkyns
+11  A: 

It is possible to preserve the stack trace before rethrowing without reflection:

static void PreserveStackTrace (Exception e)
{
    var ctx = new StreamingContext  (StreamingContextStates.CrossAppDomain) ;
    var mgr = new ObjectManager     (null, context) ;
    var si  = new SerializationInfo (e.GetType (), new FormatterConverter ()) ;

    e.GetObjectData    (si, ctx)  ;
    mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData
    mgr.DoFixups       ()         ; // ObjectManager calls SetObjectData

    // voila, e is unmodified save for _remoteStackTraceString
}

This wastes a lot of cycles compared to calling InternalPreserveStackTrace via cached delegate, but has the advantage of relying only on public functionality. Here are a couple of common usage patterns for stack-trace preserving functions:

// usage (A): cross-thread invoke, messaging, custom task schedulers etc.
catch (Exception e)
{
    PreserveStackTrace (e) ;

    // store exception to be re-thrown later,
    // possibly in a different thread
    operationResult.Exception = e ;
}

// usage (B): after calling MethodInfo.Invoke() and the like
catch (TargetInvocationException tiex)
{
    PreserveStackTrace (tiex.InnerException) ;

    // unwrap TargetInvocationException, so that typed catch clauses 
    // in library/3rd-party code can work correctly;
    // new stack trace is appended to existing one
    throw tiex.InnerException ;
}
Anton Tykhyy
-1 for advertising your blog instead of simply posting your (useful) solution.
Eric
You have a point, although I seriously doubt whether many people here are interested in a Russian-language blog.
Anton Tykhyy
+1 now that it's fixed.
Eric
Looks cool, what needs to happen after running these functions?
vdboor
@vdboor: I don't quite understand your question. Did the edit clarify things?
Anton Tykhyy
@Anton Tykhyy: That's exactly what I was looking for, thanks!
vdboor
Actually, it's not much slower than invoking `InternalPreserveStackTrace` (about 6% slower with 10000 iterations). Accessing the fields directly by reflection is about 2.5% faster than invoking `InternalPreserveStackTrace`
Thomas Levesque
@Thomas: when using `InternalPreserveStackTrace`, it is much faster to create a delegate for it instead of calling with reflection every time. I compared to the former scenario.
Anton Tykhyy