views:

851

answers:

7

Duplicate of: http://stackoverflow.com/questions/57383/in-c-how-can-i-rethrow-innerexception-without-losing-stack-trace

I have some operations that I invoke asynchronously on a background thread. Sometimes, things go bad. When this happens, I tend to get a TargetInvocationException, which, while appropriate, is quite useless. What I really need is the TargetInvocationException's InnerException, like this:

    try
    {
        ReturnValue = myFunctionCall.Invoke(Target, Parameters);
    }
    catch (TargetInvocationException err)
    {
        throw err.InnerException;
    }

That way, my callers are served up with the REAL exception that occured. The problem is, that the throw statement seems to reset the stack trace. I'd like to basically rethrow the inner exception, but keep the stack trace it originally had. How do I do that?

CLARIFICATION: The reason I want only the inner exception is that this class tries to 'abstract away' the whole fact that these functions (delegates supplied by caller) are run on other threads and whatnot. If there is an exception, then odds are it has nothing to do with being run on a background thread, and the caller would really like the stack trace that goes into their delegate and finds the real issue, not my call to invoke.

+5  A: 

No, that isn't possible. Your only real opportunity is to follow the recommended pattern and throw your own exception with the appropriate InnerException.

Edit

If your concern is the presence of the TargetInvocationException and you want to disregard it (not that I recommend this, as it could very well have something to do with the fact that it's being run on another thread) then nothing is stopping you from throwing your own exception here and attaching the InnerException from the TargetInvocationException as your own InnerException. It's a little smelly, but it might accomplish what you want.

Adam Robinson
It *is* possible, see below.
Anton Tykhyy
@Anton: That's definitely an interesting find. I don't really like the idea of being able to strip off an outer exception--even `TargetInvocationException`--but clearly there is a significant portion of the community that does.
Adam Robinson
Yes, because often there's no other way — library and third-party code seldom understands `TargetInvocationException` et al., and even in our own code handling it everywhere it might conceivably pop up is no end of a bother, what with exception type checks duplicating the catch clauses and stuff.
Anton Tykhyy
A: 

You can't do that. throw always resets the stack trace, unless used without parameter. I'm afraid your callers will have to use the InnerException...

Thomas Levesque
A: 

Using the "throw" keyword with an exception will always reset the stack trace.

The best thing to do is to catch the actual exception you want, and use "throw;" instead of "throw ex;". Or to throw your own exception, with the InnerException that you want to pass along.

I don't believe what you want to do is possible.

Nate Bross
+1  A: 

Although you may feel that the TargetInvocationException is "useless", it's the reality. Don't try to pretend that .NET didn't take the original exception and wrap it with a TargetInvocationException and throw it. That really happened. Some day, you might even want some piece of information that comes from that wrapping - like maybe the location of the code that threw the TargetInvocationException.

John Saunders
+2  A: 

There is a way of "resetting" the stack trace on an exception by using the internal mechanism that is used to preserve server side stack traces when using remoting, but it is horrible:

try
{
    // some code that throws an exception...
}
catch (Exception exception)
{
    FieldInfo remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
    remoteStackTraceString.SetValue(exception, exception.StackTrace);
    throw exception;
}

This puts the original stack trace in the _remoteStackTraceString field of the exception, which gets concatenated to the newly reset stack trace when the exception is re-thrown.

This is really a horrible hack, but it does achieve what you want. You are tinkering inside the System.Exception class though so this method may therefore break in subsequent releases of the framework.

adrianbanks
this is awesome, for when you have a *genuine* need to rethrow an exception in a different place but keep the old stack trace (like in a unit testing framework)
Rob Fonseca-Ensor
A: 

As others have said, use the "throw" keyword without adding to it to keep the exception chain intact. If you need that original exception (assuming that is what you mean) then you could call Exception.GetBaseException() at the end of your chain to get the Exception that started it all.

Zensar
+2  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 InternalPreserveStackTrace, 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