为什么从GUI线程抛出的AggregateException在应用程序异常处理程序中被“解包”?

时间:2016-04-06 15:29:45

标签: c# exception-handling async-await

我有一个WinForm异步GUI应用程序,我在program.cs中设置了一些“全局”异常处理。我还有一个GUI线程正在执行“等待Task.WhenAll()”并捕获其异常并抛出等待的Task.Exception属性,以便AggregateException一直到program.cs中的异常处理程序(我想要迭代内部异常并记录它们。)

我可以看到从myAll()的try / catch中抛出的异常确实抛出了AggreateException,但是当我在program.cs中调试处理程序时,它不再是AggregateException - 它只是第一个异常AggregateException。我无法弄清楚是什么代码正在为我做这个“解缠”?

的Program.cs:

static void Main() {
    Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
    Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
    ...
    }

static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) {
            if (e.Exception is AggregateException) {
                // expect to log the contents of (e.Exception as AggregateException).Flatten().InnerExceptions, but exception coming 
                // in is not AggregateException but instead is
                // ApplicationException("message 1")
                }
            else {
                // handling for non aggregate exceptions
                }

在Form1.cs

private async void button1_Click(object sender, EventArgs e) {

            Task overall = Task.WhenAll(
                Task.Run(()=>  { throw new ApplicationException("message 1"); }),
                Task.Run(() => { throw new ApplicationException("message 2"); })
                );

            try {
                await overall;
                }
            catch {
                throw overall.Exception; // this is AggregateException
                }
            }
        }

2 个答案:

答案 0 :(得分:2)

不只是AggregateException - WinForms 总是只向处理程序发送GetBaseException()。也就是说,只是任何InnerException链的最内层例外。

显然这是longstanding WinForms bug,此时可能是永久性的。

您必须使用自己的类型解决它:

public class ExceptionWrapper : Exception
{
    public new Exception InnerException { get; set; }
}

throw new ExceptionWrapper { InnerException = overall.Exception };

答案 1 :(得分:0)

this issue的最佳解决方法是通过AppDomain.FirstChangeException事件捕获引发的异常,然后将此异常基本异常引用与Application.ThreadException引发的异常进行比较。

这样的事情:

private Exception lastFirstChanceException;
AppDomain.CurrentDomain.FirstChanceException += (sender, e) =>
{
   lastFirstChanceException = e.Exception;
};
Application.ThreadException += (sender, e) =>
{
   if (lastFirstChanceException?.GetBaseException() == e.Exception)
   {
       var realException = lastFirstChanceException; // This is the "real" exception thrown by some code
   }
};