背景: 我有一个异步调用,在某个深处的地方引发了异常... 在我的实际应用程序中,它是一个异步运行的插件,因此我想包装该异常并用该异常起源的插件名称装饰该异常。
但是.Net丢弃了外部异常,因此丢失了其他详细信息,
我制作了一个带有两个按钮的示例Windows窗体应用程序,并设置了一个常规异常处理程序
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
然后一个按钮:
private async void buttonThrowAndWrapExceptionAsync_Click(object sender, EventArgs e)
{
_textBoxToSetTextOnGivenLastCall = textBox1;
try
{
await Task.Run(() =>
ThrowExceptionA());
}
catch (Exception a)
{
throw new Exception("B", a);
}
}
另一个按钮:
private async void buttonThrowAndRethrowNewExceptionAsync_Click(object sender, EventArgs e)
{
_textBoxToSetTextOnGivenLastCall = textBox2;
try
{
await Task.Run(() =>
ThrowExceptionA());
}
catch (Exception)
{
throw new Exception("B");
}
}
当我
throw new Exception("B", a);
在上下文中,异常“ A”发生在Application_ThreadException回调中,没有任何痕迹'B'... B从未出现,这不是我期望的。
当我
throw new Exception("B");
Application_ThreadException中出现异常“ B”,而没有出现异常“ A”,这在我看来是可以理解的
在我的“真实”应用程序中, 我正在考虑进行一次“ hack”操作,然后将“ A” .ToString()添加到我的“ B”异常中,但是我知道这很糟糕,而且我确信我们有很多逻辑希望能够包装异常< / p>
顺便说一句...在OnComplete中使用后台处理程序进行异常处理的工作方式与此处的“异步”代码相同...
可以在此处找到示例: https://github.com/buildcomplete/dotnet-winforms-async-exceptionhandling
编辑: 在查看了建议的Why does the inner exception reach the ThreadException handler and not the actual thrown exception?相似之后,并提供了答案的答案,我实际上对停止这种行为更感兴趣。
现在我做了一个丑陋的攻击...当引发包装异常时,我将包装B包裹在一个新的异常ProtectedFromASyncUnfoldException中。
throw new ProtectedFromASyncUnfoldException(new Exception("B", a));
这破坏了InnerExceptions的正常行为...这真的不是我不想做的...更好的解决方法吗?
这是Hack类:
public class ProtectedFromASyncUnfoldException : Exception
{
public Exception InternalException { get; }
public ProtectedFromASyncUnfoldException() { }
public ProtectedFromASyncUnfoldException(string message)
: base(message) { }
/// <summary>
/// Hack of exception.
/// Setting InternalException instead of innerException
/// to avoid strange behaviour of async exception handling in windows forms
/// that otherwise throws away all but the base exception
/// </summary>
/// <param name="message"></param>
/// <param name="innerException">transformed to InternalException</param>
public ProtectedFromASyncUnfoldException(string message, Exception innerException)
: base(message)
{
InternalException = innerException;
}
public ProtectedFromASyncUnfoldException(Exception innerException)
: this(innerException.Message, innerException) { }
protected ProtectedFromASyncUnfoldException(SerializationInfo info, StreamingContext context)
: base(info, context) { }
public override string ToString()
{
return base.ToString()
+ Environment.NewLine
+ Environment.NewLine
+ "---------------------"
+ InternalException?.ToString();
}
}