我正在使用Windows窗体应用程序,并且使用System.Timers.Timer来定期检查数据库中的数据。
如何将计时器Elapsed事件处理程序中发生的异常传递到主应用程序中?如果我使用下面的代码,异常会被“吞噬”,主应用程序永远不会得到它(即使我有ThreadException和UnHandledException的处理程序)。
// Main Form
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
// Manager class
private System.Timers.Timer _timer;
void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
doSomeDatabaseActions();
}
catch (Exception ex)
{
throw new ApplicationException("How do I get this error back into main thread...", ex);
}
}
答案 0 :(得分:30)
如果您无权访问主线程,则可以将异常抛出到另一个非计时器线程上:
catch (Exception exception)
{
ThreadPool.QueueUserWorkItem(
_ => { throw new Exception("Exception on timer.", exception); });
}
答案 1 :(得分:25)
由于System.Timers.Timer
吞下了事件处理程序中抛出的任何异常,因此您需要将异常编组到另一个线程(可能是UI线程)。您可以通过Control.Invoke
执行此操作,或者将错误信息存储在成员变量中,并让UI线程在操作完成后检查此错误信息。如果不是null
,那么UI就可以抛出。
来自MSDN:
在.NET Framework 2.0版和 之前,Timer组件捕获 并抑制抛出的所有异常 由Elapsed的事件处理程序 事件。此行为受制于 在将来的.NET版本中进行更改 框架。
刚刚在.NET 4.0中检查过,此行为尚未更改。
答案 2 :(得分:4)
您可以将异常分配给局部变量并检查是否抛出了异常:
// Main Form
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
// Manager class
private System.Timers.Timer _timer;
private exception = null;
void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
//reset the exception in case object is reused.
this.exception = null;
try
{
doSomeDatabaseActions();
}
catch (Exception ex)
{
this.exception = ex;
}
}
/**
* Checks whether the exception object is set.
*/
public bool hasExceptionOccured(){
return (this.exception != null);
}
//The main application will call this to get the exception.
public Exception getException(){
return this.exception;
}
答案 3 :(得分:2)
我想你想要在主窗体上处理异常,这个解决方案不完整,但是展示了如何用“Action”来实现它
using System;
using System.Timers;
public class MainForm
{
public MainForm()
{
var tm = new TestManager(exception =>
{
//do somthing with exception
//note you are still on the timer event thread
});
}
}
public class TestManager
{
private readonly Action<Exception> _onException;
public TestManager(System.Action<System.Exception> onException )
{
_onException = onException;
}
private System.Timers.Timer _timer;
void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
doSomeDatabaseActions();
}
catch (Exception ex)
{
//throw new ApplicationException("How do I get this error back into main thread...", ex);
_onException(ex);
}
}
}