窥探try ... catch与__try ... __except的区别
VC中的这两个东西肯定谁都用过, 不过它们之间有什么区别, 正好有时间研究了一下, 如果有错误欢迎拍砖.
基于VC2005, 32位XP 平台测试通过. 估计对于其他版本的VC和操作系统是不通用的.
1. try ... catch
这个是C++语言定义的, 每个C++都有对其的不同的实现. 使用也很简单. 比如我们有一个函数, 读入年龄. 如果<=0 或者 >=100, 抛出异常:
int readAge() {
int age = 读入年龄;
if (age <=0 || age >= 100) {
throw AgeException(age);
}
return age;
}
其中 AgeException 的定义为
class AgeException {
public:
int errorAge;
AgeException(int age) {
errorAge = age;
}
};
在使用的时候也比较简单,
try {
int i = readAge();
printf("Age inputed is %d", i);
} catch (AgeException e) {
printf("error. Age inputed = %d and is not valid.", e.errorAge);
}
2. __try ... __except
这个是VC自己定义的不是C++的关键字. VC在编译__try ... __except的时候, 会按照Windows SEH(结构化异常)处理的规则, 把异常处理部分加入到当前线程的异常处理链中. 这部分不详细写了, SEH处理在网上的文章一搜一大把.
3. try...catch 与 __try ... __except 使用上的区别
对于上面的AgeException, 我们也可以使用__try... __except 来处理:
__try {
int i = readAge();
printf("Age inputed is %d", i);
} __except (EXCEPTION_EXECUTE_HANDLER) {
printf("Age is not correct.");
}
但是, 对于__try ... __except 能够处理的异常(比如下面的代码), C++异常处理try .. catch 不能够捕获(Catch段不能执行):
try {
int *p = NULL;
*p = 0;
} catch (...) {
printf("Exception occured.");
}
注: 这里其实和编译器有关, VC2005由/EH加上参数来控制, 详情参见http://msdn.microsoft.com/en-us/library/1deeycx5(VS.80).aspx . 这里讨论的是默认的情况, 不处理的时候.
这是为什么呢. 仔细看了下, 当我们在程序里面throw出来一个异常的时候, 调试器(比如VC, WinDBG)会记录下面一个事件:
First-chance exception at 0x7c812afb (kernel32.dll) in trycatch.exe: Microsoft C++ exception: AgeException at memory location 0x0012fc98..
也就是说, 在VC中, throw出来的都是Microsoft C++ exception. 只有这种Exception才能被try...catch捕获. 同样, 用WinDBG装载上面的程序
__try {
int i = readAge();
printf("Age inputed is %d", i);
} __except (EXCEPTION_EXECUTE_HANDLER) {
printf("Age is not correct.");
}
会发现, 出现的异常为C++ exception, 异常代码为0xe06d7363:
也就是说, 在C++中throw出来的异常是一种特殊的类型的异常, 是微软专门为VC++实现的,异常代码为0xe06d7363. (有意思的是ASCII码为0x6d, 0x73, 0x63的字符为msc)
到这里我们基本可以得出一个结论, try...catch和__try...__except其实从本质上来说是一回事, 他们从根源上来说都是用到了Windows的SEH处理机制. 不同点在于:
-) try...catch 只处理异常代码为0xe06d7363的C++ exception, 不会理会其他的;
-) try...catch 对于编译器来说做了一些额外的工作, 但是最终的实现是和__try...__except都要归结于SEH
-) try...catch 多了一些额外的传递具体的异常信息的部分(catch的是何种异常. 不像是__try...__except, 需要用ExceptionCode去判断)
想到这, 想到了下面一个问题, 就是VC++编译器是如何知道catch的异常信息的呢? 换句话说, 对于下面的代码, 我们知道出现了异常, 但是怎么得到异常的信息的呢?
__try {
int i = readAge();
printf("Age inputed is %d", i);
} __except (EXCEPTION_EXECUTE_HANDLER) {
printf("Age is not correct."); //如何知道readAge中throw出来的AgeException?
}
为了调试方便, 把异常类和抛出异常的代码修改一下, 在创建异常的时候传递错误的age和一条消息
class AgeException {
public:
int errorAge;
char *p;
AgeException(int age, char* msg) {
errorAge = age;
p = msg;
}
};
int readAge() {
int age = 123; //
if (age <=0 || age >= 100) {
throw AgeException(age, "Age is outof range.");
}
return age;
}
用WinDBG装入, 运行, 出现了C++异常后, 使用.exr -1 命令查看最近出现的异常:
可以看出, 这个异常为前面讨论的C++异常(0xe06d7363类型), 带有3个参数, 每个参数, 参数分别为0x19930520, 0x0012fca4, 0x00417bc8. 因为我没有找到C++异常中参数的含义, 只能猜了(哪位如果知道请赐教).
考虑到抛出异常的代码抛出的异常类型为AgeException, 那么很自然想到抛出的异常作为一个指针存储在参数中. 因为没有资料, 只能挨个试验了. 使用命令dt trycatch!AgeException 地址, 来把trycatch模块(编译出的程序名是trycatch.exe)中地址的内容按照类AgeException显示出来:
果然, 第一个参数0x19930520里面是不是我们想要的; 当输入第二个参数的时候, 该地址中的内容和预料的一致, 是我们抛出的异常中的内容. 这样验证了猜想. try...catch的工作流程为:
-) 编译器在编译try...catch的时候, 也是利用Windows的SEH, 只不过仅仅针对C++异常(0xe06d7363类型)进行处理;
-) 抛出异常的时候(throw), 把生成的异常类的实例地址, 保存在异常信息的第二个参数中
-) catch异常的时候, 从异常信息第2个参数中读出地址, 并转化为异常类的实例, 供程序使用.
要使用__try...__except模拟上述的过程, 程序可以改为:
__try {
int i = readAge();
printf("Age inputed is %d", i);
} __except (extract(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER) {
printf("Exception happene.");
}
void extract(LPEXCEPTION_POINTERS p) {
int d = p->ExceptionRecord->NumberParameters; //参数数目, 这里没用到
unsigned int * ex = (unsigned int *) p->ExceptionRecord->ExceptionInformation[1]; //第二个参数
AgeException * e = (AgeException *) ex; //转换,得到异常类的实例
printf("==> %d \n", e->errorAge); //异常的信息可以知道了.
printf("==> %s \n", e->p); //异常的信息可以知道了.
}
运行, 和预期的结果是一致的.
最后可以得到结论(不知道这样说是否完全正确) :
try...catch是编译器对__try ... __except的一个包装; 该包装仅处理C++异常类型, 但是提供了比较方便的方法来传递抛出的异常信息, 这样程序员能够比较方便的处理异常, 而不用想上面的例子那样要手工去异常信息中去取.
窥探try ... catch与__try ... __except的区别的更多相关文章
- 异常处理__try{}__except(EXCEPTION_EXECUTE_HANDLER){}
在一个函数中不能混合使用 try{}catch(CException *e){} 与 __try{}__except(EXCEPTION_EXECUTE_HANDLER){} 编译时报错 error ...
- VC++ 异常处理 __try __except的用法
转载:https://blog.csdn.net/jiaxiaokai/article/details/50983867 __try __except的用法: __try __except是windo ...
- legend---十一、thinkphp事务中if($ans1&&$ans2){}else{}方式和try{}catch{}方式事务操作的区别在哪里
legend---十一.thinkphp事务中if($ans1&&$ans2){}else{}方式和try{}catch{}方式事务操作的区别在哪里 一.总结 一句话总结:执行的条件其 ...
- __try __except与__try __finally的嵌套使用以及__finally的调用时机
原文:https://blog.csdn.net/SwordArcher/article/details/82465522 try-finally语句的语法与try-except很类似,稍有不同的是, ...
- Promise 中reject 和 catch 处理上有什么区别
reject 是用来抛出异常,catch 是用来处理异常reject 是 Promise 的方法,而 catch 是 Promise 实例的方法reject后的东西,一定会进入then中的第二个回调, ...
- try catch语句在VC下的处理
使用VC编译QT程序碰到一个问题: 我在.h文件里定义: LoadingWidget* w;然后.cpp文件里定义: void MyClass::ModifyTask(){ // w = ...
- c++ 11 国标标准方面的异常处理与微软在Visual Studio 2012的异常处理的区别
这段代码: __try { } __except(GetErrorCode()) { } 可以捕获空指针,但是包围在其中的代码不能有自带析构函数的对象.c++ 11 标准里面的auto_ptr关键字, ...
- VC++ try catch (转)
VC++ try catch (转) 以前都是用try{} catch(-){}来捕获C++中一些意想不到的异常, 今天看了Winhack的帖子才知道,这种方法在VC中其实是靠不住的.例如下面的代 ...
- try{} catch(…){} 讨论(转)
以前都是用try{} catch(…){}来捕获C++中一些意想不到的异常, 今天看了Winhack的帖子才知道,这种方法在VC中其实是靠不住的.例如下面的代码: .try .{ .BYTE* pch ...
随机推荐
- 路由网关zuul(七)
一.定义 Zuul包含了请求的路由和过滤的2个主要的功能: 其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤器功能则负责对请求处理过程进行干预,是实现请求校验,服 ...
- [Hackerrank]时间转换Time Conversion
题目链接 大致要求是说给定一个十二小时制的时间,给出它的二十四小时制的形式. 输入格式:hh:mm:ssAM 或者 hh:mm:ssPM,其中01≤hh≤12,00≤mm,ss≤59 思路 判断字符串 ...
- bit & byte & B & KB & Kbps & KBps & ps
存储单位bit & byte & B & KB & Kbps & KBps & ps (bit,位:byte,字节:区别) Bit,位 :二进制数 ...
- 【矩阵乘法】【快速幂】【递推】斐波那契数列&&矩乘优化递推模板
题目大意: F[0]=0 F[1]=1 F[n+2]=F[n+1]+F[n] 求F[n] mod 104. F[n+2] F[n+1] = 1 1 1 0 * F[n+1] F[n] 记这个矩阵为A, ...
- 【前缀和】【分类讨论】hdu5163 Taking Bus
#include<cstdio> using namespace std; int T,n,m,x,y; long long sum[100001],ans,d[100001]; int ...
- python基础-协程函数、递归、模块、包等内容
1. 协程函数 1.1 yield基本用法 yield作用 1.把函数的执行结果封装好,即封装__iter__和__next__,即得到一个迭代器 2.与return功能类似,都可以返回值,但不同的是 ...
- Matlab设置形状大小
x=0:10; y=2*x; plot(x,y,'-*','linewidth',0.5,'markersize',6)%%默认线宽为0.5,点大小为6 说明:调整线宽也可改变点的形状,这实际上是通过 ...
- 在java中如何在非servlet的普通类中获取request、response、session
原文:http://blog.csdn.net/u012255097/article/details/53092628 在spring的普通类中: HttpServletRequest request ...
- Swift,枚举
枚举类型判断 1.设置并利用枚举 enum Weacher{ case a case b case c } var d=Weacher.b switch d{ case .a: print(" ...
- 全文检索引擎[asp版]
search.asp: <% set DM=server.CreateObject("DeepMap.HLL")pnn=0: wdd="": pnn=Re ...