异常处理与MiniDump详解(3) SEH(Structured Exception Handling)
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
一、 综述
SEH--Structured Exception Handling,是Windows操作系统使用的异常处理方式。
对于SEH,有点需要说明的是,SEH是属于操作系统的特性,不为特定语言设计,但是实际上,作为操作系统的特性,几乎就等同与面向C语言设计,这点很好理解,就像Win32 API,Linux下的系统调用,都是操作系统的特性吧,实际还是为C做的。但是,作为为C语言设计的东西,实际上可调用的方式又多了,汇编,C++对于调用C语言的接口都是比较方便的。
二、 基础篇
还是简单介绍一下SEH的使用,但是不准备太详细的介绍了,具体的详细介绍见参考中提及的书目。关于SEH的基本应用,《Windows核心编程》绝对是最佳读物(其实个人一直认为《Windows核心编程》是Windows编程领域必看的第二本书,第一本是《Programming Windows》。关于SEH更深入的一点的知识可能就要参考一些能用汇编讲解的书籍了,《Windows用户态程序高效排错》算是其中讲的不错的一本。
首先,SEH也有像C++异常一样的语法,及类try-catch语法,在SEH中为__try-except语法,抛出异常从throw改为RaiseException,在MSDN中的语法描述为:
__try
{
// guarded code
}
__except ( expression )
{
// exception handler code
}
见一个实际使用的例子:
例1:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
__try
{
RaiseException(0, 0, 0, NULL);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
cout <<"Exception Raised." <<endl;
}
cout <<"Continue running" <<endl;
}
这可能是最简单的SEH的例子了,输出如下:
Exception Raised.
Continue running
这个例子和普通C++异常的try-catch类似,也很好理解。只不过catch换成了except。
因为C语言没有智能指针,那么就不能缺少finally的异常语法,与JAVA,Python等语言中的也类似,(这是C++中没有的)finally语法的含义就是无论如何(不管是正常还是异常),此句总是会执行,常用于资源释放。
例2:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
__try
{
__try
{
RaiseException(0, 0, 0, NULL);
}
__finally
{
cout <<"finally here." <<endl;
}
}
__except(1)
{
}
__try
{
__try
{
int i;
}
__finally
{
cout <<"finally here." <<endl;
}
}
__except(1)
{
}
cout <<"Continue running" <<endl;
getchar();
}
这个实例看起来过于奇怪,因为没有将各个try-finally放入独立的模块之中,但是说明了问题:
- finally的语句总是会执行,无论是否异常finally here总是会输出。
- finally仅仅是一条保证finally语句执行的块,并不是异常处理的handle语句(与except不同),所以,假如光是有finally语句块的话,实际效果就是异常会继续向上抛出。(异常处理过程也还是继续)
- finally执行后还可以用except继续处理异常,但是SEH奇怪的语法在于finally与except无法同时使用,不然会报编译错误。
如下例:
__try
{
RaiseException(0, 0, 0, NULL);
}
__except(1)
{
}
__finally
{
cout <<"finally here." <<endl;
}
VS2005会报告
error C3274: __finally 没有匹配的try
这点其实很奇怪,难道因为SEH设计过于老了?-_-!因为在现在的语言中finally都是允许与except(或类似的块,比如catch)同时使用的。C#,JAVA,Python都是如此,甚至在MS为C++做的托管扩展中都是允许的。如下例:(来自MSDN中对finally keyword [C++]的描述)
using namespace System;
ref class MyException: public System::Exception{};
void ThrowMyException() {
throw gcnew MyException;
}
int main() {
try {
ThrowMyException();
}
catch ( MyException^ e ) {
Console::WriteLine( "in catch" );
Console::WriteLine( e->GetType() );
}
finally {
Console::WriteLine( "in finally" );
}
}
当你不习惯使用智能指针的时候常常会觉得这样会很好用。关于finally异常语法和智能指针的使用可以说是各有长短,这里提供刘未鹏的一种解释,(见参考5的RAII部分,文中比较的虽然是JAVA,C#,但是实际SEH也是类似JAVA的)大家参考参考。
SEH中还提供了一个比较特别的关键字,__leave,MSDN中解释如下
Allows for immediate termination of the __try block without causing abnormal termination and its performance penalty.
简而言之就是类似goto语句的抛出异常方式,所谓的没有性能损失是什么意思呢?看看下面的例子:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
int i = 0;
__try
{
__leave;
i = 1;
}
__finally
{
cout <<"i: " <<i <<" finally here." <<endl;
}
cout <<"Continue running" <<endl;
getchar();
}
输出:
i: 0 finally here.
Continue running
实际就是类似Goto语句,没有性能损失指什么?一般的异常抛出也是没有性能损失的。
MSDN解释如下:
The __leave keyword
The __leave keyword is valid within a try-finally statement block. The effect of __leave is to jump to the end of the try-finally block. The termination handler is immediately executed. Although a goto statement can be used to accomplish the same result, a goto statement causes stack unwinding. The __leave statement is more efficient because it does not involve stack unwinding.
意思就是没有stack unwinding,问题是。。。。。。如下例,实际会导致编译错误,所以实在不清楚到__leave到底干啥的,我实际中也从来没有用过此关键字。
#include <iostream>
#include <windows.h>
using namespace std;
void fun()
{
__leave;
}
int main()
{
__try
{
fun();
}
__finally
{
cout <<" finally here." <<endl;
}
cout <<"Continue running" <<endl;
getchar();
}
三、 提高篇
1. SEH的优点
1) 一个很大的优点就是其对异常进程的完全控制,这一点是C++异常所没有的,因为其遵循的是所谓的终止设定。
这一点是通过except中的表达式来控制的(在前面的例子中我都是用1表示,实际也就是使用了EXCEPTION_EXECUTE_HANDLER方式。
EXCEPTION_CONTINUE_EXECUTION (–1) 表示在异常发生的地方继续执行,表示处理过后,程序可以继续执行下去。 C++中没有此语义。
EXCEPTION_CONTINUE_SEARCH (0) 异常没有处理,继续向上抛出。类似C++的throw;
EXCEPTION_EXECUTE_HANDLER (1) 异常被处理,从异常处理这一层开始继续执行。 类似C++处理异常后不再抛出。
2) 操作系统特性,不仅仅意味着你可以在更多场合使用SEH(甚至在汇编语言中使用),实际对异常处理的功能也更加强大,甚至是程序的严重错误也能恢复(不仅仅是一般的异常),比如,除0错误,访问非法地址(包括空指针的使用)等。这里可以用一个例子来说明:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
__try
{
int *p = NULL;
*p = 0;
}
__except(1)
{
cout <<"catch that" <<endl;
}
cout <<"Continue running" <<endl;
getchar();
}
输出:
catch that
Continue running
在C++中这样的情况会导致程序直接崩溃的,这一点好好利用,可以使得你的程序稳定性大增,以弥补C++中很多的不足。但是,问题又来了,假如异常都被这样处理了,甚至没有声息,非常不符合发生错误时死的壮烈的错误处理原则。。。。。。。很可能导致程序一堆错误,你甚至不知道为什么,这样不利于发现错误。
但是,SEH与MS提供的另外的特性MiniDump可以完美的配合在一起,使得错误得到控制,但是错误情况也能捕获到,稍微的缓解了这种难处(其实也说不上完美解决)。
这一点需要使用者自己权衡,看看到底开发进入了哪个阶段,哪个更加重要,假如是服务器程序,那么在正式跑着的时候,每崩溃一次就是实际的损失。。。所以在后期可以考虑用这种方式。
关于这方面的信息,在下一次在详细讲解。
2. SEH的缺点
其实还是有的,因为是为操作系统设计的,实际类似为C设计,那么,根本就不知道C++中类/对象的概念,所以,实际上不能识别并且正确的与C++类/对象共存,这一点使用C++的需要特别注意,比如下例的程序根本不能通过编译。
例一:
int main()
{
CMyClass o;
__try
{
}
__except(1)
{
cout <<"catch that" <<endl;
}
cout <<"Continue running" <<endl;
getchar();
}
例二:
int main()
{
__try
{
CMyClass o;
}
__except(1)
{
cout <<"catch that" <<endl;
}
cout <<"Continue running" <<endl;
getchar();
}
错误信息都为:
warning C4509: 使用了非标准扩展:“main”使用SEH,并且“o”有析构函数
error C2712: 无法在要求对象展开的函数中使用__try
这点比较遗憾,但是我们还是有折衷的办法的,那就是利用函数的特性,这样可以避开SEH的不足。
比如,希望使用类的使用可以这样:
这个类利用了上节的CResourceObserver类,
class CMyClass : public CResourceObserver<CMyClass>
{
};
void fun()
{
CMyClass o;
}
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
__try
{
fun();
}
__except(1)
{
cout <<"catch that" <<endl;
}
cout <<"Continue running" <<endl;
getchar();
}
输出:
class CMyClass Construct.
class CMyClass Deconstruct.
Continue running
可以看到正常的析构,简而言之就是将实际类/对象的使用全部放进函数中,利用函数对对象生命周期的控制,来避开SEH的不足。
四、 参考资料
- Windows核心编程(Programming Applications for Microsoft Windows),第4版,Jeffrey Richter著,黄陇,李虎译,机械工业出版社
- MSDN—Visual Studio 2005 附带版,Microsoft
- 加密与解密,段钢编著,电子工业出版社
- Windows用户态程序高效排错,熊力著,电子工业出版社
5. 错误处理(Error-Handling):为何、何时、如何(rev#2),刘未鹏(pongba)著
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
异常处理与MiniDump详解(3) SEH(Structured Exception Handling)的更多相关文章
- 异常处理与MiniDump详解(4) MiniDump
http://blog.csdn.net/vagrxie/article/details/4398721 异常处理与MiniDump详解(4) MiniDump 分类: [Wi ...
- 异常处理与MiniDump详解(转)
一. 综述 总算讲到MiniDump了. Dump有多有用我都无法尽数,基本上属于定位错误修复BUG的倚天剑.(日志可以算是屠龙刀)这些都是对于那些不是必出的BUG,放在外面运行的时候出现的BUG ...
- 异常处理与MiniDump详解(2) 智能指针与C++异常
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie 讨论新闻组及文件 一. 综述 <异常处理与MiniDump详解(1) C++异常>稍 ...
- 异常处理与MiniDump详解(1) C++异常(转)
异常处理与MiniDump详解(1) C++异常 write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie 讨论新闻组及文件 一. 综述 我很少敢为自己写 ...
- Structured Exception Handling
https://docs.microsoft.com/en-us/windows/desktop/Debug/structured-exception-handling An exception is ...
- 异常处理与MiniDump 用于投放市场c++异常捕获
最近一段时间,新上线的软件在外场偶尔会出现异常崩溃的情况.由于试用范围比较分散,很难一一前往现场定位问题.而传统的log日志方法,在崩溃的情况下,并不能比较准确的表示出问题位置,这使得软件调试进程缓慢 ...
- C#编程.异常处理(Exception Handling Statements)
C#语言包含结构化异常处理(Structured Exception Handling,SEH). throw The throw statement is used to signal the oc ...
- C# to IL 10 Exception Handling(异常处理)
Exception handling in IL is a big let down. We expected a significant amount of complexity,but were ...
- Spring Boot异常处理详解
在Spring MVC异常处理详解中,介绍了Spring MVC的异常处理体系,本文将讲解在此基础上Spring Boot为我们做了哪些工作.下图列出了Spring Boot中跟MVC异常处理相关的类 ...
随机推荐
- 原生javascript实现分页效果+搜索功能
一.概述 首先,我们要明确为何需要分页技术,主要原因有以下: 1.分页可以提高客户体验度,适当地选择合适的数据条数,让页面显得更有条理,使得用户体验感良好,避免过多数据的冗余. 2.提高性能的需要. ...
- unity简易ui框架
在unity项目开发中,ui模块的开发往往占据了很大一部分工作,部分游戏甚至绝大部分的工作都是在ui上,如何高效管理各种界面,这里分享一套高效易用的UI框架. 首先,我们定义一个PanelBase类, ...
- 搭建Eclipse和MyEclipse的开发环境
主要步骤: 下载并配置Eclipse 建立并运行一个简单的javaSE项目 下载并破解MyEclipse 整合Eclipse和MyEclipse 开发环境和Tomcat结合 关于这个配置也可以参考:h ...
- ssh 远程拷贝文件到本地
scp userA@192.168.0.205:/home/xinshang-toolkit/wwwroot/macheng/tianxiaohuanew-mc.dsceshi.cn/public/ ...
- 令人头疼的Connection Reset
背景: 要爬取某网站的数据,数据每页10条,有很多页(形式如同table表格).使用HttpClient 逐行逐页爬取数据,但在循环爬取多次时,总会在不确定的位置报错 在检查代码逻辑无果之后,开始疯狂 ...
- bsgs(Baby Steps Giant Steps)算法
BSGS算法(Baby Steps Giant Steps算法,大步小步算法,北上广深算法,拔山盖世算法) 适用问题 对于式子: $$x^y=z(mod_p)$$ 已知x,z,p,p为质数: 求解一个 ...
- Java设计模式—命令模式
命令模式是一个高内聚的模式. 定义如下:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 通用类图如下: 角色说明: ● Re ...
- Leetcode算法比赛----First Unique Character in a String
问题描述 Given a string, find the first non-repeating character in it and return it's index. If it doesn ...
- [算法练习]String to Integer (atoi)
题目说明: Implement atoi to convert a string to an integer. Hint: Carefully consider all possible input ...
- linux下close 掉socket 之后 阻塞的recv 不会立即返回
转载自:http://www.cnblogs.com/wainiwann/p/3942203.html 在开发的一个基于rtmp聊天的程序时发现了一个很奇怪的现象. 在windows下当我们执行 cl ...