我遇到一种情况,即UI线程中抛出的异常不会在调用线程中被捕获。
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;
namespace SynchronisationContextAndExceptionWPF
{
public partial class MainWindow : Window
{
private readonly SynchronizationContext _synchronizationContext;
public MainWindow()
{
InitializeComponent();
_synchronizationContext = SynchronizationContext.Current;
}
private void Button_OnClick(object sender, RoutedEventArgs e)
{
try
{
_synchronizationContext.Send(
x =>
{
try
{
DoSomethingOnUiThreadThatThrowsException();
}
catch (Exception)
{
Debug.WriteLine("Catched Exception in thread that threw it.");
throw;
}
}, null);
}
catch (Exception)
{
Debug.WriteLine("Catched Exception in thread that calles Send-Method.");
throw;
}
}
private static void DoSomethingOnUiThreadThatThrowsException()
{
throw new Exception("Any Exception...");
}
}
}
首先我认为这是不可能的(我发现的所有文档都说我可以在那里捕获异常)。
经过一些研究后我发现了问题:我的应用程序使用了UnhandledExceptionHandler。处理DispatcherUnhandledException
- 事件。我正在向用户显示一些信息并设置e.Handled = true;
:
using System.Diagnostics;
using System.Windows;
using System.Windows.Threading;
namespace SynchronisationContextAndExceptionWPF
{
public partial class App : Application
{
public App()
{
DispatcherUnhandledException += App_DispatcherUnhandledException;
}
private static void App_DispatcherUnhandledException(
object sender,
DispatcherUnhandledExceptionEventArgs e)
{
Debug.WriteLine("Catched Exception in UnhandledExceptionHandler.");
// This line makes the difference:
e.Handled = true;
}
}
}
所以问题是:即使我处理它,为什么DispatcherUnhandledException
- 事件也会被提升?
你会如何解决这种情况?
答案 0 :(得分:1)
通过阅读您拥有的代码,由于catch块中的throw
而引发该代码。如果您不想将异常传递给其他处理程序,请删除throw
。
答案 1 :(得分:0)
在lambda表达式中,您可以设置一个Exception变量,稍后在调用线程中检查此变量。如果已设置,则在调用线程时抛出异常。
private void button_Click(object sender, RoutedEventArgs e)
{
Exception threadException = null;
try
{
_synchronizationContext.Send(
x =>
{
try
{
DoSomethingOnUiThreadThatThrowsException();
}
catch (Exception ex)
{
Debug.WriteLine("Catched Exception in thread that threw it.");
threadException = ex;
//throw; --> don't throw exception here; otherwise you will get DispatcherUnhandledException twice.
}
}, null);
}
catch (Exception)
{
Debug.WriteLine("Catched Exception in thread that calles Send-Method.");
throw;
}
if(threadException != null)
{
Debug.WriteLine("Catched Exception in thread that calles Send-Method.");
throw threadException; //throw you previously catched exception here.
}
}
亲切的问候, 丹尼尔
答案 2 :(得分:0)
如果你有很多控件,你可以生成一个新的类来清理特殊的异常变量。因此,您只需要更改_synchronizationContext的初始化(希望只在控件的基类上进行一次)。
public partial class MainWindow : Window
{
private readonly MySynchronizationContext _synchronizationContext;
public MainWindow()
{
InitializeComponent();
_synchronizationContext = new MySynchronizationContext(SynchronizationContext.Current);
}
private void button_Click(object sender, RoutedEventArgs e)
{
try
{
_synchronizationContext.Send(
x =>
{
DoSomethingOnUiThreadThatThrowsException();
}, null);
}
catch (Exception)
{
Debug.WriteLine("Catched Exception in thread that calles Send-Method.");
throw;
}
}
private static void DoSomethingOnUiThreadThatThrowsException()
{
throw new Exception("Any Exception...");
}
}
class MySynchronizationContext
{
SynchronizationContext innerContext;
public MySynchronizationContext(SynchronizationContext ctx)
{
innerContext = ctx;
}
public virtual void Send(SendOrPostCallback d, object state)
{
Exception threadException = null;
try
{
innerContext.Send(_ =>
{
try
{
d.Invoke(state);
}
catch (Exception exception)
{
threadException = exception;
}
}, null);
}
catch (Exception ex)
{
}
if (threadException != null)
{
throw new Exception("Synchronization error", threadException);
}
}
}