CLR内部异常(下)
直接使用SEH
有些情况里直接使用SEH会更合适一些。特别是,如果需要在第一次遍历(first pass - SEH异常处理流程里的第一遍处理)时需要执行某些操作时,也就是在堆栈向上展开之前,SEH是唯一的选项。一个SEH里的 __try/__except 过滤代码除了决定是否要处理某个异常以外,还能执行任何操作。调试器通知(Debugger notification)就是在第一次遍历时需要考虑的领域。
过滤代码的编写需要极其小心。一般来讲,过滤代码需要考虑到任何随机,而且很可能不一致的状态。因为过滤代码在第一次遍历时执行,而析构函数(dtors)在第二次遍历时执行,holders还没有执行,而且也没有恢复它们的状态。
PAL_TRY / PAL_EXCEPT, PAL_EXCEPT_FILTER, PAL_FINALLY / PAL_ENDTRY
如果需要过滤代码,PAL_TRY家族代码是CLR里可移植的写法。因为过滤代码直接使用SEH,它不能在一个函数里与C++异常处理共存,因此在函数里不能使用holders。
再次强调,这种情况很少见。
__try / __except, __finally
在CLR里没有充足理由来直接使用它们。
异常和GC模式
使用COMPlusThrowXXX()抛出异常不会影响GC模式,而且在所有模式里都是安全的。当异常将堆栈展开到EX_CATCH,在栈上的所有holders都会销毁,释放它们的资源和重置它们的状态。当在EX_CATCH里恢复执行时,被holder保护的状态会恢复到EX_TRY时的状态。
转换
在托管代码,CLR,COM服务器和其它原生代码之间,有很多如调用规范(calling conventions),内存管理和异常处理机制之间的转换。关于异常,对CLR开发者来说是幸运的,大部分转换要么完全在CLR之外,或者就是被自动处理了。CLR开发人员日常需要考虑的就是三种转换。其它的都是高端话题。
托管代码进入CLR
由“fcall”、"jit helper"等触发。典型路径是CLR通过一个托管异常向托管代码报告错误。因此,如果是一个fcall函数直接或间接抛出一个托管异常,那没什么问题。一般的CLR托管异常实现会“做正确的事”而且会去找一个适合的托管异常处理代码。
从另一面来说,如果fcall函数也可以包含抛出CLR内部异常的代码(一个C++异常),这个异常不能泄漏到托管代码那边。为了处理这种情况,CLR提供了UnwindAndContinueHandler (UACH),它是捕捉C++异常并转换成托管异常并抛出的一系列代码。
任何从托管代码端调用的CLR函数都有可能抛出C++异常,必须将抛出异常的代码封装在INSTALL_UNWIND_AND_CONTINUE_HANDLER / UNINSTALL_UNWIND_AND_CONTINUE_HANDLER里。安装一个HELPER_METHOD_FRAME会自动安装UACH。安装UACH的性能代价很大,因此不能随处使用。一种技术是,在执行关键代码的时候不要使用UACH,但是在抛出异常之前安装一个UACH。
当一个C++异常被抛出时,而少了一个UACH,典型错误就是一个在CPFH_RealFirstPassHandler里“GC_TRIGGERS在一个GC_NOTRIGGER区域里被调用”的合约违规(Contract Violation)。要修复这些错误,留意托管代码到CLR的切换,并检查INSTALL_UNWIND_AND_CONTINUE_HANDLER或HELPER_METHOD_FRAME_BEGIN_XXX。
CLR代码到托管代码
从CLR切换到托管代码是跟平台很相关的。在32位Windows平台,CLR托管异常代码要求在进入托管代码之前已经安装了“COMPlusFrameHandler”。这些切换被一些专用的辅助函数处理,它们来执行恰当的异常处理。一般对托管代码的调用很少用其它方法。如果没有COMPlusFrameHander,最可能的情况就是托管代码端的异常处理代码没有被执行,finally块和catch块都不会被执行。
CLR代码切换到外部原生代码
从CLR调用其它原生代码(操作系统、CRT和其它DLL)的过程要格外注意。这是因为外部代码可能会触发一个异常。由于EX_TRY宏的实现方式,这是一个问题,特别是它们将一个非异常错误翻译或者封装异常的方式。对于C++异常,只能通过放弃捕捉到的异常的所有信息,是可以捕捉任意或者所有异常(通过"catch(...)")。捕捉到一个异常后*,这个宏有异常对象可检查,但如果捕捉到其它东西,那就没什么可检查的了,宏只能猜异常是什么。如果异常是从CLR外部来的,那宏总是会猜错。
目前的解决方案是将对外部代码的调用封装到一个“callout filter”。这个过滤代码会捕捉外部异常,并将它翻译成一个CLR内部异常:SEHException。这个过滤代码是预定义的,而且用起来也很简单。然而,使用过滤代码就意味着使用SEH,因此在同一个函数里不能使用C++异常。在使用C++异常的函数里加上“callout filter”要求将它分成两个函数。
要使用callout filter,这样的代码:
length = SysStringLen(pBSTR);
要写成:
BOOL OneShot = TRUE; PAL_TRY
{
length = SysStringLen(pBSTR);
}
PAL_EXCEPT_FILTER(CallOutFilter, &OneShot)
{
_ASSERTE(!"CallOutFilter returned EXECUTE_HANDLER.");
}
PAL_ENDTRY;
如果函数抛出了一个异常,而没有callout filter的话,后果就是CLR会报告一个错误的异常。而且错误报告的异常类型也不是确定的;如果系统里已经有托管异常,那么就会报告成这个托管异常。如果没有异常抛出,那么就会报告成一个内存不足异常(OOM)。在调试版里,如果缺失callout filter,会触发一个断言。断言消息会包含“The runtime may have lost track of the type of an exception(运行时可能无法确定异常的类型)”。
其它
在EX_TRY里实际上包含很多宏,这些宏永远不能在宏的实现以外使用。
BEGIN_EXCEPTION_GLUE / END_EXCEPTION_GLUE需要特别提一下。它们本来是用做切换宏的,在VS 2008里应该被换成更恰当的宏。当然,它们现在工作的很好,因此也不用被换掉。理想情况下,是在一个“清理”里程碑里,将这些实例都换掉,然后删掉这些宏。CLR开发人员如果要用它们的话,应该使用EX_TRY/EX_CATCH/EX_CATCH_END或 EX_CATCH_HRESULT。
作者:懿民
链接:https://www.jianshu.com/p/efb65766f9fe
来源:简书
CLR内部异常(下)的更多相关文章
- CLR内部异常(上)
当我们提到CLR里的“异常”,要注意一个很重要的区别.有通过如C#的try/catch/finally暴露给应用程序,并由运行时提供机制全权实现的托管异常.也有运行时自己使用的异常.大部分运行时开发人 ...
- CLR内部异常(中)
不捕捉某一个异常 常常有这种情况,代码不需要捕捉异常,但需要执行一些清理或者修正操作.虽然不总是,支持物(holders)经常用在这种场景里.在支持物(holders)不适用的情况里,CLR提供了两个 ...
- 【性能诊断】六、并发场景的性能分析(windbg案例,大量的内部异常造成CPU飙升)
在做产品的某个核心模块的性能优化时,发现压到100并发时应用服务器的CPU就飙升至90%以上,50并发以后TPS就基本定格在一个数值上.使用性能监视器收集应用服务器的数据,发现每秒的.NET CLR ...
- Dynamics AX 2012 R2 AIF 内部异常
今天,Reinhard发现某个入站端口,突然一直报错: The server was unable to process the request due to an internal erro ...
- EF 更新条目时出错。有关详细信息,请参见内部异常。
现象:使用EF新增记录时,一直报上述异常,网上说是值为空.主键外键未设等原因导致,但是改正这些情况下问题依然 解决过程:异常中有一句(请参见内部异常),一直都没有当回事,后来实在没办法就静下心来看了看 ...
- ORACL内部异常:
ORACL内部异常: ORA-00001: 违反唯一约束条件 (.) ORA-00017: 请求会话以设置跟踪事件 ORA-00018: 超出最大会话数 ORA-00019: 超出最大会话许可数 OR ...
- TP5使用API时不可预知的内部异常
最常见的错误形式例如 controller不存在或者 action不存在之类的 我们第一时间想到的 就是 使用 try{}catch(){} 来捕获 例如: /** * show方法在common里定 ...
- TP5内部异常API数据输出的自定义方法编写
需求:利用postman进行请求api接口过程中 关于一些数据输出异常的情况下 我们希望通过自己编写一些类和方法 实现便于后端人员进行根据提示进行调试处理! 以下测试的时候 请设置 app_debug ...
- 聊聊“装箱”在CLR内部的实现
原文连接:https://mattwarren.org/2017/08/02/A-look-at-the-internals-of-boxing-in-the-CLR/ 作者 Matt Warren. ...
随机推荐
- k8s 运行应用
一.deployment 创建过程 kubect创建deployment —> deployment 创建ReplicaSet—>根据ReplicaSet 创建Pod 命名方式 relic ...
- RabbitMQ Policy的使用
RabbitMQ作为最流行的MQ中间件之一,广泛使用在各类系统中,今天我们就来讨论一下如何通过Policies给RabbitMQ中已经创建的Queue添加属性和参数. Policise 的作用 通常来 ...
- nrm的安装和使用
1.安装nodejs,下载地址,http://nodejs.cn/download/,安装过程直接点击下一步即可 安装完成后cmd输入npm -v 查看当前安装的npm的版本,如下图提示所示则表示安装 ...
- [转]Sublime Text 3安装Json格式化插件
1.安装Package control 首先需要安装Package control,如果已经安装请跳过此步骤.按Ctrl+Shift+p打开命令搜索框,输入PC,点击图中条目安装,如下图: 成功后 ...
- IntelliJ IDEA web项目进行数据库连接时出现java.lang.ClassNotFoundException: com.mysql.jdbc.Driver错误解决办法
首先看报错信息: 意思是找不到类: com.mysql.jdbc.Driver.也就是说tomcat找不到MySQL数据库连接要用的jar包! 出现这种错误的原因是: 项目中没有导入这个jar包, ...
- Java自学-类和对象 构造方法
怎么使用 Java 构造方法? 通过一个类创建一个对象,这个过程叫做实例化 实例化是通过调用构造方法(又叫做构造器)实现的 步骤 1 : 什么是构造方法 方法名和类名一样(包括大小写) 没有返回类型 ...
- CSS疑难杂症
1.text-align: center + letter-spacing: 2em 字体不居中 办法:添加text-indent: 2em 2.first-child伪类选择不到元素 办法:确保备选 ...
- js文本对象模型[DOM]【续】(Node节点类型)
一.Document类型 document实例1.常用的一些属性documentElement 始终指向HTML页面中的<html>元素.body 直接指向<body>元 ...
- JavaScript之控制标签css
控制标签css标签.style.样式='样式具体的值'如果样式出现中横线,如border-radius,将中横线去掉,中横线后面的单词首字母大写,写成borderRadius如果原来就要该样式,表示修 ...
- Java 之 cookie 记住用户登录时间案例
需求: 1. 访问一个Servlet,如果是第一次访问,则提示:您好,欢迎您首次访问. 2. 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:显示时间字符串 分析: 1. 可以采用Cookie ...