Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 324|回复: 0

送专利啦~~ .Net高阶异常处理之TopLevelEH

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-5-19 23:32:33 | 显示全部楼层 |阅读模式

      我们知道,.Net的应用程序运行在.net framework虚拟机上,对于在运行时发生的错误,我们有try...catch可以捕捉,实在不济,对于winform和asp.net 我们都有全局的事件可以订阅处理。具体请参见:http://www.cnblogs.com/eaglet/archive/2009/02/17/1392191.html ,但是如果不通过.net Framework,而是直接通过P/Invoke调用操作系统的C/C++写的库,而不是通过.Net Framework的BCL调用操作系统功能,那么上面链接所说的方法便失去作用。譬如我有一个方法:

     [DllImport("kernel32")] private static extern void RtlMoveMemory(IntPtr dst, ref byte src, Int32 len);

    然后我们拖个按钮,写个事件:

     private void button1_Click(object sender, EventArgs e) { byte a = 0; RtlMoveMemory(IntPtr.Zero, ref a, 1); }

    程序会直接崩溃,用上述的方法无论如何都捕捉不到的。不信您可以试试。

    那怎么办?解铃还须系铃人。你直接调用操作系统API,难道操作系统没有提供异常捕获机制么?有的。现在我们引入几个术语。

    术语:
    SEH: 结构化异常处理
    VEH: 向量化异常处理
    TopLevelEH:顶层异常处理

    相信写过C或者C++程序的同学可能不会陌生。现在我们介绍当用P/Invoke调用出现异常时,操作系统的异常处理顺序流程:

    1. 交给调试器(前提是进程必须被调试)
    2. 执行VEH
    3. 执行SEH
    4. TopLevelEH(进程被调试时不会被执行)
    5. 交给调试器(上面的异常处理都说处理不了,就再次交给调试器)
    6. 调用异常端口通知csrss.exe

    下面咱就详细讨论一下各个步骤都干了哪些细活:

    1. 第一次交给调试器
     
     如果该出现异常的程序正在被调试,则该异常首先交给调试器处理(通过DebugPort)。调试器拿到这个异常后,需要判断是否要处理该异常.

    2. 执行VEH
     
    这里就不讲Veh的概念了,有兴趣的去Google一下。
     如果没有被调试,或者调试器返回DBG_EXCEPTION_NOT_HANDLED,则就会检查是否存在VEH。如果存在VEH,则把异常交给他们处理。
     VEH是个链表,可以存在多个Veh。每个VEH按顺序被调用。
     一个VEH可以返回连个值:EXCEPTION_CONTINUE_SEARCH、EXCEPTION_CONTINUE_EXECUTION。返回                          EXCEPTION_EXECUTE_HANDLER是无效的,等同于EXCEPTION_CONTINUE_SEARCH。                       
     当一个Veh返回EXCEPTION_CONTINUE_SEARCH,则把异常交给下一个VEH处理。
     如果返回EXCEPTION_CONTINUE_EXECUTION,认为已经被处理,退出异常处理器在异常指令处继续执行。
     从执行顺序来看,VEH是在SEH之前执行的,并且不依赖某一线程,本进程中任何线程出现异常都可以被VEH处理,所以在有些时候是很有用处的。

    那么怎么添加一个VEH呢?这里不是我们介绍的重点,请参考:http://msdn.microsoft.com/en-us/library/ms681420%28VS.85%29.aspx

    3. 执行SEH
     
    SEH是基于线程栈的异常处理机制。当所有的VEH都不处理该异常,该异常就会让SEH处理,所以它只能处理自己线程的异常

    4. TopLevelEH

      顶层异常处理,这个就是我们今天介绍的重点了,这个其实是利用SEH实现的。在最顶层的SEH中,可以注册一个顶层异常处理器。虽然他是基于SEH实现的,但是它可以处理所有线程抛出的异常
    当SEH都处理不了该异常,在最顶层的SEH中就会检查是否注册了顶层异常处理,如果注册了,则执行顶层异常处理。
    注意:如果该进程正在调试状态,顶层异常处理会被忽略,不会被执行
    顶层异常处理函数也可以返回三个值:EXCEPTION_CONTINUE_SEARCH、EXCEPTION_EXECUTE_HANDLER、EXCEPTION_CONTINUE_EXECUTION,先记着,等会有用  

      这个到底有什么用呢?应用程序难免会出现崩溃的,一旦崩溃了怎么办?想想QQ,每次崩溃都会弹出友好提示,它是怎么做到的呢?对了,就是这个TopLevelEH。

    关于这个,某java开发的同学其实已经申请了专利:

    公开号 CN101794243 A 发布类型 申请 专利申请号 CN 201010126856 公开日 2010年8月4日 申请日期 2010年3月18日 优先权日 2010年3月18日 摘要: 一种利用操作系统结构化异常处理加固java应用程序的方法 CN 101794243 A 本发明提供一种利用操作系统结构化异常处理加固java应用程序的方法,Java提供了在java程序中调用本地程序的方法,这些本地程序通常以动态库的形式存在,一旦动态库中出现没有捕获的错误,就会导致整个java虚拟机崩溃,并且没有任何补救方法。通过利用操作系统的结构化异常处理机制,能保证java虚拟机在崩溃以后按照需要执行相应的补救措施,比如发出友好化的通知,重新启动java虚拟机等。

     

    看看,人家java的Jni(就是java中调用本地链接库的方法,对应于.net的P/Invoke)也同样遇到了这个问题,然后。。然后就专利了还,好吧这个专利太简单,我送给.net的同学们好了。

    该专利被华为引用:

    公开号 WO2013071766 A1 发布类型 申请 专利申请号 PCT/CN2012/078524 公开日 2013年5月23日 申请日期 2012年7月12日 优先权日 2011年11月14日 公告号 CN103107905A, EP2712119A1, US20140115587 申请人 Huawei Technologies Co., Ltd., 华为技术有限公司 摘要: 本发明公开了一种异常处理方法、装置和客户端,属于在线应用领域。该方法包括:虚拟管理服务器接收与第一客户端交互的虚拟机发送的异常通知,所述异常通知至少携带用户标识和应用标识;所述虚拟管理服务器根据保存的与所述用户标识及应用标识对应的异常处理方式,保存所述虚拟机数据或与所述应用标识对应的应用的应用数据,并释放所述虚拟机资源。该装置包括:接收模块和异常处理模块。本发明使得不同应用、不同用户可以根据其需求定制不同的异常处理方法,也使得客户端不但可保存异常发生时的时刻或最接近异常发生时的时刻用户的使用状态,而且提高了在线应用系统的容量和效率。

     说白了就是捕捉到异常之后发消息而已啦,其实所谓的专利就是这么简单的东西。

     关于5和6我们就略过好了,讲讲大家都关心的怎么来实现吧。

    首先定义一个委托:

     public delegate Int32 CallBack(ref long a); CallBack mycall;

     注册一个顶层异常处理器的方法如下:

    [DllImport("kernel32")] private static extern Int32 SetUnhandledExceptionFilter(CallBack cb);

    在系统初始化时,调用:

    mycall = new CallBack(MyExceptionfilter); SetUnhandledExceptionFilter(mycall);
    MyExceptionfilter签名如下:
    Int32 MyExceptionfilter(ref long a)

    它返回一个Int值,对应上面的TopLevelEH:

    EXCEPTION_EXECUTE_HANDLER == 1 表示我已经处理了异常,可以优雅地结束了
    EXCEPTION_CONTINUE_SEARCH == 0 表示我不处理,其他人来吧,于是windows调用默认的处理程序显示一个错误框,并结束
    EXCEPTION_CONTINUE_EXECUTION e== -1 表示错误已经被修复,请从异常发生处继续执行。

    在实际方法中,你可以记录下程序的dump文件(Dump文件有什么用?回头我再写一篇好了),发送消息,然后优雅的结束或者重启,做出极好的客户体验,是不是很棒?!接下来我们处理开头的异常,那其实很简单了,必定会被捕获到,不信您可以试试。

    代码实在很简单,我就不上传了,有心的同学一试就出来了。

    觉得写得好,您就点个赞~

    参考链接:http://bbs.pediy.com/showthread.php?t=173853

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-4 04:41 , Processed in 0.070912 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表