为什么“showdialog”的异常抛出行为如果附带Visual Studio调试器启动,则会有所不同
本文关键字:调试器 Studio Visual 启动 有所不同 如果 showdialog 异常 为什么 | 更新日期: 2023-09-27 18:06:57
考虑一下:Form1启动Form2作为一个模态对话框,调用"System.Windows.Forms.Form.ShowDialog"。Form2在GUI线程上抛出异常。
如果我从Visual Studio调试器中运行这个程序,我可以在Form1的调用站点捕获这个Exception(这是我没有预料到的!)。如果我没有在没有附加调试器的情况下启动程序,即使我稍后附加了调试器,我也无法从Form1捕获异常(这是我所期望的行为)。
为什么在调试器下运行时可以捕获Form1中的异常?或者,更确切地说,为什么调试器的存在改变了"ShowDialog"的异常抛出行为?
下面的代码足以说明这个问题。
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
public class Form1 : Form
{
public Form1()
{
this.MouseClick += this.OnMouseClick;
}
private void OnMouseClick(object sender, MouseEventArgs e)
{
try
{
var f2 = new Form2();
f2.ShowDialog(this);
}
catch (System.Exception ex)
{
MessageBox.Show(this, ex.Message, "Exception Caught!",
MessageBoxButtons.OK, MessageBoxIcon.Information
);
}
}
}
public class Form2 : Form
{
public Form2()
{
this.MouseClick += this.OnMouseClick;
}
private void OnMouseClick(object sender, MouseEventArgs e)
{
throw new Exception("Hey! That hurts!");
}
}
}
这样做是为了帮助您调试未处理的异常。ShowDialog()是特殊的,该方法直到关闭对话框才返回。通过启动另一个调度程序循环,它就变成了模态,与Application.Run()完全相同。
由调度程序循环(事件处理程序)激活的代码引发的异常通过应用程序报告。UnhandledException事件。这种行为严重妨碍了调试仍然不稳定的代码。调试器将没有理由介入并向您展示代码是如何崩溃的,因为实际上已经处理了异常。通过引发事件来处理。特殊规则,如果附加了调试器,那么分派器循环将不会捕获异常并引发事件。
现在你的catch子句起作用了。但是只有在调试时,它才会没有一个,并且不能在用户的机器上工作。
你可以通过修改Main()方法来改变这种行为,将UnhandledExceptionMode.ThrowException
传递给Application.SetUnhandledExceptionMode()。这通常是明智的做法,因为默认的事件处理程序相当蹩脚。它让用户决定让程序继续运行,用户几乎从不知道正确的选择。您需要为AppDomain.CurrentDomain.UnhandledException编写一个事件处理程序,以便正确报告每个未处理的异常,包括在工作线程中引发的异常。这样的:
[STAThread]
static void Main() {
if (!System.Diagnostics.Debugger.IsAttached) {
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
MessageBox.Show(e.ExceptionObject.ToString());
// Workaround for Windows 10.0.10586 bug:
AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
Environment.Exit(1);
}
那个catch子句值得特别提一下。你必须真的不关心对话框没有像那样使用catch-em-all异常处理。让用户非常困惑的是,窗口莫名其妙地消失了。而且很可能会让你的程序很困惑,你也会捕捉到一些非常讨厌的东西。注意调试该异常时可能遇到的麻烦。你需要调试> Windows>异常设置>勾选"公共语言运行时异常"来强制调试器在抛出异常时停止。
这是64位窗口的行为:窗体加载或显示事件中发生的异常被吞噬。一个很好的解释可以在http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/