Python中处理unchecked未捕获异常实例,pythonunchecked

Talk Is Cheap

和Java一样,python也提供了对于checked exception和unchecked exception.
对于checked exception,我们通常使用try except可以显示解决,对于unchecked
异常,其实也是提供回调或者是钩子来帮助我们处理的,我们可以在钩子里面记录崩溃栈追踪或者发送崩溃数据.

下面代码可以实现python unchecked exception回调,并输出日志信息.

Show Me The Code

复制代码 代码如下:

#!/usr/bin/env python
# coding=utf-8
import os, sys
import logging
logger = logging.getLogger(__name__)
handler = logging.StreamHandler(stream=sys.stdout)
logger.addHandler(handler)

def handle_exception(exc_type, exc_value, exc_traceback):
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    logger.error(“Uncaught exception”, exc_info=(exc_type, exc_value,
exc_traceback))

sys.excepthook = handle_exception
if __name__ == “__main__”:
    raise RuntimeError(“Test unhandled Exception”)

相关解释

1.上述忽略处理终端下键盘按Ctrl + C 终止异常.
2.上述使用python的日志管理模块输出格式化的异常信息.

Talk Is Cheap 和Java一样,python也提供了对于checked exception和unchecked
exception. 对于checked exception…

转载自:JmilkFan_范桂飓:http://blog.csdn.net/jmilk

当一个线程由于未捕获异常而退出时,JVM会把这个事件报告给应用程序提供的UncaughtExceptionHandler异常处理器。如果没有提供任何异常处理器,那么默认的行为是将栈追踪信息输出到System.err.

这一篇文章来谈谈对于WPF应用程序开发中的未捕获异常的处理。

.Net 下未捕获异常的处理

异常

异常即非正常状态,在Python中使用异常对象来表示异常。若程序在编译或运行过程中发生错误,程序的执行过程就会发生改变,抛出异常对象,程序流进入异常处理。如果异常对象没有被处理或捕捉,程序就会执行回溯(Traceback)来终止程序。

例子

public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                int i = 1 / 0;

                System.out.println("dddddd");
            }
        }).start();

上面代码执行时将抛出零除异常,我并没有将它捕获,那么JVM如何处理的呢?

首先,我们当然是要求应用程序开发人员,尽可能地在程序可能出现异常的地方都去捕捉异常,使用try…catch的方式。但是总是有一些意外的情况可能会发生,这就导致会出现所谓的“未捕获异常(UnhandledException)”。对于这一类异常,如果我们没有一个合适的策略进行处理,则当其发生的时候,会给用户带来不太好的使用体验。例如下面这样

 作者:Eaglet

<a name=”t2″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>异常类型

通用异常类型表

异常 描述
BaseException 所有异常的基类
SystemExit 解释器请求退出
KeyboardInterrupt 用户中断执行(通常是输入^C)
Exception 常规错误的基类
StopIteration 迭代器没有更多的值
GeneratorExit 生成器(generator)发生异常来通知退出
StandardError 所有的内建标准异常的基类
ArithmeticError 所有数值计算错误的基类
FloatingPointError 浮点计算错误
OverflowError 数值运算超出最大限制
ZeroDivisionError 除(或取模)零 (所有数据类型)
AssertionError 断言语句失败
AttributeError 对象没有这个属性
EOFError 没有内建输入,到达EOF 标记
EnvironmentError 操作系统错误的基类
IOError 输入/输出操作失败
OSError 操作系统错误
WindowsError 系统调用失败
ImportError 导入模块/对象失败
LookupError 无效数据查询的基类
IndexError 序列中没有此索引(index)
KeyError 映射中没有这个键
MemoryError 内存溢出错误(对于Python 解释器不是致命的)
NameError 未声明/初始化对象 (没有属性)
UnboundLocalError 访问未初始化的本地变量
ReferenceError 弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError 一般的运行时错误
NotImplementedError 尚未实现的方法
SyntaxError Python 语法错误
IndentationError 缩进错误
TabError Tab 和空格混用
SystemError 一般的解释器系统错误
TypeError 对类型无效的操作
ValueError 传入无效的参数
UnicodeError Unicode 相关的错误
UnicodeDecodeError Unicode 解码时的错误
UnicodeEncodeError Unicode 编码时错误
UnicodeTranslateError Unicode 转换时错误
Warning 警告的基类
DeprecationWarning 关于被弃用的特征的警告
FutureWarning 关于构造将来语义会有改变的警告
OverflowWarning 旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning 关于特性将会被废弃的警告
RuntimeWarning 可疑的运行时行为(runtime behavior)的警告
SyntaxWarning 可疑的语法的警告
UserWarning 用户代码生成的警告

Exception类:是通用异常基类下列异常类均继承于Exception类,Python解析器会自动将通用异常类型名称放在内建命名空间中,所以当使用通用异常类型时,不需要import
exceptions模块。

处理流程

图片 1

进到调用的方法里看看:

图片 2

可以看到,当JVM遇到未捕获的异常时,首先获取应用程序提供的UncaughtExceptionHandler异常处理器。

图片 3

如果没提供的话就直接把栈追踪信息输出到System.err;

图片 4

     
随着.Net技术的发展,.Net技术被逐渐应用到很多大型的应用软件项目中。这些项目的规模越来越大,很多项目中除了自己的代码外还引用了很多第三方的.net组件。同时很多项目又被应用到很多关键的部门,软件系统的稳定性越来越至关重要。由于.Net
框架提供了非常强大的异常处理机制,同时对一些非托管代码很难控制的系统问题比如指针越界,内存泄漏等提供了很好的解决方案。相比非托管代码构建的系统,.Net构建的系统更加稳定。不过这并不是说.Net构建的系统就完全无懈可击,很多由于代码的不严谨或者系统问题引发的故障将会导致.Net应用程序产生未捕获异常,从而导致应用程序异常终止。本文将对三种最常见的.Net应用的未捕获异常处理进行阐述。

<a name=”t3″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>异常处理

备注:这个截图是在Windows
8上面做的,其他操作系统看到的界面可能略有不同。

     
在开始本文之前,让我们来看看.Net在什么情况下会产生未捕获异常。未捕获异常从定义上说就是结构化异常处理未能捕获的异常。通俗的讲就是发生在Try
Catch块意外的异常。那么是不是我们在Main函数中加一个Try Catch
块就可以捕获全部未捕获异常了呢?答案是否定的。这里面有两种情况无法通过这种方法捕获:

<a name=”t4″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>触发异常raise

raise关键字:手动抛出一个通用的异常类型(Exception),类似Java中的throw语句。raise关键字后跟异常的名称,异常名称能够标识出异常类的对象。执行raise语句时,python会创建指定异常类的对象,还能够指定对异常对象进行初始化的参数,参数也可以为由若干参数组成的元组。

注意:一旦执行raise语句,程序就会被终止。

格式raise [exceptionType[,argument][,traceback]]

def testRaise(number):
    if number < 1:
        raise ValueError('Invalid value') #或者 raise ValueError,'Invalid value'

testRaise(0)

traceback:这个参数用于追踪异常对象,一般很少使用。

这样就可以触发一个异常,并且接收异常信息。

用户看到这个窗口的时候,其实一般只能点击Close the
prograrm按钮。也就是说,这种情况下会导致用户无法继续使用这个程序,而且他们还得不到任何具体的消息:到底发生了什么事情了?除非他们去查看Windows的事件日志。(但一般的用户是不太会这个操作的)

  1. GC
    产生的异常,这种异常通常因为Finalize函数中引发未捕获异常引起。当然这并不绝对,一些系统问题比如内存耗尽有时候也会造成GC异常。

<a name=”t5″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>传递异常

当你捕获到异常之后又希望再次的触发异常只需要使用不带任何参数的raise关键字。

!/usr/bin/env python
import os
try:
    openFile = open('notExistsFile.txt','r')
    fileContent = openFile.readlines()
except IOError:
    print 'File not Exists'
    if not os.path.exists('notExistsFile.txt'):
        raise
except:
    print 'process exception'

异常会在捕获之后再次触发同一个异常。

图片 5

2.
主线程以为的线程引发的未捕获异常。这些异常我们往往可以在线程的主函数中用Try
Catch 来捕获,但如果系统中使用了外部的组件,或者甚至是.Net
框架自带的一些系统组件,由这些组件的线程引发的异常,调用代码无法通过Try
Catch来捕获。

<a name=”t6″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>assert语句触发异常

assert语句根据后面的表达式的真假来控制程序流。若为True,则往下执行。若为False,则中断程序并调用默认的异常处理器,同时输出指定的提示信息。

格式:

assert expression,'information'

Example:

#!/usr/bin/env python
def testAssert(x):
    assert x < 1,'Invalid value'
testAssert(1)
print 'Valid value'

output:

AssertionError: Invaild value

我们可以看到在Windows事件日志中,会有两个具体的事件。首先是一个.NET
Runtime的事件

从上面两点来看,即使我们的代码在每个地方都加了Try Catch
,也不能百分百杜绝未捕获异常的发生。

<a name=”t7″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>捕获异常try..except..else

注意:except子句的数量没有限制,但使用多个except子句捕获异常时,如果异常类之间具有继承关系,则子类应该写在前面,否则父类将会直接截获子类异常。放在后面的子类异常也就不会执行。

格式:

try:
    可能触发异常的语句块
except [exceptionType]:
    捕获可能触发的异常[可以指定处理的异常类型]
except [exceptionType][,date]:
    捕获异常并获取附加数据
except:
    没有指定异常类型,捕获任意异常
else:
    没有触发异常时,执行的语句块

try的工作原理

执行一个try语句时,python解析器会在当前程序流的上下文中作标记,当出现异常后,程序流能够根据上下文的标记回到标记位,从而避免终止程序。

1.
如果try语句执行时发生异常,程序流跳回标记位,并向下匹配执行第一个与该异常匹配的except子句,异常处理完后,程序流就通过整个try语句(除非在处理异常时又引发新的异常)。

2.
如果没有找到与异常匹配的except子句(也可以不指定异常类型或指定同样异常类型Exception,来捕获所有异常),异常被递交到上层的try(若有try嵌套时),甚至会逐层向上提交异常给程序(逐层上升直到能找到匹配的except子句。实在没有找到时,将结束程序,并打印缺省的错误信息)。

3.
如果在try子句执行时没有发生异常,python将执行else语句后的语句(可选),然后控制流通过整个try语句。

#!/usr/bin/env python
try:
    openFile = open('notExistsFile.txt','r')
    fileContent = openFile.readlines()
except IOError:   
    print 'File not Exists'        #执行
except:
    print 'process exception'      #不执行
else:
    print 'Reading the file'       #不执行

output:

In [157]: %run testError.py
File not Exists

嵌套try

#!/usr/bin/env python
try:
    try:
        openFile = open('notExistsFile.txt','r')
        fileContent = openFile.readlines()
    except IOError:
        print 'File not Exists'      #执行
except:
    print 'process exception'        #不执行 
else: 
    print 'Reading the file'         #执行

Output:

In [159]: %run testError.py
File not Exists
Reading the file

图片 6

鉴于此,为了提高系统的健壮性和可维护性,我们需要通过一种方法来截获这些未捕获异常,并进行适当的处理。

<a name=”t8″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>捕捉多个异常

方法一:指定一个通用异常,可以捕获多个不同的包含在Exception类中的异常类。

try:
    语句块
except Exception:
    语句块

方法二:在一个except子句后将多个异常作为元组元素列出。

try:
    语句块
except (IOError,ValueError): 
    语句块

方法三:except子句后不带任何异常名称,捕获所有异常

try:
    语句块
except: 
    语句块

然后是一个Application Error的事件

.Net 的设计者已经考虑到这些问题,并且为我们提供了一个叫
UnhandledExceptionEventHandler
的事件,通过这个事件,我们可以截获未捕获异常,并进行处理。

<a name=”t9″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>try..finally语句

无论try语句块中是否触发异常,都会执行finally子句中的语句块,因此一般用于关闭文件或关闭因系统错误而无法正常释放的资源。比如文件关闭,释放锁,把数据库连接返还给连接池等。

import os
def write_test(fileName,content_iterable):
    try:
        pwd = open(fileName,'w')
        for key,value in content_iterable.items():
            pwd.write(key+'\t'+value+'\n')  #传入String类型参数同时加入换行符
    finally:
        pwd.close()

if __name__ == '__main__':
    fileName = '/usr/local/src/pyScript/fileOperation.txt'
    dic = {'name':'Jmilk','age':'23','city':'BJ'}
    if os.path.exists(fileName):
        write_test(fileName,dic)
    else:print 'File not exist!'

注意:try..finally与try..except 是可以同时使用的。

In [3]: try:
   ...:     raise
   ...: except Exception:
   ...:     print 'error'
   ...:     raise
   ...: finally:
   ...:     print 'success'
   ...:
error
success
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-530db52949e7> in <module>()
      1 try:
----> 2     raise
      3 except Exception:
      4     print 'error'
      5     raise

TypeError: exceptions must be old-style classes or derived from BaseException, not NoneType

NOTE:try…finally 的意义在于,就是我们在 try 代码块中执行了 return
语句,但是仍然会继续执行在 finally
中的代码块,所以我们一般用作处理资源的释放。

图片 7

这个事件的事件参数UnhandledExceptionEventArgs e,
有两个属性,一个是ExceptionObject,这个属性返回为截获异常的对象实例。还有一个属性是IsTerminating,这个属性告诉我们这个异常是否会导致应用终止。这里需要说明的是,对于.Net1.1
和 .Net2.0及以上,情况是不一样的,.Net1.1
只有在主线程中的未捕获异常才会终止应用程序,而.Net2.0及以上版本则是始终终止应用程序。如果不终止应用程序,而是有CLR
将当前异常消化,系统的运行状态很可能不可控,最后可能会发生更大的故障,所以.Net2.0以后,对于所有未捕获异常,一律终止当前应用。这样看来,对于.net2.0以上的应用似乎我们截获未捕获异常已经毫无意义,其实不然。通过截获为未捕获异常,我们可以记录下程序是再哪里产生这种未捕获异常的,以便程序的开发者改进程序。我们也可以在当前应用退出前为系统做一些其他的保护工作,比如备份数据,告警提示等等。

<a name=”t10″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>自定义异常

通过(直接或简介)继承Exception类来创建一个自定义异常类,自定义的异常类只能通过raise关键字来手动触发。

class testError(Exception):    #直接集成Exception类
    def __init__(self,arg):
        self.args = arg
try:
    raise testError('Just test')
except testError,info:
    print info.args

output:

In [52]: %run test.py
('J', 'u', 's', 't', ' ', 't', 'e', 's', 't')

通常来说,这样的用户体验有值得改进的地方。我们虽然不能防止异常的产生,但是当意外发生的时候,我们应该要以更好地方式地通知到用户,或者尽可能地不要影响用户当前的操作。

下面我们来看看三种常见的.Net应用分别如何来截获未捕获异常。

<a name=”t11″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>with..as触发异常自动关闭资源

在使用类文件的流对象时,都需要单独的调用close()来关闭资源。with..as语句能够实现在with语句块执行完后,自动的关闭文件。如果with语句块中触发异常,会调用默认的异常处理器处理,而且文件仍然能够正常关闭。

#!/usr/bin/env python
import os
def testWith(fileName):
    try:
        with open(fileName,'r+') as pwd:
            pwd.readlines()
            print 2/0
    except Exception:
            print 'File closed:',pwd.closed  #判断文件是否关闭
if __name__ == '__main__':
    if os.path.exists('/usr/local/src/pyScript/fileOperation.txt'):
        testWith('/usr/local/src/pyScript/fileOperation.txt')
        print 'continue'

output:

In [17]: %run test.py
File closed: True    #没有call close()函数,文件仍然自动关闭。
continue

 

  • 控制台应用

<a name=”t12″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>as获取异常信息

每个异常都会有一定的描述信息,可以通过as关键字来获取。但是这种异常信息并不适合一般用户阅读,所以会使用自定义的异常信息。但是仍然会将原有的异常信息保留起来,用于后期的异常分析。

#!/usr/bin/env python
try:
    try:
        openFile = open('notExistsFile.txt','r')
        fileContent = openFile.readlines()
    except (IOError,ValueError) as info:  #或者except (IOError,ValueError),info: 
        print info
except:
    print 'process exception'
else:
    print 'Reading the file'

output:

In [164]: %run testError.py
[Errno 2] No such file or directory: 'notExistsFile.txt'
Reading the file

在WPF这种应用程序中,会有两大类未处理异常:一类是在UI线程抛出来的,例如点击了用户界面上面的某个控件,然后执行某个代码的时候,遇到了异常;另一类是非UI线程跑出来的,例如在一个多线程的程序里面,工作线程的代码遇到了异常。

  

<a name=”t13″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>异常参数

也可以使用异常参数作为输出的异常信息参数,来获取异常信息。并且异常参数中包含有异常信息、错误数字、错误位置等属性。

#!/usr/bin/env python
try:
    try:
        openFile = open('notExistsFile.txt','r')
        fileContent = openFile.readlines()
    except (IOError,ValueError),info:
        print dir(info)
        print info.args
except:
    print 'process exception'
else:
    print 'Reading the file'

output:

In [44]: %run test.py
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__getslice__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__unicode__', 'args', 'errno', 'filename', 'message', 'strerror']
(2, 'No such file or directory')
Reading the file

对于UI线程的未处理异常,我们可以通过监控下面这个事件来处理

     首先为当前AppDomain 添加  UnhandledExceptionEventHandler

<a name=”t14″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>traceback追踪异常

使用traceback追踪异常的时候,需要import
traceback模块。traceback模块可以有效的帮助查看异常的详细信息。

注意:若希望获取异常的详细信息,却又不会终止程序的执行,可以在except子句中使用tarceback.print_exc()函数。

tarceback.print_exc()

print_exc(limit=None, file=None)

Shorthand for ‘print_exception(sys.exc_type, sys.exc_value,
sys.exc_traceback, limit, file)’.

(In fact, it uses sys.exc_info() to retrieve the same information in a
thread-safe way.)

输出sys.exc_type, sys.exc_value, sys.exc_traceback, limit,
file等异常信息,实际上是以线程安全的方式去使用sys.exc_info()函数来获取相同的信息。

#!/usr/bin/env python
import traceback

try:
    openFile = open('notExistsFile.txt','r')
    fileContent = openFile.readlines()
except IOError as info:
    print 'File not Exists'
    print info
    traceback.print_exc()
    print 'continue'
except:
    print 'process exception'
else:
    print 'Reading the file'

output:

In [38]: %run test.py
File not Exists
[Errno 2] No such file or directory: 'notExistsFile.txt'
Traceback (most recent call last):
  File "/usr/local/src/pyScript/test.py", line 5, in <module>
    openFile = open('notExistsFile.txt','r')
IOError: [Errno 2] No such file or directory: 'notExistsFile.txt'
continue

异常信息的重定向:如果希望将异常的信息保存在一个指定的文件中,以供后期分析。可以使用下面的方法:

#!/usr/bin/env python
import traceback

try:
    with open('notExistsFile.txt','r') as openFile:
        fileContent = openFile.readlines()
except IOError:
    with open('errorLog','w+') as errorInfo:
        traceback.print_exc(file=errorInfo)
    print 'continue'
except:
    print 'process exception'
else:
    print 'Reading the file'

output:

In [61]: %run test.py
continue

In [62]: cat errorLog
Traceback (most recent call last):
  File "/usr/local/src/pyScript/test.py", line 5, in <module>
    with open('notExistsFile.txt','r') as openFile:
IOError: [Errno 2] No such file or directory: 'notExistsFile.txt'

Application.Current.DispatcherUnhandledException  

图片 8AppDomain.CurrentDomain.UnhandledException += 

<a name=”t15″ target=”_blank” style=”text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);”></a>sys.exc_info()获取异常信息

traceback.print_exc()函数实际上是call sys.exc_info()

import sys  
try:  
    a=b  
    b=c  
except:  
    info=sys.exc_info()  
    print info[0],":",info[1]  

output:

In [65]: %run test.py
<type 'exceptions.NameError'> : name 'b' is not defined

一个参考代码如下:

             new UnhandledExceptionEventHandler(UnhandledExceptionEventHandler);

using System;
using System.Windows;

namespace WpfApplicationExceptionSample
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        public App()
        {
            Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
        }


        void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            MessageBox.Show("我们很抱歉,当前应用程序遇到一些问题,该操作已经终止,请进行重试,如果问题继续存在,请联系管理员.", "意外的操作", MessageBoxButton.OK, MessageBoxImage.Information);//这里通常需要给用户一些较为友好的提示,并且后续可能的操作

            e.Handled = true;//使用这一行代码告诉运行时,该异常被处理了,不再作为UnhandledException抛出了。
        }
    }
}

 

运行的效果大致如下

     再添加事件响应函数

图片 9

图片 10        static void UnhandledExceptionEventHandler(object sender, UnhandledExceptionEventArgs e)
图片 11图片 12        图片 13{
图片 14            try
图片 15图片 16            图片 17{

 

图片 18                using (System.IO.FileStream fs = new System.IO.FileStream(@”c:\testme.log”, 

对于非UI线程抛出的未处理异常,我们需要监控另外一个事件来处理

                        
System.IO.FileMode.Append, System.IO.FileAccess.Write))

AppDomain.CurrentDomain.UnhandledException 
 

图片 19图片 20                图片 21{

一个参考代码如下

图片 22                    using (System.IO.StreamWriter w = new System.IO.StreamWriter(fs, 

using System;
using System.Windows;

namespace WpfApplicationExceptionSample
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        public App()
        {
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
        }

        void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            MessageBox.Show("我们很抱歉,当前应用程序遇到一些问题,该操作已经终止,请进行重试,如果问题继续存在,请联系管理员.", "意外的操作", MessageBoxButton.OK, MessageBoxImage.Information);
        }


    }
}

                            
System.Text.Encoding.UTF8))

 

图片 23图片 24                    图片 25{
图片 26                        w.WriteLine(e.ExceptionObject);
图片 27                    }
图片 28                }
图片 29            }
图片 30            catch
图片 31图片 32            图片 33{
图片 34            }
图片 35        }

令人不解的是,这个事件中没有和前面那个事件一样的e.Handled参数,就是说,虽然这样是可以捕捉到非UI线程的异常,而且也可以进行相应的处理,但是应用程序还是会退出,也就是说这个异常还是被当作是未处理异常继续汇报给Runtime。

 

为了改进这一点,我们可以通过修改配置文件来实现。

现在我们就可以截获未捕获异常了

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <legacyUnhandledExceptionPolicy enabled="1"/>
  </runtime>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>

下面是完整的测试代码:

 

图片 36        public class TestRaiseException
图片 37图片 38        图片 39{
图片 40            ~TestRaiseException()
图片 41图片 42            图片 43{
图片 44                int i = 0;
图片 45                int j = 1 / i;
图片 46            }
图片 47        }
图片 48
图片 49        static void UnhandledExceptionEventHandler(object sender, UnhandledExceptionEventArgs e)
图片 50图片 51        图片 52{
图片 53            try
图片 54图片 55            图片 56{

这里的legacyUnhandledExceptionPolicy,如果enabled=1的话,用意是使用早期版本的异常处理策略。

图片 57                using (System.IO.FileStream fs = new System.IO.FileStream(@”c:\testme.log”,

                      System.IO.FileMode.Append, System.IO.FileAccess.Write))

 图片 58图片 59                图片 60{

图片 61                    using (System.IO.StreamWriter w = new System.IO.StreamWriter(fs, 

                         System.Text.Encoding.UTF8))

 图片 62图片 63                    图片 64{

图片 65                        w.WriteLine(e.ExceptionObject);
图片 66                    }
图片 67                }
图片 68            }
图片 69            catch
图片 70图片 71            图片 72{
图片 73            }
图片 74        }
图片 75
图片 76        static void Main(string[] args)
图片 77图片 78        图片 79{

图片 80            AppDomain.CurrentDomain.UnhandledException += 

                    new UnhandledExceptionEventHandler(UnhandledExceptionEventHandler);

 

 图片 81

图片 82            TestRaiseException testRaiseException = new TestRaiseException();
图片 83
图片 84        }

 

 程序运行后记录下日志如下

System.DivideByZeroException: Attempted to divide by zero.
   at TestCosole.Program.TestRaiseException.Finalize()

 

  • WinForm

    WinForm 应用通过 Application.ThreadException
事件来截获未捕获异常

  详见 园子里面另一篇博客,这里就不再冗诉。

    
体面地处理程序的未捕获异常

  • Asp.net

     ASP.NET 应用和前两种应用有所不同,ASP.NET
一般在后台线程或者线程池中产生未捕获异常,才会导致W3WP.exe终止,并在事件查看器中产生一条类似下面内容的事件:EventType
clr20r3, P1 w3wp.exe, P2 6.0.3790.1830, P3 42435be1, P4
app_web_ncsnb2-n, P5 0.0.0.0, P6 440a4082, P7 5, P8 1, P9
system.nullreferenceexception, P10 NIL. 

     要截获ASP.NET 的未捕获异常,我们需要为每个应用程序域安装事件钩子

     这个过程需要分两步完成:

     首先创建一个实现IHttpModule接口的类 

图片 85using System;
图片 86using System.Data;
图片 87using System.Configuration;
图片 88using System.Web;
图片 89using System.Web.Security;
图片 90using System.Web.UI;
图片 91using System.Web.UI.WebControls;
图片 92using System.Web.UI.WebControls.WebParts;
图片 93using System.Web.UI.HtmlControls;
图片 94
图片 95namespace WebMonitor
图片 96图片 97图片 98{
图片 99图片 100    /**//// <summary>
图片 101    /// Summary description for UnhandledExceptionModule
图片 102    /// </summary>
图片 103    public class UnhandledExceptionModule : IHttpModule
图片 104图片 105    图片 106{
图片 107        static object _initLock = new object();
图片 108        static bool _initialized = false;
图片 109
图片 110        public UnhandledExceptionModule()
图片 111图片 112        图片 113{
图片 114            //
图片 115            // TODO: Add constructor logic here
图片 116            //
图片 117        }
图片 118
图片 119
图片 120        void OnUnhandledException(object o, UnhandledExceptionEventArgs e)
图片 121图片 122        图片 123{
图片 124            //Do some thing you wish to do when the Unhandled Exception raised.
图片 125
图片 126            try
图片 127图片 128            图片 129{

图片 130                using (System.IO.FileStream fs = new System.IO.FileStream(@”c:\testme.log”, 

                       
System.IO.FileMode.Append, System.IO.FileAccess.Write))

 图片 131图片 132                图片 133{

图片 134                    using (System.IO.StreamWriter w = new System.IO.StreamWriter(fs, System.

                            Text.Encoding.UTF8))

 图片 135图片 136                    图片 137{

图片 138                        w.WriteLine(e.ExceptionObject);
图片 139                    }
图片 140                }
图片 141            }
图片 142            catch
图片 143图片 144            图片 145{
图片 146            }
图片 147        }
图片 148图片 149        IHttpModule Members#region IHttpModule Members
图片 150
图片 151        public void Dispose()
图片 152图片 153        图片 154{
图片 155            throw new Exception(“The method or operation is not implemented.”);
图片 156        }
图片 157
图片 158        public void Init(HttpApplication context)
图片 159图片 160        图片 161{
图片 162            // Do this one time for each AppDomain.
图片 163            lock (_initLock)
图片 164图片 165            图片 166{
图片 167                if (!_initialized)
图片 168图片 169                图片 170{
图片 171                    AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);
图片 172                    _initialized = true;
图片 173                }
图片 174            }
图片 175        }
图片 176
图片 177        #endregion
图片 178    }
图片 179}
图片 180

 

 第二步:

 修改web.config

在 system.web 段中加入

图片 181    <httpModules>
图片 182      <add name=”UnhandledExceptionModule” type=”WebMonitor.UnhandledExceptionModule” />
图片 183      
图片 184    </httpModules>

 

 完成这两步后,你的ASP.NET 应用就可以截获未捕获异常了。

 下面是测试代码

 

图片 185using System;
图片 186using System.Data;
图片 187using System.Configuration;
图片 188using System.Web;
图片 189using System.Web.Security;
图片 190using System.Web.UI;
图片 191using System.Web.UI.WebControls;
图片 192using System.Web.UI.WebControls.WebParts;
图片 193using System.Web.UI.HtmlControls;
图片 194
图片 195public partial class _Default : System.Web.UI.Page 
图片 196{
图片 197    protected void Page_Load(object sender, EventArgs e)
图片 198    {
图片 199
图片 200    }
图片 201
图片 202    protected void TestMe(object state)
图片 203    {
图片 204        byte[] buf = new byte[2];
图片 205        buf[2] = 0;
图片 206    }
图片 207
图片 208    protected void Button1_Click(object sender, EventArgs e)
图片 209    {

图片 210        System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(TestMe), 

                  null);

 

 图片 211    }

图片 212}
图片 213

 按下Button1后,w3wp.exe被终止,testme.log 中记录下了异常信息如下:

System.IndexOutOfRangeException: Index was outside the bounds of the
array.

   at _Default.TestMe(Object state) in
c:”ApolloWorkFolder”test”laboratory

“TestWebSite”Default.aspx.cs:line 21

   at
System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object
state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)

   at
System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(

TryCode code, CleanupCode backoutCode, Object userData)

   at System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext,

ContextCallback callback, Object state)

   at System.Threading.ExecutionContext.Run(ExecutionContext
executionContext,

ContextCallback callback, Object state)

   at
System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(

_ThreadPoolWaitCallback tpWaitCallBack)

   at
System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object
state)

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图