Search code examples
c#exceptionstack-tracetargetinvocationexceptioninner-exception

How to rethrow the inner exception of a TargetInvocationException without losing the stack trace


I have many methods which are calling using Delegate.DynamicInvoke. Some of these methods make database calls and I would like to have the ability to catch a SqlException and not catch the TargetInvocationException and hunt through its inners to find what's actually gone wrong.

I was using this method to rethrow but it clears the stack trace:

 try
 {
      return myDelegate.DynamicInvoke(args);
 }
 catch(TargetInvocationException ex)
 {
     Func<TargetInvocationException, Exception> getInner = null;
     getInner =
        delegate(TargetInvocationException e)
        {
        if (e.InnerException is TargetInvocationException)
            return getInner((TargetInvocationException) e.InnerException);

         return e.InnerException;
        };

     Exception inner = getInner(ex);
     inner.PreserveStackTrace();
     throw inner;
 }

The PreserveStackTrace method is an extension method I fixed up thanks to another post (I don't know what it actually does). However, this doesn't appear to preserve the trace either:

public static void PreserveStackTrace(this 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);
    mgr.DoFixups(); 
}

Solution

  • If you just want to re-throw an inner exception preserving its stack trace, you can do it with a method like this:

    public static void Rethrow(this Exception ex)
    {
      typeof(Exception).GetMethod("PrepForRemoting",
          BindingFlags.NonPublic | BindingFlags.Instance)
          .Invoke(ex, new object[0]);
      throw ex;
    }
    

    This technique is used by Rx (and is exposed by them as an extension method Exception.PrepareForRethrow) and is also used by the Async CTP by its automatic-unwrapping system (without a publicly-exposed API).

    Note, however, that this technique is technically unsupported. Hopefully Microsoft will add an official API for this in the future. A suggestion has been opened on Microsoft Connect if you would like to vote for it.

    Update: An official API has been added to .NET 4.5: ExceptionDispatchInfo.