【问题标题】:Very strange Application.ThreadException behaviour非常奇怪的 Application.ThreadException 行为
【发布时间】:2011-03-01 17:27:24
【问题描述】:

我正在使用 Application.ThreadException 事件来处理和记录我的 winforms 应用程序中的意外异常。

现在,在我的应用程序的某个地方,我有以下代码(或者说是等效的,但这个虚拟代码足以重现我的问题):

            try
            {
                throw new NullReferenceException("test");
            }
            catch (Exception ex)
            {
                throw new Exception("test2", ex);
            }

我显然希望我的 Application_ThreadException 处理程序能够通过“test2”异常,但情况并非总是如此。通常,如果另一个线程将我的代码编组到 UI,我的处理程序会收到“测试”异常,就好像我根本没有捕捉到“测试”一样。

这是一个重现此行为的简短示例。设计者的代码我省略了。

     static class Program
{
    [STAThread]
    static void Main()
    {
        Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
        //Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); // has no impact in this scenario, can be commented.

        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }

       static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        //this handler is never called
    }

    static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
    {
        Console.WriteLine(e.Exception.Message);
    }
}

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        button1.Click+=new EventHandler(button1_Click);
    }

    protected override void OnLoad(EventArgs e) {
    System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(ThrowEx));
    t.Start();
    }


    private void button1_Click(object sender, EventArgs e)
    {
        try
        {
            throw new NullReferenceException("test");
        }
        catch (Exception ex)
        {
            throw new Exception("test2", ex);
        }
    }

    void ThrowEx()
    {
        this.BeginInvoke(new EventHandler(button1_Click));
    }
}

这个程序在我电脑上的输出是:

test
... here I click button1
test2

我已在 .net 2.0、3.5 和 4.0 上复制了此内容。有人有合乎逻辑的解释吗?

【问题讨论】:

    标签: c# .net winforms exception


    【解决方案1】:

    您的代码中有一个错误使得很难调试正在发生的事情:您在创建表单的句柄之前启动线程。这将使 BeginInvoke 失败。修复:

        protected override void OnLoad(EventArgs e) {
            System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(ThrowEx));
            t.Start();
        }
    

    Anyhoo,这是设计行为。运行 BeginInvoke 目标的 Windows 窗体代码如下所示:

      try
      {
          this.InvokeMarshaledCallback(tme);
      }
      catch (Exception exception)
      {
          tme.exception = exception.GetBaseException();
      }
      ...
           if ((!NativeWindow.WndProcShouldBeDebuggable && (tme.exception != null)) && !tme.synchronous)
           {
               Application.OnThreadException(tme.exception);
           }
    

    是 exception.GetBaseException() 调用搞砸了您的异常消息。为什么 Windows 窗体设计者选择这样做对我来说不是很清楚,参考源中的代码没有评论。我只能猜测如果没有它,异常将更难以调试,以防它是由 Windows 窗体管道代码而不是应用程序代码引发的。不是很好的解释。

    他们已经说过他们won't fix it,也许你可以添加你的投票。不要抱太大希望。

    解决方法是不设置 InnerException。当然不是一个很好的选择。

    【讨论】:

    • GetBaseException 调用旨在从Invoke 解开TargetInvocationException
    • 不是这样的。 Control.BeginInvoke() 与 Delegate.BeginInvoke() 完全不同。它不会将异常编组回调用者。
    【解决方案2】:

    你必须打电话

    Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

    首先在您的 Main() 方法中。

    【讨论】:

      【解决方案3】:

      异常 #1:在创建窗口句柄之前,无法在控件上调用 InvokeBeginInvoke

      所以,不要试图从构造函数中调用。在OnLoad()做吧:

      public partial class Form1 : Form
      {
          public Form1()
          {
              InitializeComponent();
              this.Load += new EventHandler(Form1_Load);
              button1.Click += new EventHandler(button1_Click);
          }
      
          private void Form1_Load(object sender, EventArgs e)
          {
              System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(ThrowEx));
              t.Start();
          }
      
          ...
      }
      

      【讨论】:

      • 你的建议是有道理的,但这并不能改变奇怪的行为。
      • @Brann,你是对的。奇怪的行为仍然存在。但是,我通过在按钮单击处理程序中不执行 catch (Exception ex) 而只是 catch 来纠正它。很有意思。抛出异常的异常不是异常的后代。
      猜你喜欢
      • 2016-03-13
      • 2020-12-02
      • 1970-01-01
      • 1970-01-01
      • 2018-04-20
      • 2020-02-09
      • 1970-01-01
      • 2016-12-04
      • 1970-01-01
      相关资源
      最近更新 更多