当前位置: 首页 > 编程日记 > 正文

异常处理与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放入独立的模块之中,但是说明了问题:

  1. finally的语句总是会执行,无论是否异常finally here总是会输出。
  2. finally仅仅是一条保证finally语句执行的块,并不是异常处理的handle语句(与except不同),所以,假如光是有finally语句块的话,实际效果就是异常会继续向上抛出。(异常处理过程也还是继续)
  3. 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的不足。

四、   参考资料

  1. Windows核心编程(Programming Applications for Microsoft Windows),第4版,Jeffrey Richter著,黄陇,李虎译,机械工业出版社
  2. MSDN—Visual Studio 2005 附带版,Microsoft
  3. 加密与解密,段钢编著,电子工业出版社
  4. Windows用户态程序高效排错,熊力著,电子工业出版社

5. 错误处理(Error-Handling):为何、何时、如何(rev#2),刘未鹏(pongba)著

write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie

转载于:https://www.cnblogs.com/lehoho/p/9364966.html

相关文章:

电热水器技术性能指标

1、即热式和贮水式的选择 有使用安全、卫生、不受水压限制&#xff0c;随时可供热水&#xff0c;水温易调节等优点&#xff0c;在发达的西方国家已广泛地使用&#xff0c;即热式热水器体积小&#xff0c;不须预热&#xff0c;但功率大&#xff0c;通常在4-6kw以上&#xff0…

java相关网络协议无响应_java网络协议有哪些

上网的途径有很多&#xff0c;java是最普遍的&#xff0c;那么卑java网络协议有哪些?了解网络安全常识&#xff0c;首先就要了解计算机网络安全有哪些基本注意事项&#xff0c;下面佰佰安全网小编就带您认识一下吧。概念协议是指计算机通信网络中两台计算机之间进行通信所必须…

es日期format_elasticsearch存储日期格式字段

elasticsearch创建index之后&#xff0c;可以设置mapping&#xff0c;如果mapping中没有设置date的format&#xff0c;那么默认为两种格式&#xff1a;date_optional_time 此格式为ISO8601标准 示例&#xff1a;2018-08-31T14:56:18.00008:00epoch_millis 也就是时间戳 示例151…

arraylist 后往前遍历_面试官:谈谈常用的Arraylist和Linkedlist的区别

Arraylist&#xff1a;底层是基于动态数组&#xff0c;根据下表随机访问数组元素的效率高&#xff0c;向数组尾部添加元素的效率高&#xff1b;但是&#xff0c;删除数组中的数据以及向数组中间添加数据效率低&#xff0c;因为需要移动数组。例如最坏的情况是删除第一个数组元素…

linux mipi驱动分析_寒武纪社招内推数字IC设计、DSI驱动、软件架构、产品经理、芯片架构、工具链开发、深度学习、FAE工程师...

点击上方蓝字关注我吧&#xff01;为什么内推更靠谱&#xff1f;内推是基于人脉关系链的推荐&#xff0c;其背后有一定的信用背书&#xff0c;靠谱的人推荐的人相对也会比较靠谱&#xff0c;所以企业一般职位都是从内部开始分享的&#xff0c;相较于自己海投简历&#xff0c;内…

关于鼠标、键盘的几个例子

1 1. 鼠标的哪个按键被点击&#xff1f;2 <html>3 <head>4 <script type"text/javascript">5 function whichButton(event)6 {7 if (event.button2)8 {9 alert("你点击了鼠标右键!")10 }11 else12 {13 alert("你点击了鼠标左键!&qu…

mysql性能优化1

当我们去设计数据库表结构&#xff0c;对操作数据库时&#xff08;尤其是查表时的SQL语句&#xff09;&#xff0c;我们都需要注意数据操作的性能。这里&#xff0c;我们不会讲过多的SQL语句的优化&#xff0c;而只是针对MySQL这一Web应用最多的数据库。希望下面的这些优化技巧…

java获取date的时分秒_Java 之 Date 获取 年月日时分秒

package com.util;import java.text.DateFormat;import java.util.Calendar;import java.util.Date;public class Test {public void getTimeByDate(){Date date new Date();DateFormat df1 DateFormat.getDateInstance();//日期格式&#xff0c;精确到日System.out.println(…

python中字典的value可以为任意对象_Python对象作为字典值

所以我有以下代码,其中字典的值是一个对象,该对象的关键是对象中的一个项目&#xff1a;class MyObject():def getName(self):return self.namedef getValue(self):return self.valuedef __init__(self,name, value):self.name nameself.value valuedict {}object MyObject…

Android Java使用JavaMail API发送和接收邮件的代码示例

JavaMail是Oracle甲骨文开发的Java邮件类API,支持多种邮件协议,这里我们就来看一下Java使用JavaMail API发送和接收邮件的代码示例 使用Javamail发送邮件&#xff0c;必需的jar包&#xff08;请下载javamail的源文件&#xff0c;官方下载页&#xff1a;http://www.oracle.com/t…

python 包用法_Python 基础教程之包和类的用法

Python 基础教程之包和类的用法这篇文章主要介绍了 Python 基础教程之包和类的用法的相关资料, 需要的朋友可以参考下Python 是一种面向对象、解释型计算机程序设计语言&#xff0c;由 Guido van Rossum 于 1989 年底发明&#xff0c;第一个公开发行版发行于 1991 年。Python 语…

[bzoj2259][Oibh]新型计算机_Dijkstra

新型计算机 bzoj-2259 Oibh 题目大意&#xff1a;给定一个n个数的数列&#xff0c;第i个数为a[i]&#xff0c;更改第i个数至x的代价为|x-a[i]|。求最小代价&#xff0c;使得&#xff1a;读入一个数s1后&#xff0c;向后连着读s1个数&#xff0c;然后如s2&#xff0c;再向后读s2…

arcengine 加载地图不显示_地图建筑建模制作与输出

导读阅读完此文&#xff0c;你会了解&#xff1a;1、地图建筑模型通常如何制作的2、地图建筑模型替换策略地图上往往会有一些定制建筑的需求&#xff0c;例如将下面的水立方做成气泡感的。 加入定制模型之前加入定制模型之后这种需求就需要建模师对建筑做定制化建模。模型制作首…

用easyx画电子钟_Canvas入门-利用Canvas绘制好玩的电子时钟

在这之前你需要了解一下方法的使用&#xff1a;beginPath()closePath()moveTo()lineTo()fill()stroke()fillRect()clearRect()这些我在前面的文章介绍过&#xff0c;可以看&#xff1a;画个圆arc()方法arc(x, y, radius, startAngle, endAngle, anticlockwise) > 画一个以(x…

前端开发工程师面试题之综合篇

温馨提示&#xff1a;以下系列的面试题是通过整合网上各位大牛的文章而成&#xff0c;站在巨人的肩膀上&#xff0c;能够让我们更进一步。 1、页面从输入URL到页面加载显示完成&#xff0c;这个过程中都发生了什么&#xff1f; 输入域名地址发送域名地址至DNS服务器并获得对应W…

C语言基础(12)-输入和输出

1. int scanf(const char *format, ...) 说明&#xff1a;scanf用于通过控制台输入字符串。 注意&#xff1a; (1).通过scanf()函数输入的字符串&#xff0c;系统会自动在其后面补一个0,scanf默认回车和空格都是代表输入完成&#xff0c;这样会导致无法输入一个完整的字符串。 …

java static 可见性_Java多线程 synchronized与可见性的关系以及可见性问题总结

作者&#xff1a;七里香的编程之路出自&#xff1a;OSCHINA原文&#xff1a;my.oschina.net/u/4098550/blog/4548274能保证可见性的措施除了volatile 可以让变量保证可见性外.happens-before九大规则. 都是能够保证可见性的. 其中就包含了锁操作(synchronized 和 lock) 和 vola…

表达式树 java_表达树—构建表达式树、获取表达式(二)

public classExprTree {//最后访问头结点public BinaryTreeNode buildExprTree(char postfixExpr[],intsize){LinkedList stacknewLinkedList();BinaryTreeNode nodenull;for(int i0;inodenewBinaryTreeNode();node.setLeft(null);node.setRight(null);node.setData(postfixExp…

python 客户端 如何获取手机_Python学习---Django的request扩展[获取用户设备信息]

关于Django的request扩展【获取用户设备信息】settings.pyINSTALLED_APPS [...app01, # 注册app]STATICFILES_DIRS (os.path.join(BASE_DIR, "statics"),) # 现添加的配置,这里是元组&#xff0c;注意逗号TEMPLATES [...DIRS: [os.path.join(BASE_DIR, templates)…

: error c2062: 意外的类型“int”_Go 命令行解析 flag 包之扩展新类型

上篇文章 说到&#xff0c;除布尔类型 Flag&#xff0c;flag 支持的还有整型&#xff08;int、int64、uint、uint64&#xff09;、浮点型&#xff08;float64&#xff09;、字符串&#xff08;string&#xff09;和时长&#xff08;duration&#xff09;。flag 内置支持能满足大…

java英文字符串大小写转换 必须使用_【Java基础】之字符串大小写转换不利用API....

public class UpStr{static String str "AbcDeFdDSfgdsadeADFSAFCfdsa";public String transformUpperOrLower(String str, String type){//将字符串转换为char数组char[] ch str.toCharArray();if (type null || type.length() 0 || type.equals(""))…

.net core 17

转载于:https://www.cnblogs.com/qingwengang/p/6297486.html

vue中子组件和子组件之间怎么通信_vue.js组件之间如何通信?

vue.js组件之间如何通信&#xff1f;下面本篇文章就来给大家介绍一下Vue.js组件间通信方式。有一定的参考价值&#xff0c;有需要的朋友可以参考一下&#xff0c;希望对大家有所帮助。平时在使用Vue框架的业务开发中&#xff0c;组件不仅仅要把模板的内容进行复用&#xff0c;更…

以太坊RLP机制分析

目录 1 RLP 定义 2 RLP 编码规则 3 RLP 编码实例 4 RLP 分析 1 RLP 定义 RLP&#xff0c;即 Recursive Length Prefix, 递归长度前缀编码&#xff0c;是以太坊数据序列化的主要方法&#xff0c; 具有较好的数据处理效率&#xff0c;尤其是将长度和类型统一作为前缀&#xff0c;…

Android Studio导入Eclipse项目的两种方法

Android Studio导入Eclipse项目有两种方法&#xff0c;一种是直接把Eclipse项目导入Android Studio&#xff0c;另一种是在Eclipse项目里面进行转换&#xff0c;然后再导入Android Studio。 1. 直接导入 打开Android Studio&#xff0c;如果里面已经打开了项目&#xff0c;选择…

mediawiki java api_维基百科 MediaWiki API 解析

使用开放的 API 做一个自己的小项目&#xff0c;是一个很好的学习方法。但好像开放的 API 选择并不多。这里给大家多一个选择&#xff0c;简单介绍一下维基百科使用的 MediaWiki API。简介先简单介绍几个容易混淆的概念。WikiWiki 是一种在网络上开放且可供多人协同创作的超文本…

elasticdump安装_elasticsearch导出、导入工具-elasticdump

elasticsearch导出、导入工具-elasticdumpelasticsearch 数据导入到本地&#xff0c;或本地数据导入到elasticsearch中&#xff0c;或集群间的数据迁移&#xff0c;可以用elasticsearch的工具—elasticdumpelasticdump 可以用用npm安装本地运行&#xff0c;也可以用docker容器运…

mysql 无法登陆_MySQL root用户无法登录原因及解决办法

MySQL root密码正确&#xff0c;却怎么也bai无法du从本地登录MySQL登录提示ERROR 1045 (28000): Access denied for user rootlocalhost (using password: YES)可能原因是mysql库中bai的user表缺少一个root指向host&#xff1a;localhost的数据项&#xff0c;只有一个root指向h…

Spring Boot启动过程(二)

书接上篇 该说refreshContext(context)了&#xff0c;首先是判断context是否是AbstractApplicationContext派生类的实例&#xff0c;之后调用了强转为AbstractApplicationContext类型并调用它的refresh方法。由于AnnotationConfigEmbeddedWebApplicationContext继承自EmbeddedW…

dom vue 加载完 执行_前端面试题——Vue

前言前几天整理了一些 html css JavaScript 常见的面试题(https://segmentfault.com/u/youdangde_5c8b208a23f95/articles)&#xff0c;然后现在也是找了一些在 Vue 方面经常出现的面试题&#xff0c;留给自己查看消化&#xff0c;也分享给有需要的小伙伴。如果文章中有出现纰…