329
votes

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;
    }
}
10
There is another way to do this that doesn't require any voodoo. Take a look at the answer here: stackoverflow.com/questions/15668334/… - Timothy Shields
The exception thrown in the dynamically called method is the inner exception of the "Exception has been thrown by the target of an invocation" exception. It has its own stack trace. There really is not much else there to worry about. - ajeh
use mi.Invoke(this, BindingFlags.DoNotWrapExceptions, null, null, null) - Wouter

10 Answers

523
votes

In .NET 4.5 there is now the ExceptionDispatchInfo class.

This lets you capture an exception and re-throw it without changing the stack-trace:

using ExceptionDispatchInfo = System.Runtime.ExceptionServices.ExceptionDispatchInfo;

    try
    {
        task.Wait();
    }
    catch(AggregateException ex)
    {
        ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
    }

This works on any exception, not just AggregateException.

It was introduced due to the await C# language feature, which unwraps the inner exceptions from AggregateException instances in order to make the asynchronous language features more like the synchronous language features.

86
votes

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, ctx) ;
    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 ;
}
34
votes

I think your best bet would be to just put this in your catch block:

throw;

And then extract the innerexception later.

25
votes

Nobody has explained the difference between ExceptionDispatchInfo.Capture( ex ).Throw() and a plain throw, so here it is.

The complete way to rethrow a caught exception is to use ExceptionDispatchInfo.Capture( ex ).Throw() (only available from .Net 4.5).

Below there are the cases necessary to test this:

1.

void CallingMethod()
{
    //try
    {
        throw new Exception( "TEST" );
    }
    //catch
    {
    //    throw;
    }
}

2.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        ExceptionDispatchInfo.Capture( ex ).Throw();
        throw; // So the compiler doesn't complain about methods which don't either return or throw.
    }
}

3.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch
    {
        throw;
    }
}

4.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        throw new Exception( "RETHROW", ex );
    }
}

Case 1 and case 2 will give you a stack trace where the source code line number for the CallingMethod method is the line number of the throw new Exception( "TEST" ) line.

However, case 3 will give you a stack trace where the source code line number for the CallingMethod method is the line number of the throw call. This means that if the throw new Exception( "TEST" ) line is surrounded by other operations, you have no idea at which line number the exception was actually thrown.

Case 4 is similar with case 2 because the line number of the original exception is preserved, but is not a real rethrow because it changes the type of the original exception.

13
votes
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.

10
votes

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.

10
votes

First: don't lose 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.

9
votes

Based on Paul Turners answer I made an extension method

    public static Exception Capture(this Exception ex)
    {
        ExceptionDispatchInfo.Capture(ex).Throw();
        return ex;
    }

the return ex ist never reached but the advantage is that I can use throw ex.Capture() as a one liner so the compiler won't raise an not all code paths return a value error.

    public static object InvokeEx(this MethodInfo method, object obj, object[] parameters)
    {
        {
            return method.Invoke(obj, parameters);
        }
        catch (TargetInvocationException ex) when (ex.InnerException != null)
        {
            throw ex.InnerException.Capture();
        }
    }
5
votes

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))();

    }
3
votes

Anpother sample code which uses exception serialization/deserialization. It does not require the actual exception type to be serializable. Also it uses only public/protected methods.

    static void PreserveStackTrace(Exception e)
    {
        var ctx = new StreamingContext(StreamingContextStates.CrossAppDomain);
        var si = new SerializationInfo(typeof(Exception), new FormatterConverter());
        var ctor = typeof(Exception).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }, null);

        e.GetObjectData(si, ctx);
        ctor.Invoke(e, new object[] { si, ctx });
    }