为什么WPF Dispatcher.Invoke不传播异常?

时间:2010-06-24 20:33:09

标签: .net wpf exception exception-handling invoke

这是我的假设例子。我有一个非常简单的WPF窗口,带有一个Button。 Button.Click事件有一个像这样的处理程序。

Action doit = () =>
{
    Action error = () => { throw new InvalidOperationException("test"); };

    try {
        this.Dispatcher.Invoke(error, DispatcherPriority.Normal);
    } catch (Exception ex) {
        System.Diagnostics.Trace.WriteLine(ex);
        throw;
    }
};
doit.BeginInvoke(null, null);

我希望通过Trace.WriteLine调用捕获并记下该异常。相反,没有捕获异常并且应用程序中断。

有人知道可能的解释吗?为了捕获由Dispatcher.Invoke调用的委托引发的异常,您建议使用哪种解决方法?

更新1 :我在异常处理代码中添加了throw。我不想实际忽略该异常。我的问题的重点是正确处理它。问题是永远不会执行异常处理代码。

请记住,这是一个假设的例子。我的真实代码看起来不像那样。另外,假设我无法更改要调用的方法中的代码。

更新2 :考虑这个类似的例子。我有一个Windows窗体窗口而不是WPF窗口。它有一个按钮,几乎完全相同的处理程序。唯一的区别在于调用代码。它是这样的。

this.Invoke(error);

在Windows窗体中,执行异常处理代码。为什么不同?

1 个答案:

答案 0 :(得分:6)

更新:要观察其他线程中的异常,您需要使用Task,将其排入Dispatcher线程(使用TaskScheduler.FromCurrentSynchronizationContext) ,等等,就这样:

var ui = TaskScheduler.FromCurrentSynchronizationContext();
Action doit = () => 
{ 
    var error = Task.Factory.StartNew(
        () => { throw new InvalidOperationException("test"); },
        CancellationToken.None,
        TaskCreationOptions.None,
        ui); 

    try { 
        error.Wait(); 
    } catch (Exception ex) { 
        System.Diagnostics.Trace.WriteLine(ex); 
    } 
}; 
doit.BeginInvoke(null, null); 

更新(再次):由于您的目标是可重复使用的组件,我建议您转移到基于Task的界面或其他基于SynchronizationContext的界面,例如event-based asynchronous pattern,而不是将组件基于DispatcherISynchronizeInvoke

Dispatcher - 基于组件仅适用于WPF / Silverlight;基于ISynchronizeInvoke的组件仅适用于Windows窗体。基于SynchronizationContext的组件将透明地与WPF或Windows Forms一起使用,并且(通过更多工作)ASP.NET,控制台应用程序,Windows服务等。

基于事件的异步模式是编写基于SynchronizationContext的组件的旧推荐方法;它仍然适用于.NET 3.5时代代码。但是,如果您使用的是.NET 4,则任务并行库更加灵活,干净且功能强大。 TaskScheduler.FromCurrentSynchronizationContext下方使用SynchronizationContext,是编写需要此类同步的可重用组件的新方法。