我正在使用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上重现了这一点。有人有合理的解释吗?
答案 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:在创建窗口句柄之前,无法在控件上调用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();
}
...
}
答案 2 :(得分:0)
你必须致电
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
首先在你的Main()方法中。