非常奇怪的Application.ThreadException行为

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

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

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

我显然希望我的Application_ThreadException处理程序能够传递“test2”exception,但情况并非总是这样。 通常情况下, 如果另一个线程将我的代码编组到UI,我的处理程序会收到“测试”exception,就像我根本没有抓到“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)); } } 

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

 test ... here I click button1 test2 

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

您的代码中存在一个错误,使得调试正在进行的操作变得很困难:在创建表单的句柄之前启动该线程。 这将使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()调用,它会搞砸你的exception消息。 为什么Windows窗体设计师选择这样做对我来说不是很清楚,参考源中的代码没有评论。 我只能猜测,如果没有它,exception将更难调试,以防它由Windows窗体管道代码而不是应用程序代码引发。 不是很好的解释。

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

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

exception#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(); } ... } 

你必须打电话

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

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