【问题标题】:Can C# WinForm static void Main NOT catching Exception?C# WinForm static void Main 不能捕获异常吗?
【发布时间】:2016-05-25 05:07:23
【问题描述】:

我有一个WinForm 应用程序,用C# 编写,我在Program.cs 中放置了一个try-catch 块,在程序条目中,static void Main 方法,就在应用程序的开头,如下所示:

using System;
using System.IO;
using System.Windows.Forms;

namespace T5ShortestTime {
    static class Program {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main() {
            try {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new T5ShortestTimeForm());
            } catch (Exception e) {
                string errordir = Path.Combine(Application.StartupPath, "errorlog");
                string errorlog = Path.Combine(errordir, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".txt");
                if (!Directory.Exists(errordir))
                    Directory.CreateDirectory(errordir);                
                File.WriteAllText(errorlog, e.ToString());              
            }
        }
    }
}

如您所见,Application 被放入 try-catch 块和 catch 块中,它唯一做的就是创建一个错误日志文件。

现在,到目前为止一切顺利。我的应用程序运行良好,如果我遇到崩溃,最后一个Exception 应该被try-catch 块捕获并存储在错误日志文件中。

但是,当我运行我的程序一段时间后,我得到了一个未处理的异常(null 参考)。令我惊讶的是,该异常并没有创建错误日志文件。

现在,this post 表明它可能是由 ThreadExceptionHandleProcessCorruptedStateExceptions(两个最受好评的答案)引起的,但我的案例显示了一个简单的 null 引用异常:

Problem signature:
  Problem Event Name:   CLR20r3
  Problem Signature 01: T5ShortestTime.exe
  Problem Signature 02: 2.8.3.1
  Problem Signature 03: 5743e646
  Problem Signature 04: T5ShortestTime
  Problem Signature 05: 2.8.3.1
  Problem Signature 06: 5743e646
  Problem Signature 07: 182
  Problem Signature 08: 1b
  Problem Signature 09: System.NullReferenceException
  OS Version:   6.3.9600.2.0.0.272.7
  Locale ID:    1033
  Additional Information 1: bb91
  Additional Information 2: bb91a371df830534902ec94577ebb4a3
  Additional Information 3: aba1
  Additional Information 4: aba1ed7202d796d19b974eec93d89ec2

Read our privacy statement online:
  http://go.microsoft.com/fwlink/?linkid=280262

If the online privacy statement is not available, please read our privacy statement offline:
  C:\Windows\system32\en-US\erofflps.txt

为什么会这样?

【问题讨论】:

  • 这不是您创建全局异常处理程序的方式。查看此页面右侧的“已链接”部分。那里接受的答案会告诉你该怎么做。
  • @jmcilhinney 你的意思是ThreadException?
  • 在示例中, // 启动一个新线程,与 Windows 窗体分开,这将引发异常。 private void button2_Click(object sender, System.EventArgs e) { ThreadStart newThreadStart = new ThreadStart(newThread_Execute); newThread = new Thread(newThreadStart); newThread.Start(); } 故意在新线程中处理异常。但这可以创建null 引用异常(如我的情况)而不是ThreadException(应该是这种类型 - 不是吗?)?

标签: c# winforms exception


【解决方案1】:

最后一个异常应该被 try-catch 块捕获

这不会发生。除了在一种情况下,当您运行带有调试器的程序时。所以你肯定会相信它会起作用,每个人总是开始用 F5 运行他们的程序一段时间。

Application.Run() 在其代码中有一个引发事件的支持,try/catch-em-all 当事件处理程序抛出未处理的异常时引发 Application.ThreadException 事件。这种支持真的,真的是必要的,尤其是在 x64 版本的 Windows 7 上。Very Bad Things happen 当没有异常处理程序时。但是,当您使用调试器运行时,该支持不存在,这使得未处理的异常难以调试。

因此,当您调试时,您的 catch 子句将运行。使未处理的异常难以调试。当您在没有调试器的情况下运行时,您的 catch 子句将运行并且您的程序将崩溃,正如您所描述的那样。使未处理的异常难以调试。

所以不要这样做。 Application.Run() 如何处理未处理的异常由 Application.SetUnhandledExceptionMode() 方法配置。你会更喜欢这个版本:

    [STAThread]
    static void Main() {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        if (!System.Diagnostics.Debugger.IsAttached) {
            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
            AppDomain.CurrentDomain.UnhandledException += LogException;
        }
        Application.Run(new Form1());
    }

    private static void LogException(object sender, UnhandledExceptionEventArgs e) {
        string errordir = Path.Combine(Application.StartupPath, "errorlog");
        string errorlog = Path.Combine(errordir, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".txt");
        if (!Directory.Exists(errordir))
            Directory.CreateDirectory(errordir);
        File.WriteAllText(errorlog, e.ToString());
        AppDomain.CurrentDomain.UnhandledException -= LogException;
        MessageBox.Show("Error details recorded in " + errorlog, "Unexpected error");
        Environment.Exit(1);
    }

使用此代码,您可以毫无问题地调试未处理的异常。 Debugger.IsAttached 测试确保调试器在事件处理程序发生故障时始终停止。如果没有调试器,它会禁用 Application.ThreadException 事件(它非常无用)并倾向于监听 all 异常。包括在工作线程中提出的那些。

您应该向用户发出警报,这样窗口就不会消失得无影无踪。我本来打算推荐 MessageBox,但注意到 this bug 目前又回到了 Windows 10 上。叹息。

【讨论】:

  • 感谢您的解释,这应该是答案。最后一个问题,您说它对 Windows 7 x64 尤其不利,我使用的是 Windows 10 x64。这有什么区别吗?在你的帖子中,你说,“更新到 Windows 8 或更高版本,他们已经解决了这个 wow64 问题。” - 我认为这也适用于 Windows 10,这是正确的吗?
  • Win10没有这个问题,它是Win7特有的。
  • 好的,谢谢。然后我会心平气和... ;) 我还注意到Dispose 仍然在Exception 之后被称为。我将SetUnhandledExceptionMode 实现为ThrowException,并使用throw new System.Exception() 对其进行了测试。令人惊讶的是,该程序会生成 两个 错误日志文件而不是 一个。一个是System.Exception,另一个是Dispose 中的Exception - 而Dispose 中的那个是稍后生成的。为什么会这样?这实际上是否预期Dispose 仍然在 一个致命异常之后被调用?
  • 你说的Dispose方法不是很清楚。推测它实际上是 Dispose(bool) 重载,它也被终结器调用。你可以选择如何终止你的程序,Environment.Exit() 仍然通过运行终结器来清理,Environment.FailFast() 不会。当 disposing 参数为假时,Dispose(bool) 不应该做任何危险的事情,你永远不想让终结器线程崩溃。如果这没有帮助,请单击按钮。
  • 评论中的解释很有帮助。似乎第二个Exception 是由Environment.Exit 引起的。当我没有把Environment.Exit 替换为Environment.FailFast 时(是的,它是WinForm 所具有的默认无参数 Dispose),程序没有not 产生两个日志。如果使用AppDomain.CurrentDomain.UnhandledException -= LogException; 行,我也希望它会做同样的事情。再次感谢! ;)
【解决方案2】:

ThreadException 不是像 (NullReferenceException) 这样的异常类型。就是这样:

此事件允许您的 Windows 窗体应用程序以其他方式处理 Windows 窗体线程中发生的未处理异常

这意味着它处理除主线程之外的线程中的异常。

因此,您还需要订阅:AppDomain.CurrentDomain.UnhandledException 以处理主线程中的异常(无论异常类型如何,例如 NullReferenceIndexOutOfRange 等)。

【讨论】:

    【解决方案3】:

    好的,最后我实现了Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException),例如Hans Passant for VB.Net in this post。在这里,我为C# 放置了我自己的代码+错误记录:

    static void Main() {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        if (!System.Diagnostics.Debugger.IsAttached) {
            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
            AppDomain.CurrentDomain.UnhandledException += LogUnhandledExceptions;
        }
        Application.Run(new T5ShortestTimeForm());
    }
    
    private static void LogUnhandledExceptions(object sender, UnhandledExceptionEventArgs e) {
        Exception ex = (Exception)e.ExceptionObject;
        string errordir = Path.Combine(Application.StartupPath, "errorlog");
        string errorlog = Path.Combine(errordir, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".txt");
        if (!Directory.Exists(errordir))
            Directory.CreateDirectory(errordir);
        File.WriteAllText(errorlog, ex.ToString());
        Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(ex));
    }
    

    此外,这里的混淆来源似乎是实际上有两个传播Exceptions发生:

    第一个是应用程序本身的任何异常:

    System.Exception: Exception of type 'System.Exception' was thrown.
       at T5ShortestTime.T5ShortestTimeForm..ctor() in C:\Test.cs:line 45
       at T5ShortestTime.Program.Main() in C:\Test.cs:line 19
       at ...
    

    而第二个发生在Form组件的Dispose期间,又产生了一个异常,就是null引用异常:

    System.NullReferenceException: Object reference not set to an instance of an object.
       at T5ShortestTime.T5ShortestTimeForm.Dispose(Boolean disposing)
       at System.ComponentModel.Component.Finalize()
    

    所以,当我在我的应用程序中测试异常时,NullReferenceException 是最后一个,在 Dispose 中。

    我只有在上面将UnhandledExceptionMode 设置为ThrowException 后才设法捕捉到这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-05
      • 2017-06-24
      • 2016-05-22
      • 2023-03-14
      • 1970-01-01
      • 2013-06-03
      相关资源
      最近更新 更多