非常奇怪的Application.ThreadException行为

时间:2010-06-09 15:03:32

标签: c# .net winforms exception

我正在使用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上重现了这一点。有人有合理的解释吗?

3 个答案:

答案 0 :(得分:7)

您的代码中存在一个错误,使得调试正在进行的操作变得很困难:在创建表单的句柄之前启动该线程。这将使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);
       }

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

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

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

答案 1 :(得分:1)

异常#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();
    }

    ...
}

答案 2 :(得分:0)

你必须致电

  

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

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