Dialog MessageBox有时隐藏在主窗体后面

我们的一些非技术用户遇到问题,我们的应用程序中的对话框有时可能会显示在主窗体后面,并且应用程序不接受任何输入,直到消息框(他们看不到)被解除。

应用程序是用C#编写的,消息框是标准的,例如代码可以像MessageBox.Show(消息,标题)一样简单,消息框可以由主UI线程创建(即不是某些后台线程)。 应用程序不必全屏运行,但90%的用户确实全屏运行。

大多数时候((可能> 99%)消息框显示正确,我从来没有设法看到它出错,但我看到一台机器出错了。

我注意到的一件事是,如果你有一个显示对话框的应用程序,那么当你查看你的任务管理器时,你只能在应用程序列表中看到一个条目。 每当隐藏消息框时,您将看到两个条目,一个用于主应用程序,另一个用于此消息框。

一旦你知道发生了什么事情就很容易解决问题,但是我们的一些非技术用户对它感到困惑并最终关闭了他们的计算机。 (而那些使用远程桌面的人在解决问题时更加困惑)。

我不认为它与操作系统有关,因为我已经看到它发生在Vista中并且被告知它也发生在Windows 2003服务器上的终端会话中。

有什么事情知道为什么会这样,更重要的是,如果可以做任何事情来避免它吗?

MessageBox.Show()方法的一些重载采用IWin32Window参数作为第一个参数。 如果您将表单作为第一个参数传递,则应该防止这种情况发生。

是否总是相同的消息框(对于相同的消息?)来自相同的forms?

理想情况下,您应该尝试找到一些方法来重现问题,或者至少自动重现问题。 这将使您的调试更容易,然后您可以确保您的未来更改将修复该错误,而不是必须等待几周来获得用户的反馈。

如果它始终是相同的消息并且在同一窗口中,由相同的操作产生,并且如果从用户的观点触发MessageBox调用相当容易,并且如果您的UI是相对标准的,则可以自动化UI使用AutoIT脚本并让它在循环中运行,直到问题发生。

和/或,你可以创建一个“调试” – 你的应用程序的构建,你可以把它给一些用户(最好是那些似乎最常遇到问题的用户),它们会将StackFrame对象的内容写入一个每次调用MessageBox之前的日志文件或类似内容(您可以在MessageBox周围创建一个包装器以使其更容易)。

然后,当您的某个用户遇到问题时,您可以查看日志文件并查看它的来源(源代码文件,行,调用堆栈等)。 您还可以将此与来自其他用户的日志进行比较,并查看每次MessageBox是来自同一位置还是来自不同。 这将显示调用有问题的MessageBox的位置和位置。

可能有更容易的解决方案(特别是如果你的应用程序有很多程序集)涉及一些.Net调试器,当问题发生时你会附加到你的应用程序以查看调用堆栈等,但我只用本机应用程序做到这一点(使用OllyDbg)到目前为止,而不是.Net。 其他人也许可以进一步扩展这个想法……

确认问题。 我们要做的是修复它:1。运行新任务并显示消息框2.在主UI空间中,当任务仍在运行时 – 在执行DoEvents的循环中等待。 像这样的东西

更新2015-12-17。 转载问题yestarday。 为了回复我的情况 – 最小化应用程序,“等待”弹出窗口(在我们的情况下它发生在一些空闲时间后),然后在任务栏中单击主应用程序图标。 这会“隐藏”弹出窗口,因此无法将其显示在屏幕上。 下面的代码经过测试并解决了问题。 但我仍然不明白它是什么/为什么会发生。

  private static DialogResult ShowMessageBox( string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) { var showMessageBoxTask = new Task(() => { var form = new Form() {TopMost = true}; var result = MessageBox.Show( form, PrepareMessage(message), caption, buttons, icon); form.Dispose(); return result; }); showMessageBoxTask.Start(); while (!showMessageBoxTask.IsCompleted && !showMessageBoxTask.IsFaulted) { Application.DoEvents(); } return showMessageBoxTask.Result; } 

你说“消息框可以由主UI线程创建”,我认为这意味着它们并不总是由主UI线程创建。 你的问题听起来像MessageBox.Show偶尔从另一个线程调用。

在父窗体中,在MessageBox.Show()之前添加它:

this.TopMost = false;