我在我的应用程序中注册了一个错误处理程序,它在发生未处理的异常时显示用户对话框MessageBox。它连接到Application.DispatcherUnhandledException事件,因此发生在UI线程上。现在我已经看到了一种定期抛出异常的情况,每秒一次,并且新的对话框不断弹出。我试图通过锁来防止这种情况,但由于它都在同一个线程上,因此没有任何效果。在同一个线程上额外的睡眠锁定显然导致屏幕上出现一个阻塞的MessageBox。
MessageBox似乎释放了用于显示其他MessageBox的UI线程。如何在不阻塞线程的情况下阻止它?
用户可以选择继续或退出应用程序。继续时,应该出现下一个排队的MessageBox,不应该丢弃它。如果一次出现太多消息,用户仍然可以决定退出该应用程序。
答案 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
。其他可能性包括使用Invoke
或BeginInvoke
在UI线程上执行某些操作的后台线程。