为啥内部异常到达 ThreadException 处理程序而不是实际抛出的异常?

Posted

技术标签:

【中文标题】为啥内部异常到达 ThreadException 处理程序而不是实际抛出的异常?【英文标题】:Why does the inner exception reach the ThreadException handler and not the actual thrown exception?为什么内部异常到达 ThreadException 处理程序而不是实际抛出的异常? 【发布时间】:2010-09-25 17:44:28 【问题描述】:

我在抛出异常并在Application.ThreadException 事件处理程序中捕获它们时看到一些奇怪的行为。

下面的示例中发生的基本上是在BackgroundWorkerDoWork 事件处理程序中引发了异常。 RunWorkerCompleted 事件处理程序重新抛出一个新异常,并将原始异常作为内部异常。

为什么在ThreadException 事件处理程序中出现内部异常,而不是实际抛出的异常?如果我没有在 RunWorkerCompleted 事件处理程序中提供内部异常,则会显示正确的异常。

using System;
using System.Windows.Forms;
using System.ComponentModel;

namespace WierdExceptionApp

    class WierdExceptionForm : Form
    
        BackgroundWorker worker = new BackgroundWorker();

        public WierdExceptionForm()
        
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
            worker.RunWorkerAsync();
        

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        
            if (e.Error != null)
            
                throw new Exception("worker_RunWorkerCompleted", e.Error);
            
        

        void worker_DoWork(object sender, DoWorkEventArgs e)
        
            throw new Exception("worker_DoWork");
        

        [STAThread]
        static void Main()
        
            Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
            Application.Run(new WierdExceptionForm());
        

        static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
        
            MessageBox.Show(e.Exception.Message);
        
   

【问题讨论】:

令人震惊;在我没有意识到的情况下,这一直在咬我多年。对于我可以发誓我处理过的事情,我会看到这些偶尔出现的错误报告。它们非常罕见,以至于没有花很多时间研究它们,但最终它开始发生在一些重要的事情上,我意识到例外情况正在发生变化。 WTF?? 【参考方案1】:

RunWorkerCompleted 事件由使 Control.Invoke() 工作的 WF 管道从 BGW 线程编组到 UI 线程。本质上,有一个由消息循环清空的委托队列。执行此操作的代码 Control.InvokeMarshaledCallbacks(),您将在调用堆栈上看到它,它有一个 catch (Exception) 子句来捕获未处理的异常。该子句调用 Application.OnThreadException,传递 Exception.GetBaseException() 的值。

嗯,这就解释了为什么你只看到内部异常。为什么这样做有点不清楚。可能会切掉 UI 线程中代码的堆栈帧,因为真正的异常来自后台线程。

【讨论】:

然而,WinForms 中的 另一个 错误,微软肯定不会修复! 那是多么愚蠢! MS 肯定不会解决这个问题,因为这将是他们眼中的“重大变化”。仍然让所有人都去connect.microsoft.com/VisualStudio/feedback/… 并注册问题。 试图在这里进一步解决这个问题:***.com/q/59146505/7453 我不认为它使用Exception.GetBaseException(),因为当我在我的异常类public override Exception GetBaseException() => this; 中覆盖它时,ThreadException 处理程序仍然会得到内部异常。 referencesource.microsoft.com/#System.Windows.Forms/winforms/…【参考方案2】:
        if (e.Error != null)
        
            throw new Exception("worker_RunWorkerCompleted", new Exception("Inner", new Exception("Inner inner")));
        

最后你会得到“内在的内在”。看来这是 Application_ThreadException 方法查看最里面的异常的行为。

【讨论】:

以上是关于为啥内部异常到达 ThreadException 处理程序而不是实际抛出的异常?的主要内容,如果未能解决你的问题,请参考以下文章

如何解决空指针异常和 ThreadException 中的错误android

想知道为啥空的内部迭代器会导致 mapPartitionsWithIndex 出现不可序列化的异常

各类异常捕获

java.io.EOFException这是个啥异常应该怎么解决

Application.ThreadException 和 AppDomain.CurrentDomain.UnhandledException 有啥区别?

C# 程序异常管理方案