疑难杂症 我在做项目的过程中,出现报错的提示,我对他不理解。提示是这样的: 对象未释放 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 异常处理之ThreadException、unhandledException及多线程异常处理 一:ThreadException和unhandledException的区别处理未捕获的异常是每个应用程序起码有的功能,C#在AppDomain提供了UnhandledException 事件来接收未捕获到的异常的通知。常见的应用如下: 代码 static void Main(string[] args) { AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); } static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Exception error = (Exception)e.ExceptionObject; Console.WriteLine("MyHandler caught : " + error.Message); }未捕获的异常,通常就是运行时期的BUG,于是我们可以在UnhandledException 的注册事件方法CurrentDomain_UnhandledException中将未捕获异常的信息记录在日志中。值得注意的是,UnhandledException提供的机制并不能阻止应用程序终止,也就是说,CurrentDomain_UnhandledException方法执行后,应用程序就会被终止。上面我们举的例子来自于控制台程序,UnhandledException可以在任何应用程序域中使用,在某些应用程序模型,如windows窗体程序,还存在ThreadException来处理 Windows 窗体线程中所发生的其未经处理的异常。即,在windows窗体程序中,使用 ThreadException 事件来处理 UI 线程异常,使用 UnhandledException 事件来处理非 UI 线程异常。ThreadException可以阻止应用程序终止。具体使用方法如下: 代码 [STAThread] static void Main() { Application.ThreadException += new ThreadExceptionEventHandler(UIThreadException); Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); Application.Run(new ErrorHandlerForm()); } private static void UIThreadException(object sender, ThreadExceptionEventArgs t) { try { string errorMsg = "Windows窗体线程异常 : \n\n"; MessageBox.Show(errorMsg + t.Exception.Message + Environment.NewLine + t.Exception.StackTrace); } catch { MessageBox.Show("不可恢复的Windows窗体异常,应用程序将退出!"); } } private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { try { Exception ex = (Exception)e.ExceptionObject; string errorMsg = "非窗体线程异常 : \n\n"; MessageBox.Show(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace); } catch { MessageBox.Show("不可恢复的非Windows窗体线程异常,应用程序将退出!"); } } 除了Windows窗体程序,再来说一下WPF程序。WPF的UI线程和Windows的UI线程有点不一样。WPF的UI线程是交给一个叫做调度器的类:Dispatcher。代码如下: 代码public App() { this.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(Application_DispatcherUnhandledException); AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); } void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { try { Exception ex = e.ExceptionObject as Exception; string errorMsg = "非WPF窗体线程异常 : \n\n"; MessageBox.Show(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace); } catch { MessageBox.Show("不可恢复的WPF窗体线程异常,应用程序将退出!"); } } private void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { try { Exception ex = e.Exception; string errorMsg = "WPF窗体线程异常 : \n\n"; MessageBox.Show(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace); } catch { MessageBox.Show("不可恢复的WPF窗体线程异常,应用程序将退出!"); } }无论是Windows窗体程序还是WPF程序,我们都看到捕获的异常当中分为"窗体线程异常"和"非窗体线程异常"。如在Windows窗体程序中,如果在窗体线程中, throw new Exception("窗体线程异常");将会触发ThreadException事件。 Thread t = new Thread((ThreadStart)delegate { throw new Exception("非窗体线程异常"); }); t.Start(); 将会触发UnhandledException事件,然后整个应用程序会被终止。 二:多线程异常处理 多线程的异常处理,要采用特殊的做法。以下的处理方式会存在问题: 代码 try { Thread t = new Thread((ThreadStart)delegate { throw new Exception("多线程异常"); }); t.Start(); } catch (Exception error) { MessageBox.Show(error.Message + Environment.NewLine + error.StackTrace); } 应用程序并不会在这里捕获线程t中的异常,而是会直接退出。从.NET2.0开始,任何线程上未处理的异常,都会导致应用程序的退出(先会触发AppDomain的UnhandledException)。上面代码中的try-catch实际上捕获的还是当前线程的异常,而t是属于新起的异常,所以,正确的做法应该是: 代码 Thread t = new Thread((ThreadStart)delegate { try { throw new Exception("多线程异常"); } catch (Exception error) { MessageBox.Show("工作线程异常:" + error.Message + Environment.NewLine + error.StackTrace); } }); t.Start(); 也就是说,新起的线程中异常的捕获,可以将线程内部代码全部try起来。原则上来说,每个线程自己的异常应该在自己的内部处理完毕,不过仍旧有一个办法,可以将线程内部的异常传递到主线程。在Windows窗体程序中,可以使用窗体的BeginInvoke方法来将异常传递给主窗体线程: 代码 Thread t = new Thread((ThreadStart)delegate { try { throw new Exception("非窗体线程异常"); } catch (Exception ex) { this.BeginInvoke((Action)delegate { throw ex; }); } }); t.Start();上文的代码将最终引发主线程的Application.ThreadException。最终的结果看起来有点像: 在WPF窗体程序中,你可以采用如下的方法将工作线程的异常传递到主线程: 代码 Thread t = new Thread((ThreadStart)delegate { try { throw new Exception("非窗体线程异常"); } catch (Exception ex) { this.Dispatcher.Invoke((Action)delegate { throw ex; }); } }); t.Start();WPF窗体程序的处理方式与Windows窗体程序比较,有两个很有意思的地方:第一个是,在Windows窗体中,我们采用的是BeginInvoke方法。你会发现使用Invoke方法,并不能引发主线程的Application.ThreadException。而在WPF窗体程序中,无论是调度器的Invoke还是BeginInvoke方法都能将异常传递给主线程。第二个地方就是InnerException。WPF的工作线程异常将会抛到主线程,变成主线程异常的InnerException,而Windows窗体程序的工作线程异常,将会被吃掉,直接变为null,只是在异常的Message信息中保存工作线程异常的Message。 三:ASP.NET异常处理我们都知道ASP.NET的全局异常处理方法是Global中的Application_Error方法。我曾经查过ASP.NET的Appdomain.CurrentDomain.unhandledException,结果用反射得到的结果,unhandledException所注册的事件方法根本不是这个方法。联想到ASP.NET页面,包括这个全局处理类,都是交给aspnet_isapi.dll处理的,而aspnet_isapi.dll不是一个托管程序集。所以,应该理解为,ASP.NET的未捕获异常的处理,不同于托管异常(即CLR异常),而是交给aspnet_isapi.dll这个非托管DLL处理的。 对象没有释放? 这种超级炸弹 plus 超级蛋疼 不用COPYMEMORY函数,如何将双精度浮点数存储到数组中 循环输出出了很神奇的问题 求解:WinForm应用程序,DataSet里的更新无法保存至本地数据库mdf文件 急 c#控制iis 填写默认文档问题请教 如何获得打印机所在路径 如何在C#中调用另一个exe文件? C# 访问access问题 关于C#中DataSet的问题... 关于c# contextmenustrip的问题 用正则表达式检测格式的问题 麻煩看下這段新增代碼?哪裡有問題????
异常处理之ThreadException、unhandledException及多线程异常处理 一:ThreadException和unhandledException的区别处理未捕获的异常是每个应用程序起码有的功能,C#在AppDomain提供了UnhandledException 事件来接收未捕获到的异常的通知。常见的应用如下:
代码
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
} static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception error = (Exception)e.ExceptionObject;
Console.WriteLine("MyHandler caught : " + error.Message);
}未捕获的异常,通常就是运行时期的BUG,于是我们可以在UnhandledException 的注册事件方法CurrentDomain_UnhandledException中将未捕获异常的信息记录在日志中。值得注意的是,UnhandledException提供的机制并不能阻止应用程序终止,也就是说,CurrentDomain_UnhandledException方法执行后,应用程序就会被终止。上面我们举的例子来自于控制台程序,UnhandledException可以在任何应用程序域中使用,在某些应用程序模型,如windows窗体程序,还存在ThreadException来处理 Windows 窗体线程中所发生的其未经处理的异常。即,在windows窗体程序中,使用 ThreadException 事件来处理 UI 线程异常,使用 UnhandledException 事件来处理非 UI 线程异常。ThreadException可以阻止应用程序终止。具体使用方法如下:
代码
[STAThread]
static void Main()
{
Application.ThreadException += new ThreadExceptionEventHandler(UIThreadException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.Run(new ErrorHandlerForm());
} private static void UIThreadException(object sender, ThreadExceptionEventArgs t)
{
try
{
string errorMsg = "Windows窗体线程异常 : \n\n";
MessageBox.Show(errorMsg + t.Exception.Message + Environment.NewLine + t.Exception.StackTrace);
}
catch
{
MessageBox.Show("不可恢复的Windows窗体异常,应用程序将退出!");
}
} private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
try
{
Exception ex = (Exception)e.ExceptionObject;
string errorMsg = "非窗体线程异常 : \n\n";
MessageBox.Show(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace);
}
catch
{
MessageBox.Show("不可恢复的非Windows窗体线程异常,应用程序将退出!");
}
} 除了Windows窗体程序,再来说一下WPF程序。WPF的UI线程和Windows的UI线程有点不一样。WPF的UI线程是交给一个叫做调度器的类:Dispatcher。代码如下:
代码
public App()
{
this.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(Application_DispatcherUnhandledException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
} void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
try
{
Exception ex = e.ExceptionObject as Exception;
string errorMsg = "非WPF窗体线程异常 : \n\n";
MessageBox.Show(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace);
}
catch
{
MessageBox.Show("不可恢复的WPF窗体线程异常,应用程序将退出!");
}
} private void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
try
{
Exception ex = e.Exception;
string errorMsg = "WPF窗体线程异常 : \n\n";
MessageBox.Show(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace);
}
catch
{
MessageBox.Show("不可恢复的WPF窗体线程异常,应用程序将退出!");
}
}无论是Windows窗体程序还是WPF程序,我们都看到捕获的异常当中分为"窗体线程异常"和"非窗体线程异常"。如在Windows窗体程序中,如果在窗体线程中, throw new Exception("窗体线程异常");
将会触发ThreadException事件。 Thread t = new Thread((ThreadStart)delegate
{
throw new Exception("非窗体线程异常");
});
t.Start();
将会触发UnhandledException事件,然后整个应用程序会被终止。 二:多线程异常处理 多线程的异常处理,要采用特殊的做法。以下的处理方式会存在问题:
代码
try
{
Thread t = new Thread((ThreadStart)delegate
{
throw new Exception("多线程异常");
});
t.Start();
}
catch (Exception error)
{
MessageBox.Show(error.Message + Environment.NewLine + error.StackTrace);
} 应用程序并不会在这里捕获线程t中的异常,而是会直接退出。从.NET2.0开始,任何线程上未处理的异常,都会导致应用程序的退出(先会触发AppDomain的UnhandledException)。上面代码中的try-catch实际上捕获的还是当前线程的异常,而t是属于新起的异常,所以,正确的做法应该是:
代码
Thread t = new Thread((ThreadStart)delegate
{
try
{
throw new Exception("多线程异常");
}
catch (Exception error)
{
MessageBox.Show("工作线程异常:" + error.Message + Environment.NewLine + error.StackTrace);
}
});
t.Start(); 也就是说,新起的线程中异常的捕获,可以将线程内部代码全部try起来。原则上来说,每个线程自己的异常应该在自己的内部处理完毕,不过仍旧有一个办法,可以将线程内部的异常传递到主线程。在Windows窗体程序中,可以使用窗体的BeginInvoke方法来将异常传递给主窗体线程:
代码
Thread t = new Thread((ThreadStart)delegate
{
try
{
throw new Exception("非窗体线程异常");
}
catch (Exception ex)
{
this.BeginInvoke((Action)delegate
{
throw ex;
});
}
});
t.Start();上文的代码将最终引发主线程的Application.ThreadException。最终的结果看起来有点像: 在WPF窗体程序中,你可以采用如下的方法将工作线程的异常传递到主线程:
代码
Thread t = new Thread((ThreadStart)delegate
{
try
{
throw new Exception("非窗体线程异常");
}
catch (Exception ex)
{
this.Dispatcher.Invoke((Action)delegate
{
throw ex;
});
}
});
t.Start();WPF窗体程序的处理方式与Windows窗体程序比较,有两个很有意思的地方:第一个是,在Windows窗体中,我们采用的是BeginInvoke方法。你会发现使用Invoke方法,并不能引发主线程的Application.ThreadException。而在WPF窗体程序中,无论是调度器的Invoke还是BeginInvoke方法都能将异常传递给主线程。第二个地方就是InnerException。WPF的工作线程异常将会抛到主线程,变成主线程异常的InnerException,而Windows窗体程序的工作线程异常,将会被吃掉,直接变为null,只是在异常的Message信息中保存工作线程异常的Message。 三:ASP.NET异常处理我们都知道ASP.NET的全局异常处理方法是Global中的Application_Error方法。我曾经查过ASP.NET的Appdomain.CurrentDomain.unhandledException,结果用反射得到的结果,unhandledException所注册的事件方法根本不是这个方法。联想到ASP.NET页面,包括这个全局处理类,都是交给aspnet_isapi.dll处理的,而aspnet_isapi.dll不是一个托管程序集。所以,应该理解为,ASP.NET的未捕获异常的处理,不同于托管异常(即CLR异常),而是交给aspnet_isapi.dll这个非托管DLL处理的。