所有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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容