来自异步线程的内部异常Ower在.Net中写入外部异常

时间:2018-11-29 15:58:31

标签: c# .net exception-handling async-await

背景: 我有一个异步调用,在某个深处的地方引发了异常... 在我的实际应用程序中,它是一个异步运行的插件,因此我想包装该异常并用该异常起源的插件名称装饰该异常。

但是.Net丢弃了外部异常,因此丢失了其他详细信息,

我制作了一个带有两个按钮的示例Windows窗体应用程序,并设置了一个常规异常处理程序

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

然后一个按钮:

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

另一个按钮:

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

当我

throw new Exception("B", a); 

在上下文中,异常“ A”发生在Application_ThreadException回调中,没有任何痕迹'B'... B从未出现,这不是我期望的。

当我

throw new Exception("B");

Application_ThreadException中出现异常“ B”,而没有出现异常“ A”,这在我看来是可以理解的

在我的“真实”应用程序中, 我正在考虑进行一次“ hack”操作,然后将“ A” .ToString()添加到我的“ B”异常中,但是我知道这很糟糕,而且我确信我们有很多逻辑希望能够包装异常< / p>

顺便说一句...在OnComplete中使用后台处理程序进行异常处理的工作方式与此处的“异步”代码相同...

可以在此处找到示例: https://github.com/buildcomplete/dotnet-winforms-async-exceptionhandling

编辑: 在查看了建议的Why does the inner exception reach the ThreadException handler and not the actual thrown exception?相似之后,并提供了答案的答案,我实际上对停止这种行为更感兴趣。

现在我做了一个丑陋的攻击...当引发包装异常时,我将包装B包裹在一个新的异常ProtectedFromASyncUnfoldException中。

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

这破坏了InnerExceptions的正常行为...这真的不是我不想做的...更好的解决方法吗?

这是Hack类:

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

0 个答案:

没有答案