非常奇怪的Application.ThreadException行为

Bra*_*ann 9 .net c# exception winforms

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

现在,在我的应用程序的某个地方,我有以下代码(或者更确切地说是相同的东西,但这个虚拟代码足以重现我的问题):

            try
            {
                throw new NullReferenceException("test");
            }
            catch (Exception ex)
            {
                throw new Exception("test2", ex);
            }
Run Code Online (Sandbox Code Playgroud)

我显然希望我的Application_ThreadException处理程序能够传递"test2"异常,但情况并非总是这样.通常情况下,如果另一个线程将我的代码封送到UI,我的处理程序会收到"测试"异常,就像我根本没有抓到"test"一样.

以下是重现此行为的简短示例.我省略了设计师的代码.

     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));
    }
}
Run Code Online (Sandbox Code Playgroud)

我的计算机上该程序的输出是:

test
... here I click button1
test2
Run Code Online (Sandbox Code Playgroud)

我在.net 2.0,3.5和4.0上重现了这个.有人有合理的解释吗?

Han*_*ant 7

您的代码中存在一个错误,使得调试正在进行的操作变得很困难:在创建表单的句柄之前启动该线程.这将使BeginInvoke失败.固定:

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

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);
       }
Run Code Online (Sandbox Code Playgroud)

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

他们已经说过他们不会修复它,也许你可以添加你的投票.不要抱有希望.

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

  • 不是这个.Control.BeginInvoke()与Delegate.BeginInvoke()完全不同.它没有将异常编组回调用者. (3认同)

Jes*_*cer 1

异常#1:Invoke或者BeginInvoke在创建窗口句柄之前无法在控件上调用。

因此,不要尝试从构造函数中调用。这样做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();
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)