1

Background: I have a async call, that somewhere deep down throws an exception... In my real application, it is a plugin that is running async, so I would like to wrap the exception and decorate the exception with the plugin name that the exception originates from.

But .Net throws away the outer exception, so additional details are lost,

I have made an example windows forms application with two buttons, and setup a general exception handler

Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

Then one button:

private async void buttonThrowAndWrapExceptionAsync_Click(object sender, EventArgs e)
{
    _textBoxToSetTextOnGivenLastCall = textBox1;
    try
    {
        await Task.Run(() =>
            ThrowExceptionA());
    }
    catch (Exception a)
    {
        throw new Exception("B", a);
    }
}

the other button:

private async void buttonThrowAndRethrowNewExceptionAsync_Click(object sender, EventArgs e)
{
    _textBoxToSetTextOnGivenLastCall = textBox2;
    try
    {
        await Task.Run(() =>
            ThrowExceptionA());
    }
    catch (Exception)
    {
        throw new Exception("B");
    }
}

When I

throw new Exception("B", a); 

in the context, Exception "A" occurs in the Application_ThreadException callback with no trace of 'B'... B does never appear, this is not what I would expect.

When I

throw new Exception("B");

Exception "B" appears in Application_ThreadException, and Exception "A" does not appear, this is to me understandable

In my 'real' application, I am contemplating making a 'hack' and just adding "A".ToString() to my "B" exception, but I know it is pretty bad, and I am sure we have lots of logic expecting to be able to wrap exceptions

BTW... using a background worker with exception handling in OnComplete works in the same way as the 'async' code here...

The example can be found here: https://github.com/buildcomplete/dotnet-winforms-async-exceptionhandling

EDIT: After viewing the suggested Why does the inner exception reach the ThreadException handler and not the actual thrown exception? wich is simelar, and provides an answer to why, I am actually more interested in stopping this behaviour.

For now I have made an ugly hack... When Throwing my wrapper Exception, I wrap the wrapper B in a new Exception ProtectedFromASyncUnfoldException

throw new ProtectedFromASyncUnfoldException(new Exception("B", a));

This breaks the normal behaviour of InnerExceptions... and is not really what I wan't to do... any better sollutions?

Here's the Hack class:

public class ProtectedFromASyncUnfoldException : Exception
{
    public Exception InternalException { get;  }
    public ProtectedFromASyncUnfoldException() {    }

    public ProtectedFromASyncUnfoldException(string message) 
        : base(message) {   }

    /// <summary>
    /// Hack of exception.
    /// Setting InternalException instead of innerException 
    /// to avoid strange behaviour of async exception handling in windows forms
    /// that otherwise throws away all but the base exception
    /// </summary>
    /// <param name="message"></param>
    /// <param name="innerException">transformed to InternalException</param>
    public ProtectedFromASyncUnfoldException(string message, Exception innerException) 
        : base(message)
    {
        InternalException = innerException;
    }

    public ProtectedFromASyncUnfoldException(Exception innerException) 
        : this(innerException.Message, innerException) { }

    protected ProtectedFromASyncUnfoldException(SerializationInfo info, StreamingContext context) 
        : base(info, context) { }

    public override string ToString()
    {
        return base.ToString() 
            + Environment.NewLine 
            + Environment.NewLine 
            + "---------------------" 
            + InternalException?.ToString();
    }
}
buildcomplete
  • 552
  • 2
  • 15
  • 1
    Somehow, when an event such as buttonThrowAndWrapExceptionAsync_Click is marked as async, only the inner-most exception is being raised to Application.ThreadException... Strange. – Gerardo Grignoli Nov 29 '18 at 17:02
  • Yes @NineBerry, Seems like https://stackoverflow.com/a/348115/97471 is the answer to this one. – Gerardo Grignoli Nov 29 '18 at 17:07
  • It seems very related, but there is something I don't really understand still... I tried overwriting 'GetBaseException' in a new hack class 'ProtectedFromASyncUnfoldException : Exception', but it doesn't get called (not hit by debugger), I tried to let it return null, no change... – buildcomplete Nov 30 '18 at 08:22

0 Answers0