看起来属性的Set方法中出现的异常不会冒泡到Application的ThreadException事件。
我们将该事件与AppDomain.CurrentDomain.UnhandledException事件一起使用,以捕获应用程序中发生的任何意外事故。异常详细信息写入日志,因此我们的支持和开发团队可以更好地评估问题。可悲的是,看起来这个 Catch All 在这种特殊情况下不尽如人意。
StackOverflow上有几个类似的问题,但没有答案解决全局异常处理没有捕获异常的问题。我已经知道我们可以修复它,所以不会发生异常。我们可以为每个setter添加一个TryCatch块。我们可以将BindingComplete事件添加到每个数据绑定中并以这种方式获取异常。但所有这些都违背了全局异常处理的目的,在任何其他情况下都能完美地运行。
要重现此问题,只需使用文本框创建表单,将文本框绑定到属性并在属性的set方法中引发异常。将ThreadException和UnhandledException事件添加到program.cs。运行该程序并在文本框中键入以触发异常。调试器将在异常中断,按继续(F5),让异常冒泡,就像调试器外部一样。任何正常的异常都会在这些事件中结束,但这一事件不会。
Form1.cs的
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
textBox1.DataBindings.Add("Text", this, "TestValue", true, DataSourceUpdateMode.OnPropertyChanged);
}
private string _TestValue = "";
public string TestValue
{
get{return _TestValue;}
set
{
_TestValue = value;
throw new Exception("Something bad happened in here");
}
}
Program.cs的
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += ThreadExceptionHandler;
AppDomain.CurrentDomain.UnhandledException += new System.UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
Application.Run(new Form1());
}
private static void ThreadExceptionHandler(object sender, System.Threading.ThreadExceptionEventArgs args)
{
try
{
//RR.Common.ErrorLogRt.WriteError(args.Exception.StackTrace.ToString(), args.Exception.Message.ToString(), true);
MessageBox.Show(args.Exception.Message);
}
catch
{
MessageBox.Show("Error writing to exception log. This program will now terminate abnormally.");
Application.Exit();
}
}
static void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e)
{
try
{
if (e != null)
{
Exception ex = e.ExceptionObject as Exception;
//RR.Common.ErrorLogRt.WriteError(ex.StackTrace.ToString(), ex.Message.ToString(), true);
MessageBox.Show(ex.Message);
}
else
{
MessageBox.Show("Unhandled Error: " + e.ToString());
}
}
catch
{
MessageBox.Show("Error writing to exception log. This program will now terminate abnormally.");
Application.Exit();
}
}
static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
try
{
if (e != null && e.Exception != null && e.Exception.InnerException != null)
{
//The unobserved exception is always the same, The actual exception that cause it will be the inner exception.
Exception ex = e.Exception.InnerException;
MessageBox.Show(e.Exception.Message);
//RR.Common.ErrorLogRt.WriteError(ex.StackTrace.ToString(), ex.Message.ToString(), true);
}
else
{
MessageBox.Show("Unhandled Error: " + e.ToString());
}
}
catch
{
MessageBox.Show("Error writing to exception log. This program will now terminate abnormally.");
Application.Exit();
}
}
}
答案 0 :(得分:3)
Remarks from Binding.FormattingEnabled Property
将此属性设置为true还可以启用错误处理行为 导致引发BindingComplete事件。这个的处理程序 事件可以根据成功,错误或者采取适当的行动 通过检查绑定过程中的异常 BindingCompleteEventArgs的BindingCompleteState属性 参数。
internal bool PushData(bool force)
{
Exception ex = null;
if (!force && this.ControlUpdateMode == ControlUpdateMode.Never)
{
return false;
}
if (this.inPushOrPull && this.formattingEnabled)
{
return false;
}
this.inPushOrPull = true;
try
{
if (this.IsBinding)
{
object value = this.bindToObject.GetValue();
object propValue = this.FormatObject(value);
this.SetPropValue(propValue);
this.modified = false;
}
else
{
this.SetPropValue(null);
}
}
catch (Exception ex2)
{
ex = ex2;
if (!this.FormattingEnabled)
{
throw;
}
}
finally
{
this.inPushOrPull = false;
}
if (this.FormattingEnabled)
{
BindingCompleteEventArgs bindingCompleteEventArgs = this.CreateBindingCompleteEventArgs(BindingCompleteContext.ControlUpdate, ex);
this.OnBindingComplete(bindingCompleteEventArgs);
return bindingCompleteEventArgs.Cancel;
}
return false;
}
如您所见,将第4个参数传递为true:DataBindings.Add("Text", this, "TestValue", true
负责捕获PushData
中的异常并将其传递给BindingComplete
事件。如果启用了格式化,除AppDomain.CurrentDomain.FirstChanceException
之外的其他任何地方都没有其他方法(BindingComplete
除外)。
答案 1 :(得分:1)
我知道WPF存在一个解决方案,但我无法让它适用于winforms。 似乎异常被框架所困,我无法找到合适的跟踪来听。
你可以做的是处理第一次机会异常(要注意这可能会让你抓住比你想要的更多的方式)。 这将在您的示例中显示一个消息框,其中包含“此处发生了一些不好的事情”:
AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
//...
private static void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs firstChanceExceptionEventArgs)
{
if(firstChanceExceptionEventArgs.Exception is TargetInvocationException)
{
if(firstChanceExceptionEventArgs.Exception.InnerException != null)
MessageBox.Show(firstChanceExceptionEventArgs.Exception.InnerException.Message);
else
MessageBox.Show(firstChanceExceptionEventArgs.Exception.Message);
}
}
如果你很好奇,这就是我所说的WPF解决方案: http://tech.pro/tutorial/940/wpf-snippet-detecting-binding-errors
答案 2 :(得分:0)
对于微软来说,它似乎已被报告为一个缺陷,微软已将其关闭为“未修复”: ReflectPropertyDescriptor.SetValue does not preserve stack trace
Microsoft Reference Source中的,SetValue
方法的代码(第1085行到第1173行)包含一个结构如下的块:
try // <--- This ...
{
try
{
// Code to invoke SetMethod.
}
catch(Exception)
{
// Code to rewind.
// Code to throw inner or rethrow.
}
}
finally // <--- ... and this consume the exception before you can handle it.
{
// Code to raise change notification.
}
外部try ... finally
块正在消耗(第二次机会)异常,阻止您在代码中处理它。 dbugger仍然可以捕获(第一次机会)异常,但它不会退出SetValue
方法。