为什么“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”的异常抛出行为如果附带Visual Studio调试器启动,则会有所不同

这样做是为了帮助您调试未处理的异常。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/

找到