在一个线程上阻止多个MessageBox而不阻塞

时间:2014-03-04 12:12:12

标签: c# wpf multithreading

我在我的应用程序中注册了一个错误处理程序,它在发生未处理的异常时显示用户对话框MessageBox。它连接到Application.DispatcherUnhandledException事件,因此发生在UI线程上。现在我已经看到了一种定期抛出异常的情况,每秒一次,并且新的对话框不断弹出。我试图通过锁来防止这种情况,但由于它都在同一个线程上,因此没有任何效果。在同一个线程上额外的睡眠锁定显然导致屏幕上出现一个阻塞的MessageBox。

MessageBox似乎释放了用于显示其他MessageBox的UI线程。如何在不阻塞线程的情况下阻止它?

用户可以选择继续或退出应用程序。继续时,应该出现下一个排队的MessageBox,不应该丢弃它。如果一次出现太多消息,用户仍然可以决定退出该应用程序。

2 个答案:

答案 0 :(得分:1)

此处的问题不在于消息框。这是消息框仍然打开时发生的情况,同时发生了另一个异常。在这种情况下,您的DispatcherUnhandledException处理程序将重新输入相同的主题。

但是,用户尚未就上一个错误做出决定,之前的消息框仍在等待他的输入。您的调用堆栈如下所示:

=> DispatcherUnhandledExceptionHandler (current)
MessageBox.Show
DispatcherUnhandledExceptionHandler (previous)

显然,如果没有先从DispatcherUnhandledExceptionHandler (previous)返回,您就无法从DispatcherUnhandledExceptionHandler (current)返回:您正在相同主题的嵌套调用中。如果没有与用户确认这个拳头,你就不能从DispatcherUnhandledExceptionHandler (current)回来,回到鸡蛋。

我可以想到解决这个问题的一种方法,同时坚持你的问题要求。它是在另一个线程上显示消息框,并在等待用户选择时阻止主线程的消息循环。这样,在获得用户对当前异常的同意之前,主线程上不会再出现异常:

Application.Current.DispatcherUnhandledException += (s, e) =>
{
    this.IsEnabled = false; // disable the main window
    try
    {
        var result = Task.Factory.StartNew(
            () => MessageBox.Show(
                e.Exception.Message, "Continue?", 
                MessageBoxButton.YesNo),
            TaskCreationOptions.LongRunning).Result; // this blocks
        if (result == MessageBoxResult.Yes)
            e.Handled = true;
    }
    finally
    {
        this.IsEnabled = true; // enable the main window
    }
};

从UI体验前景来看,这很难看,但它可以为您提供所需的工作流程。

答案 1 :(得分:0)

消息框仍然是在你的线程上运行的窗口,所以他们必须抽取窗口消息(模态消息循环),以便在需要时重新绘制,移动,处理键盘输入(例如Tab在控件之间移动),鼠标输入。他们仍然会将消息发送到同一线程上的其他窗口,以确保重新拥有拥有它们的窗口。拥有窗口被禁用,因此输入消息只会导致播放默认声音,但仍会触发计时器,仍会处理任何已发送或已发布的消息。

此类问题的常见原因是您在窗口中附加了System.Windows.Forms.Timer或WPF DispatcherTimer。其他可能性包括使用InvokeBeginInvoke在UI线程上执行某些操作的后台线程。