是的,这是设计使然,并且与 Windows 窗体的工作方式密切相关。在 Winforms 应用程序中,代码运行以响应 Windows 发布到活动窗口的消息。每个本机 Windows 应用程序都包含一个消息循环来检测这些消息。 Winforms 管道确保您的事件处理程序之一响应运行;示例代码中的button1_Click。
大多数 Winforms 控件都实现自己的事件处理程序。例如,PictureBox 有一个 Paint 事件处理程序,可确保将其图像绘制到屏幕上。这一切都是自动完成的,您无需自己编写任何代码即可完成此工作。
然而,当此代码抛出异常时,会出现一个问题,您无法捕获此类异常,因为不涉及您自己的代码。换句话说,没有地方可以让你注入自己的 try 块。您自己的程序代码的最后一部分是启动消息循环的代码。 Application.Run() 方法调用,通常在 Program.cs 中。或者,如果显示对话框,则调用 Form.ShowDialog()。这些方法中的任何一个都会启动消息循环。在 Application.Run() 调用周围放置一个 try 块是没有用的,应用程序将在捕获异常后终止。
为了解决这个问题,Winforms 消息循环代码在调度事件的代码周围包含一个 try 块。它的catch子句显示您提到的对话框,它是由ThreadExceptionDialog类实现的。
直截了当地问你的问题:这个 catch 子句确实妨碍了调试时解决代码问题。仅当没有处理异常的 catch 块时,调试器才会停止于异常。但是当你的代码抛出异常时,你在调试时会想知道它。前面提到的消息循环中的代码知道是否附加了调试器。如果是,它将分派事件而不使用 try/catch 块。现在,当您的代码抛出异常时,没有 catch 子句来处理它,调试器将停止程序,让您有机会找出问题所在。
也许您现在明白为什么您的程序会这样做。调试时,消息循环中的 catch 子句被禁用,从而使 Form1 代码中的 catch 子句有机会捕获异常。如果不这样做,消息循环 catch 子句将处理异常(通过显示对话框)并防止异常展开到 Form1 代码。
您可以通过调用 Application.SetUnhandledExceptionMode() 方法并传递 UnhandledExceptionMode.ThrowException 来完全阻止使用消息循环 catch 子句。在 Main() 方法中、在 Application.Run() 调用之前执行此操作。现在,无论哪种方式,您的程序都会表现相同。
一般来说,这不是一个坏主意,为用户提供在异常对话框中继续的选项是一个值得怀疑的功能。在这种情况下,请为 AppDomain.UnhandledException 事件实现一个事件处理程序,以便至少对用户进行一些诊断。