【发布时间】:2011-12-20 04:41:28
【问题描述】:
我有一个Application.ThreadException 的处理程序,但我发现异常并不总是正确地传递给它。具体来说,如果我从 BeginInvoke 回调中抛出带有内部异常的异常,我的 ThreadException 处理程序不会得到 outer 异常——它只会得到 inner 异常。
示例代码:
public Form1()
{
InitializeComponent();
Application.ThreadException += (sender, e) =>
MessageBox.Show(e.Exception.ToString());
}
private void button1_Click(object sender, EventArgs e)
{
var inner = new Exception("Inner");
var outer = new Exception("Outer", inner);
//throw outer;
BeginInvoke(new Action(() => { throw outer; }));
}
如果我取消注释 throw outer; 行并单击按钮,则消息框会显示外部异常(及其内部异常):
System.Exception:外部 ---> System.Exception:内部
--- 内部异常堆栈跟踪结束 ---
在 C:\svn\trunk\Code Base\Source.NET\WindowsFormsApplication1\Form1.cs:line 55 中的 WindowsFormsApplication1.Form1.button1_Click(Object sender, EventArgs e)
在 System.Windows.Forms.Control.OnClick(EventArgs e)
在 System.Windows.Forms.Button.OnClick(EventArgs e)
在 System.Windows.Forms.Button.OnMouseUp(MouseEventArgs 事件)
在 System.Windows.Forms.Control.WmMouseUp(消息和 m,MouseButtons 按钮,Int32 点击)
在 System.Windows.Forms.Control.WndProc(Message&m)
在 System.Windows.Forms.ButtonBase.WndProc(Message&m)
在 System.Windows.Forms.Button.WndProc(Message&m)
在 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message&m)
在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
在 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
但是如果throw outer; 在BeginInvoke 调用中,如上面的代码,那么ThreadException 处理程序只有 会获得内部异常。在调用ThreadException 之前,外部异常被剥离,我得到的只是:
System.Exception:内部
(这里没有调用堆栈,因为 inner 从未被抛出。在一个更现实的示例中,我捕获了一个异常并将其包装以重新抛出,会有一个调用堆栈。)
如果我使用SynchronizationContext.Current.Post 而不是BeginInvoke,也会发生同样的事情:外部异常被剥离,ThreadException 处理程序只获取内部异常。
我尝试在外部包裹更多层的异常,以防它只是剥离最外层的异常,但它没有帮助:显然某处有一个循环在按照while (e.InnerException != null) e = e.InnerException; 的行做某事。
我使用BeginInvoke 是因为我的代码需要抛出一个未处理的异常以由ThreadException 立即处理,但此代码位于调用堆栈更高的catch 块内(特别是,它在Task 的操作中,Task 将捕获异常并阻止它传播)。我正在尝试使用BeginInvoke 来延迟throw,直到下一次在消息循环中处理消息,此时我不再在catch 中。我不依附BeginInvoke的具体解决方案;我只想抛出一个未处理的异常。
即使我在其他人的catch-all 中,我如何才能导致异常(包括其内部异常)到达ThreadException?
(由于程序集的依赖性,我不能直接调用我的ThreadException-handler 方法:处理程序被 EXE 的启动代码挂钩,而我当前的问题是在较低层的 DLL 中。)
【问题讨论】:
-
是的,这是故意的。 Control.InvokeMarshaledCallbacks() 捕获任何异常,以便将其封送回调用者。使用 Invoke() 或 EndInvoke() 时相关。它使用 Exception.GetBaseException()。你无法解决这个问题。但是,您可以自己捕获异常并在这种特定情况下直接调用您的事件处理程序。
-
@HansPassant,我看到调用 GetBaseException 的 InvokeMarshaledCallbacks 中的代码。由于 GetBaseException 是虚拟的,这表明我可以覆盖它。不幸的是,我的重写 GetBaseException 方法从未被调用——所以我不确定这是导致问题的特定代码。
-
@Joe,使用 GetBaseException 对我有用。
-
@HansPassant,你知道为什么 Control.InvokeMarshaledCallbacks() 会吃掉外部异常吗?
-
@HansPassant calling
Exception.GetBaseException()here 有什么意义?它没有提供任何好处,它只会减少人们在显示的未处理异常对话框中看到的上下文/堆栈的数量,并防止任何ThreadException处理程序看到引发的实际(外部)异常。既然这是故意的,那么人们试图通过这样做来解决什么问题?不能修吗?
标签: c# exception unhandled-exception