.NET 4.0里异常处理的新机制

19 篇文章 3 订阅

前几天,有一个朋友问我为什么在.NET里不能捕捉(catch)到一些异常了,而且在调试器里也捕捉不到。研究了一下,是.NET 4.0里新的异常处理机制捣的鬼。

在.NET 4.0之后,CLR 将会区别出一些异常(都是SEH异常),将这些异常标识为破坏性异常(Corrupted State Exception)。针对这些异常,CLR 的 catch 块不会捕捉这些异常,即使你用类似下面的代码:

            try
            {
                TestMethod();
            }
            catch (Exception e)
            {
                Console.WriteLine("Catching exception: {0}", e);
            }

也没有办法捕捉到这些异常。之所以要这样设计,在 MSDN 的文章 Handling Corrupted State Exceptions里已经提到了。即,有一些支持插件的程序,例如 Visual Studio 或者SQL Server,它们支持调用托管代码编写成的插件,但是它们自己本身有很多代码是由非托管的C++写成的。由于插件经常会调用到非托管的API,而很多时间,这些插件的代码根本就不知道如何处理非托管的API抛出来的 SEH 异常。在4.0以前,因为SEH异常被转换成了跟普通.NET异常相同的异常,这样程序员只要用 catch (Exception e) 的模式就可以捕捉到所有的异常。这样处理的问题是,由于SEH异常通常都不是托管代码抛出的,托管代码根本就不知道SEH异常被扔出来的原因,简单的 catch (Exception e) 处理使得整个程序会处于一个非常不稳定的状态,使得前面被忽略的问题在后面以更严重的方式出现—— 例如保存被破坏的数据。这样,看起来使用catch (Exception e) 处理所有的异常的方法很简单,但实际上让程序员或者用户在问题延后发生时,分析起来需要花费更多的精力。

因此在4.0以后,大部分SEH(我怀疑是所有)异常都被标识成破坏性异常,在.NET里,默认情况下CLR不会捕捉它们,而是任由操作系统来处理——即关闭程序,并打开一个错误对话框通知用户。为了保证兼容性,在4.0以前编译的程序,例如在2.0、3.0和3.5编译的程序,依然采用的是老的策略—即.NET会同时捕捉.NET异常和SEH异常。而在4.0下面编译的程序才会使用新的策略,这也是在文章的开头,我的朋友所碰到的问题。你可以在.NET 4.0下面编译下面的程序,体验一下这个新变化:
Program.cs:

using System;
using System.Runtime.InteropServices;
  
namespace ConsoleApplication1
{
     class Program
     {
         [DllImport("Ref.dll")]
         private extern static void TestMethod();
  
         static void Main(string[] args)
         {
             try
             {
                 TestMethod();
             }
             catch (Exception e)
             {
                 Console.WriteLine("Catching exception: {0}", e);
             }
         }
     }
}

Ref.cpp:

#include "stdafx.h"
  
 extern "C" __declspec(dllexport) void TestMethod()
 {
     int *p = NULL;
     // 会导致.NET抛出一个AccessViolation异常
     *p = 10;
 }

上面的代码里,Program.cs 使用 P/Invoke 技术调用了 Ref.dll 文件里的 TestMethod,但是 TestMethod 尝试给一个空指针赋值,导致一个 AccessViolation 异常。如果你在2.0下面编译 program.cs,并执行的话,这个 AccessViolation 异常会被 catch(Exception e) 捕捉到,而如果你在4.0下面编译并执行的话,你会发现catch (Exception e) 是不能捕捉到这个异常的。

然而并不是所有人都想要这个新的异常机制,如果你的程序是在4.0下面编译并运行,而你又想在 .NET 程序里捕捉到 SEH 异常的话,有两个方案可以尝试:

(1)在托管程序的 .config 文件里,启用 legacyCorruptedStateExceptionsPolicy 这个属性,即简化的 .config 文件类似下面的文件:
App.config:

<?xml version="1.0"?>
<configuration>
 <startup>
   <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
 </startup>
    <runtime>
      <legacyCorruptedStateExceptionsPolicy enabled="true" />
    </runtime>
</configuration>

这个设置告诉CLR 4.0,整个.NET 程序都要使用老的异常捕捉机制。

(2)在需要捕捉破坏性异常的函数外面加一个 HandleProcessCorruptedStateExceptions 属性,这个属性只控制一个函数,对托管程序的其他函数没有影响,例如:

[HandleProcessCorruptedStateExceptions]
 static void Main(string[] args)
 {
     try
     {
         TestMethod();
     }
     catch (Exception e)
     {
         Console.WriteLine("Catching exception: {0}", e);
     }
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值