简体   繁体   English

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

[英]Prevent multiple MessageBoxes on one thread without blocking

I have registered an error handler in my application that shows a user dialog MessageBox when an unhandled exception occurs. 我在我的应用程序中注册了一个错误处理程序,当发生未处理的异常时,该错误处理程序将显示一个用户对话框MessageBox。 It's connected to the Application.DispatcherUnhandledException event and thus happens on the UI thread. 它连接到Application.DispatcherUnhandledException事件,因此发生在UI线程上。 Now I've seen a situation where an exception is thrown regularly, once per second, and new dialogs kept popping up. 现在,我看到了一种情况,每秒定期抛出一次异常,并且不断弹出新对话框。 I was trying to prevent this with a lock, but since it's all on the same thread, this doesn't have any effect. 我试图用锁来防止这种情况发生,但是由于它们都在同一线程上,因此没有任何效果。 An additional sleep lock on the same thread obviously led to a single blocked MessageBox on the screen. 同一线程上的其他睡眠锁定显然导致屏幕上单个被阻止的MessageBox。

The MessageBox seems to free the UI thread for other MessageBoxes to appear. MessageBox似乎释放了UI线程,以便其他MessageBox显示。 How can I prevent that without blocking the thread? 在不阻塞线程的情况下如何防止这种情况?

The user has the choice to continue or exit the application. 用户可以选择继续或退出应用程序。 When continuing, the next queued MessageBox should appear, it should not be discarded. 当继续时,下一个排队的MessageBox应该出现,不应丢弃。 If too many messages show up in a time, the user can still decide to exit the application. 如果一次出现太多消息,则用户仍然可以决定退出该应用程序。

The problem here is not with the message box. 这里的问题不在于消息框。 It is that what happens when the message box is still open, while another exception has occurred. 当消息框仍处于打开状态,而又发生另一个异常时,会发生这种情况。 In which case, your DispatcherUnhandledException handler gets re-entered on the same thread. 在这种情况下,您的DispatcherUnhandledException处理程序将在同一线程上重新输入。

However, the user hasn't made a decision yet about the previous error, the previous message box is still waiting for his input. 但是,用户尚未对先前的错误做出决定,先前的消息框仍在等待他的输入。 Your call stack looks like this: 您的调用堆栈如下所示:

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

Clearly, you cannot return from DispatcherUnhandledExceptionHandler (previous) , without returning from DispatcherUnhandledExceptionHandler (current) first: you're inside a nested call on the same thread. 显然,您不能从DispatcherUnhandledExceptionHandler (previous)返回,而无需先从DispatcherUnhandledExceptionHandler (current)返回:您位于同一线程的嵌套调用中。 And you cannot return from DispatcherUnhandledExceptionHandler (current) without confirming this fist with the user, back to the egg. 而且,如果没有与用户确认此拳头,您将无法从DispatcherUnhandledExceptionHandler (current)返回鸡蛋。

I can think of just one way of solving this, while sticking with your question requirements. 在坚持您的问题要求的同时,我可以想到解决此问题的一种方法。 It's to display the message box on another thread and block the main thread's message loop while waiting for the user's choice. 它是在另一个线程上显示消息框,并在等待用户选择时阻止主线程的消息循环。 This way, no more exceptions can occur on the main thread, until you obtain the user's consent regarding the current exception: 这样,在您获得有关当前异常的用户同意之前,主线程上不会再发生任何异常:

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
    }
};

This is ugly from the UI experience prospective, but it gives you the desired workflow. 从UI经验的角度来看这很丑陋,但是它为您提供了所需的工作流程。

Message boxes are still windows that run on your thread, so they have to pump window messages (a Modal Message Loop) in order to be repainted when required, moved, handle keyboard input (eg Tab to move between controls), mouse input. 消息框仍然是在线程上运行的窗口,因此它们必须抽取窗口消息(模态消息循环),以便在需要时重新绘制,移动,处理键盘输入(例如,在控件之间移动的Tab),鼠标输入。 They still dispatch messages to other windows on the same thread, to ensure that the window that owns them is repainted. 它们仍将消息分发到同一线程上的其他窗口,以确保拥有它们的窗口被重新绘制。 The owning window is disabled, so input messages simply result in the default sound being played, but timers will still be fired and any sent or posted messages will still be processed. 拥有窗口被禁用,因此输入消息只会导致播放默认声音,但是计时器仍会触发,并且任何发送或发布的消息仍将得到处理。

The usual source of such problems is that you have a System.Windows.Forms.Timer or WPF DispatcherTimer attached to the window. 此类问题的通常根源是您在窗口上附加了System.Windows.Forms.Timer或WPF DispatcherTimer Other possibilities include background threads using Invoke or BeginInvoke to perform some operation on the UI thread. 其他可能性包括使用InvokeBeginInvoke在UI线程上执行某些操作的后台线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM