System.Threading.ThreadExceptionEventHandler errHandler = delegate (object sender, System.Threading.ThreadExceptionEventArgs e) { if (e.Exception.GetType().Equals(typeof(System.Data.SqlClient.SqlException))) { MessageBox.Show("操作数据库出错,请检查数据库配置!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { MessageBox.Show("进程发生未知错误,请重新操作!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error); } }; UnhandledExceptionEventHandler errOthersThreadHandler = delegate (object sender, UnhandledExceptionEventArgs e) { MessageBox.Show("程序发生未知错误,请重新操作!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error); }; Application.ThreadException += errHandler; AppDomain.CurrentDomain.UnhandledException += errOthersThreadHandler; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Main()); //清除异常资源,防止内存漏洞 Application.ThreadException -= errHandler; AppDomain.CurrentDomain.UnhandledException -= errOthersThreadHandler;
ASP.NET中处理异常的几种方式
发布时间:2013年10月11日 / 分类:ASP.NET / 5,775 次浏览 / 评论
1、程序中使用try catch
对于预知会发生异常的代码段使用try catch主动捕获异常,适用于提示给用户或跳转到错误页面,或者通过其它方式处理异常(日志、通知等)。
1
2
3
4
5
6
7
8
9
10
11
12
|
int i = 10;
int j = 0;
try
{
Label1.Text = (i / j).ToString();
}
catch (Exception ex)
{
// 这里处理异常:Redirect、Transfer、Log、Notice等
Console.WriteLine("Page:" + ex.Message);
}
|
2、Global中使用Application_Error
如果异常在程序中没有被处理(如没有try catch),则异常的处理会流转到这个方法,这里边可以对异常进行处理。但是此方式不能捕捉子线程中的异常。
1
2
3
4
|
int i = 10;
int j = 0;
Label2.Text = (i / j).ToString();
|
1
2
3
4
5
|
void Application_Error(object sender, EventArgs e)
{
// 在出现未处理的错误时运行的代码
Server.Transfer("ErrorPage.aspx");
}
|
1
2
|
string message = HttpContext.Current.Error != null ? (HttpContext.Current.Error.InnerException != null ? HttpContext.Current.Error.InnerException.Message : string.Empty) : string.Empty;
Label1.Text = message;
|
3、在web.config中配置
出现错误后跳转到ErrorPage.aspx,和Application_Error类似,采用redirectMode模式可以传递异常到错误页面。
1
2
3
|
<customErrors redirectMode="ResponseRewrite" mode="On" defaultRedirect="~/ErrorPage.aspx">
<error statusCode="500" redirect="~/ErrorPage.aspx"/>
</customErrors>
|
4、使用FirstChance异常通知。
关联到AppDomain,如果应用程序域内发生异常,则会首先触发这个事件,然后才查找catch块处理异常。不过在这个事件中不能处理异常,不能消灭异常,只是可以按照通知进行处理。因为如果这里处理了异常,catch块就不能进行处理了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
void Application_Start(object sender, EventArgs e)
{
// 在应用程序启动时运行的代码
AppDomain.CurrentDomain.FirstChanceException += new EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs>(CurrentDomain_FirstChanceException);
}
void CurrentDomain_FirstChanceException(object sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
{
Console.WriteLine(e.Exception.Message);
// 错误:响应在上下文中不能使用
// Response.Redirect("ErrorPage.aspx");
// 错误:未将对象引用设置到对象的实例
// Server.Transfer("ErrorPage.aspx");
}
|
5、绑定UnhandledException事件
关联到AppDomain,关于这个事件并不是每次都能触发,和使用的方式有关,情况比较多。一般情况下我们只能获取这个异常,而不能阻止中断应用程序。
下边给出一个例子:
1
2
3
4
5
6
7
8
9
10
|
void Application_Start(object sender, EventArgs e)
{
// 在应用程序启动时运行的代码
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine(e.Exception.Message);
}
|
一般的未处理异常都会被Application_Error捕捉到,我们这里在线程中抛出一个异常。
另外StackOverflowException在.net4中不再能被UnhandledException捕捉到。
1
2
3
4
5
6
7
8
9
10
11
|
private void CutString()
{
//throw (new Exception("Test Unhandled exception"));
//throw (new StackOverflowException("Test Unhandled exception"));
}
protected void Button1_Click(object sender, EventArgs e)
{
Thread th = new Thread(new ThreadStart(CutString));
th.Start();
}
|
更多请参考:
http://msdn.microsoft.com/zh-Cn/library/system.appdomain.unhandledexception.aspx
http://mlichtenberg.wordpress.com/2011/09/19/catching-unhandled-exceptions-in-asp-net/
6、延伸:子线程异常的处理。网上有介绍通过代理在主线程处理子线程的异常,但是在asp.net中是无状态的,主线程有可能很快消失,其中的某些处理可能执行失败。
这里使用Thread.Sleep使主线程不会很快结束。这种异常处理起来很麻烦,不建议在asp.net中使用处理时间很长的线程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
protected void Button1_Click(object sender, EventArgs e)
{
Thread th = new Thread(new ThreadStart(() =>
{
try
{
throw (new Exception("Test Unhandled exception"));
}
catch (Exception ex)
{
//跳转到错误页面
Response.Redirect("ErrorPage.aspx");
}
}));
th.Start();
// asp.net主线程会很快结束,这里让他等等页面跳转。
Thread.Sleep(2000);
}
|
本文列举了处理异常的几种方式,有通过订阅AppDomain事件的方式,有通过配置文件的方式,还有通过Global的方式,最后还对子线程异常的处理谈了一点想法,但是都没有提供一个完善的解决方案,有兴趣的朋友可以自己试试。
C#全局异常捕获
Invoke调用函数(如反射)好比是你在一个新的线程上执行函数。
所以函数内的异常会先抛到运行时,然后才是你Invoke的层,然而你没有捕获运行时异常,故程序会崩溃,那你Invoke的catch就没机会继续捕获异常了,要解决你的这个问题也很简单。
C# 全局异常捕获
开发界有那么一个笑话,说是“「我爱你」三个字,讲出来只要三秒钟,解释要三小时,证明却要一辈子。「Bug」三个字母,发现需要三秒,找到需要三小时,Debug却要一辈子。”。就算是资深的程序员也会写出Bug,但Bug并不可怕,重要的是在Bug发生的时候迅速定位Bug。
在Visual Studio中调试的时候,我们可以借助VS的调试工具进行调试,一旦出现未处理的异常时,VS也会在第一时间捕获并显示出来。随着开发的进行,终于程序要打包上线了。那么在上线之出了BUG我们该如何处理呢?
相信如果各位年龄够大,应该都见识过QQ出错崩溃吧?在零几年的时候QQ崩溃还不是一件稀罕事儿。每当QQ崩溃的时候都会弹出一个BUGReporter程序,会希望我们将出错的报告发送给腾讯
其实我们标题所说的全局异常捕获主要目标并不是为了将异常处理掉防止程序崩溃。因为当错误被你的全局异常捕获器抓到的时候,已经证实了你程序中存在BUG。一般而言,我们的全局异常捕获主要作用就是接收到异常之后进行异常的反馈。
一、简单粗暴:在Program.cs使用Try...Catch...
大家都知道,异常是通过Throw命令抛出,一路从抛出的模块里上抛,如果中途没有被try...catch...抓住的话就会一直抛到CLR(公共语言运行时)。如果用栈来描述这个过程的话,那就是异常会从栈的栈顶一路下沉,直到中途被try...catch...抓住或者直至沉到栈底,被CLR接住。CLR接收到异常之后的处理方式非常的简单粗暴——直接报错,然后关闭程序。
不过根据刚刚我们所描述的异常上抛过程,我们不难发现:只要我们在程序把异常抛给CLR之前,抢先把异常捕获,那就可以做到全局异常处理了。不过这个try...catch...就必须放在栈的最下方。程序运行时栈的最下方函数其实就是程序运行时第一个调用的函数——main()函数。
比如说我有这么一个Windows窗体程序,一个Program.cs类,一个FrmMain.cs窗口。
Program.cs类内容如下:
|
FrmMain里有一个Button,其点击事件如下:
|
如果就现在这样的代码而言,只要在运行时单击这个Button就能时整个程序报错崩溃。
现在我们改造Program.cs里的main()函数,改成以下内容:
|
现在再次执行程序,单击button1,我们可以看到现在这次的这个异常被main()函数中的try...catch...所抓获了。
我们在这里catch内只是放了一个MessageBox,后面可以根据自己的需要改成错误报告代码。
二、更优雅的事件监听:Application.ThreadException
刚刚在上面提到了一种简单粗暴的方法,就是用try...catch...把main()函数所有内容全部包住。不过这样的代码看起来就有点蠢了。有没有什么更加优雅的方法吗?
答案当然是有。还记得Application类吧,这个负责控制整个Windows 程序的运行。我们可能用到这个类的时候更多的时候用的是下面几个方法或属性:
今天在这里我们要用到一个Application类的一个事件:ThreadException。
我们可以按F12转到Application类的定义,在ThreadException事件上,微软对它的注释是:“在发生未捕获线程异常时发生。”,这正是我们的目标。
假设还是之前的那个程序,我们将程序的Program.cs内容填入以下代码:
|
现在我们重新运行这个程序,单击button1,查看效果:
可以说效果出乎意料的完美,上一章里在main()函数里用try..catch...包住所有代码,当程序异常时将代码将会进入catch块里,处理完成后程序就退出了。然而在这边我们用Application.ThreadException事件监听并处理后,程序并不会因为异常而退出。可以说是可挽回的异常。
三、子线程异常捕获AppDomain.CurrentDomain.UnhandledException
在上面我们提到了可以通过监听Application.ThreadException事件来捕获程序异常,但这个Application.ThreadException事件只能捕获程序主线程上发生的异常。如果你用到了多线程,而且在子线程中发生了异常,不会触发Application.ThreadException事件的。
如果要监听子线程的异常,我们就需要再注册一个事件:AppDomain.CurrentDomain.UnhandledException
这个事件是在当前程序域内发生未处理异常时才会发生(如果没有监听Application.ThreadException事件的话,主线程异常最终也会触发这个事件)
我们在第一章的窗口里再加一个Button按钮,名为button2,其单击事件内容为:
|
上面的代码会在运行时创建一个线程,线程代码只有一句“throw new Exception();”致使线程抛出异常。
我们再改一下Program.cs类的内容:
|
我们来试一下,运行程序,单击button2:
漂亮,监听到事件了。但在这里我们需要注意的是注意这个AppDomain.CurrentDomain.UnhandledException事件有一个事件参数叫UnhandledExceptionEventArgs e,它有一个bool类型的IsTerminating属性。这个属性指示了公共语言运行时(CLR)会不会因为本次异常而退出。如果这个属性是true的话,那么我们可以称这个错误是不可挽回的,就算我们监听到了这个事件,在这个事件的代码执行结束后,整个程序还是会崩溃退出的(因为我在VS里,所以被VS捕捉到了。)。
四、Web程序全局异常捕获
之前提到的都是Windows程序的全局异常捕获,现在Web程序也是我们经常要与之打交道的一部分。那Web程序该如何进行全局异常捕获呢?,下面以Asp.net MVC4做演示。
我们假设我们有一个Home控制器(Controller),里面有一个Error页面,访问这个页面将抛出一个异常。
|
1. Global.asax中编写Application_Error函数
我们可以在Global.asax里写一个protected void Application_Error(object sender, EventArgs e)的函数,当发生未经处理的异常时,Asp.net将自动执行此函数。
在Global.asax文件的MvcApplication类里其他空白位置写上以下代码:
|
2.使用Filter进行全局异常捕获
Filter其实就是过滤器,在Asp.net MVC中Filter一共分为4大类
分类 | 接口 | 默认实现 | 运行时间 |
Authorization | IAuthorizationFilter | AuthorizeAttribute | 在Action方法之前和其它类型的Filter之前运行。 |
Action | IActionFilter | ActionFilterAttribute | 在Action方法之前运行。 |
Result | IResultFilter | ActionFilterAttribute | 在处理ActionResult之前或之后运行。 |
Exception | IExceptionFilter | HandleErrorAttribute | 在Action方法、ActionResult和其他类型的Filter抛出异常时运行。 |
我们可以看到有一个Exception类型的Filter,我们在这里就是借助这个Exception的Filter进行全局异常捕获。
新建一个类,名叫MyExceptionFilter,使其继承FilterAttribute类,并实现IexceptionFilter接口。MyExceptionHandler类代码如下:
|
然后我们打开项目的“App_Start”文件夹下的“FilterConfig.cs”文件,修改其RegisterGlobalFilters方法内的代码,修改如下:
|
写在最后:
在Windows程序里,如果同时监听了Application.ThreadException事件和AppDomain.CurrentDomain.UnhandledException事件的话,则异常优先被Application.ThreadException事件捕获。但Application.ThreadException事件只能监听程序主线程抛出的异常。
在Web程序里,如果同时使用了Global.asax中编写Application_Error函数进行全局异常捕获和使用Filter进行全局异常捕获,异常优先被Filter捕获。
南来地,北往的,上班的,下岗的,走过路过不要错过!
======================个性签名=====================
之前认为Apple 的iOS 设计的要比 Android 稳定,我错了吗?
下载的许多客户端程序/游戏程序,经常会Crash,是程序写的不好(内存泄漏?刚启动也会吗?)还是iOS本身的不稳定!!!
如果在Android手机中可以简单联接到ddms,就可以查看系统log,很容易看到程序为什么出错,在iPhone中如何得知呢?试试Organizer吧,分析一下Device logs,也许有用.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构