【问题标题】:SynchronizationContext and DispatcherUnhandledExceptionSynchronizationContext 和 DispatcherUnhandledException
【发布时间】:2016-01-11 07:13:09
【问题描述】:

我遇到了这样一种情况,即 UI 线程中抛出的异常没有在调用线程中被捕获。

using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;

namespace SynchronisationContextAndExceptionWPF
{
    public partial class MainWindow : Window
    {
        private readonly SynchronizationContext _synchronizationContext;

        public MainWindow()
        {
            InitializeComponent();

            _synchronizationContext = SynchronizationContext.Current;
        }


        private void Button_OnClick(object sender, RoutedEventArgs e)
        {
            try
            {
                _synchronizationContext.Send(
                    x =>
                    {
                        try
                        {
                            DoSomethingOnUiThreadThatThrowsException();
                        }
                        catch (Exception)
                        {
                            Debug.WriteLine("Catched Exception in thread that threw it.");
                            throw;
                        }
                    }, null);
            }
            catch (Exception)
            {
                Debug.WriteLine("Catched Exception in thread that calles Send-Method.");
                throw;
            }
        }

        private static void DoSomethingOnUiThreadThatThrowsException()
        {
            throw new Exception("Any Exception...");
        }
    }
}

首先我认为这是不可能的(我发现的所有文档都说我可以在那里捕获异常)。 经过一番研究,我发现了问题:我的应用程序使用了 UnhandledExceptionHandler。处理DispatcherUnhandledException-Event。我正在向用户显示一些信息并设置e.Handled = true;

using System.Diagnostics;
using System.Windows;
using System.Windows.Threading;

namespace SynchronisationContextAndExceptionWPF
{
    public partial class App : Application
    {
        public App()
        {
            DispatcherUnhandledException += App_DispatcherUnhandledException;
        }

        private static void App_DispatcherUnhandledException(
            object sender,
            DispatcherUnhandledExceptionEventArgs e)
        {
            Debug.WriteLine("Catched Exception in UnhandledExceptionHandler.");

            // This line makes the difference:
            e.Handled = true;
        }
    }
}

所以问题是:为什么即使我处理了DispatcherUnhandledException-Event 也会引发它? 您将如何解决这种情况?

【问题讨论】:

    标签: c# dispatcher synchronizationcontext


    【解决方案1】:

    如果你有很多控件,你可以生成一个新的类来记住特殊的异常变量。因此,您只需要更改 _synchronizationContext 的初始化(希望只在控件的基类中更改一次)。

    public partial class MainWindow : Window
    {
        private readonly MySynchronizationContext _synchronizationContext;
    
        public MainWindow()
        {
            InitializeComponent();
    
            _synchronizationContext = new MySynchronizationContext(SynchronizationContext.Current);
    
        }
    
        private void button_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                _synchronizationContext.Send(
                    x =>
                    {
                        DoSomethingOnUiThreadThatThrowsException();
                    }, null);
            }
            catch (Exception)
            {
                Debug.WriteLine("Catched Exception in thread that calles Send-Method.");
                throw;
            }
        }
    
        private static void DoSomethingOnUiThreadThatThrowsException()
        {
            throw new Exception("Any Exception...");
        }
    }
    
    class MySynchronizationContext
    {
        SynchronizationContext innerContext;
    
        public MySynchronizationContext(SynchronizationContext ctx)
        {
            innerContext = ctx;
        }
    
        public virtual void Send(SendOrPostCallback d, object state)
        {
            Exception threadException = null;
    
            try
            {
                innerContext.Send(_ =>
                {
                    try
                    {
                        d.Invoke(state);
                    }
                    catch (Exception exception)
                    {
                        threadException = exception;
                    }
                }, null);
            }
            catch (Exception ex)
            {
    
            }
    
            if (threadException != null)
            {
                throw new Exception("Synchronization error", threadException);
            }
        }
    }
    

    【讨论】:

    • 这对我有用!这可能不是所有人的通用解决方案,但现在让我很开心。谢谢!
    【解决方案2】:

    在您的 lambda 表达式中,您可以设置一个异常变量并稍后在调用线程中检查此变量。如果已设置,则在调用线程时抛出异常。

    private void button_Click(object sender, RoutedEventArgs e)
        {
            Exception threadException = null;
    
            try
            {
                _synchronizationContext.Send(
                    x =>
                    {
                        try
                        {
                            DoSomethingOnUiThreadThatThrowsException();
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine("Catched Exception in thread that threw it.");
                            threadException = ex;
                            //throw; --> don't throw exception here; otherwise you will get DispatcherUnhandledException twice.
                        }
                    }, null);
            }
            catch (Exception)
            {
                Debug.WriteLine("Catched Exception in thread that calles Send-Method.");
                throw;
            }
    
            if(threadException != null)
            {
                Debug.WriteLine("Catched Exception in thread that calles Send-Method.");
                throw threadException; //throw you previously catched exception here.
            }
        }
    

    亲切的问候, 丹尼尔

    【讨论】:

    • 当然这会工作......但这意味着我必须记住每次都这样调用它......另外:如果我有一个模块化软件,我有一个这样的控件(和不知道 UnhandledExceptionHandler)他们不会按预期工作......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-13
    • 1970-01-01
    • 1970-01-01
    • 2015-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多