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

问题描述 投票:0回答:2

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

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

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
                }
            }
        }
c# exception-handling async-await
2个回答
2
投票

它不仅仅是AggregateException - WinForms总是只会将GetBaseException()发送给处理程序。也就是说,只有任何InnerException链的最里面的例外。

显然这是一个longstanding WinForms bug,可能是永久性的。

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

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

throw new ExceptionWrapper { InnerException = overall.Exception };

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
   }
};
© www.soinside.com 2019 - 2024. All rights reserved.