【问题标题】:Why is the AppDomain.CurrentDomain.UnhandledException not catching unhandled exceptions from non-UI threads?为什么 AppDomain.CurrentDomain.UnhandledException 没有从非 UI 线程捕获未处理的异常?
【发布时间】:2018-07-01 14:50:00
【问题描述】:

我阅读了一些其他线程和文章,这些文章表明有一种方法可以捕获源自非 UI 线程的未处理异常。出于某种原因,这对我不起作用。未处理的 UI 异常被捕获。即使我通过在非 UI 线程中抛出异常进行测试,CurrentDomainUnhandledException 也永远不会执行。

更奇怪的是,非 UI 线程上的异常并没有像我想象的那样关闭应用程序。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Application.Current.DispatcherUnhandledException += CurrentDispatcherUnhandledException;

        AppDomain.CurrentDomain.UnhandledException +=
               CurrentDomainUnhandledException;
    }

    void CurrentDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
    {
        e.Handled = true;
        MessageBox.Show(e.Exception.Message, "Caught an unhandled exception!");
    }

    void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Exception ex = e.ExceptionObject as Exception;
        MessageBox.Show(ex.Message, "Uncaught Thread Exception",
                        MessageBoxButton.OK, MessageBoxImage.Error);
    }
}

我从一个处理插入的设备的事件处理程序中抛出,该设备绝对不是 UI 线程。调试器中断并向我显示异常未处理的消息。但是,当我继续时,应用程序继续运行并且似乎继续工作。我对此感到困惑。

_devicePlugWatcher = new ManagementEventWatcher(pluggedQueryStr);
_devicePlugWatcher.EventArrived += (DevicePluggedEventReceived);

【问题讨论】:

  • 确定没有显示消息框吗?由于调试器将 Visual Studio 带到前台,它可能隐藏在后台。如果您确定,请尝试使用Dispatcher.[Begin]Invoke 打开 UI 线程上的消息框。
  • 我在 CurrentDomainUnhandledException 中设置了一个断点,它永远不会被命中。我明白你的意思,如果执行了该代码,那么我可能应该实施该建议。目前处理程序永远不会执行。
  • 您确定没有捕获到异常吗?调试器可能会中断第一次机会异常,即使它们后来被捕获。
  • 您需要切换回 UI 线程才能显示 MessageBox。工作线程中没有消息循环,因此没有 UI 交互。使用 `CheckAccess' 方法来确定可以在哪里显示消息框或切换到 UI 线程。
  • @Mike,我不明白你的意思。如果我在 CurrentDomainUnhandledException 中设置断点,那么为什么调试器不会在那里中断?

标签: c# wpf


【解决方案1】:

如果在非线程池线程(或非Task)上引发异常,则应调用AppDomain.CurrentDomain.UnhandledException 事件处理程序。试试这个例子:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException;

        Thread thread = new Thread(() => 
        {
            Thread.Sleep(5000);
            throw new Exception("test");
        });
        thread.Start();
    }

    void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Exception ex = e.ExceptionObject as Exception;
        MessageBox.Show(ex.Message, "Uncaught Thread Exception",
                        MessageBoxButton.OK, MessageBoxImage.Error);
    }
}

并将其与发生在未观察到的Task 内的异常进行比较:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

        Task.Run(() =>
        {
            throw new Exception("test");
        });

        Loaded += (s, e) => 
        {
            //gc so the unobserved exception "bubbles" up
            GC.Collect(3, GCCollectionMode.Forced, true);
        };
    }

    private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
    {
        Exception ex = e.Exception;
        MessageBox.Show(ex.Message, "Uncaught Thread Exception",
                        MessageBoxButton.OK, MessageBoxImage.Error);
    }
}

所以你也应该处理TaskScheduler.UnobservedTaskException

【讨论】:

  • 好的。我会尝试一下,看看会发生什么。有问题的例外是在我没有创建的线程中。指示何时插入或拔出 USB 端口的设备的 Windows 管理事件是我将异常作为测试的地方。我不知道那是什么类型的线程。如果该示例对我有用,我会回复并接受答案。感谢您发布一些代码。
  • 查看 Visual Studio 显示的线程,似乎 System.Management.dll!System.Management.MTAHelper.WorkerThread 是发布设备插入和拔出事件的线程。因此我不相信我需要TaskScheduler_UnobservedTaskException。如果我处理拔出串行设备的 Windows 管理事件,并且我插入代码以引发未处理的异常,则调试器将中断并发出未处理的消息。但是 CurrentDomainUnhandledException 事件处理程序不会执行。
  • 看看这个 git hub 项目的一些代码,它显示了我是如何实现设备检测的。 github.com/Shawn1874/CodeSamples/tree/master/WpfSamples/…
  • 基本上添加第三个异常处理程序对我不起作用。没有人从 System.Management.dll!System.Management.MTAHelper.WorkerThrre‌​ad 捕获未处理的异常
猜你喜欢
  • 2011-05-20
  • 2018-10-15
  • 2013-03-09
  • 1970-01-01
  • 2011-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多