pyqt5 捕获异常确保程序不退出_Windows结构化异常处理(SEH)简介

01

介绍

作为一名C++开发者,当提到异常处理时,我们往往会联想到try,catch、throw等关键字。这些关键字我们并不陌生。虽然实际应用中很少使用,但却不可否认C++这一机制的强大作用。

(对于不了解异常处理是什么的小伙伴,推荐翻看《C++ Primer》一书的相关章节)。

对于C++异常处理的强大及使用,这里我不再赘述。而作为一名Windows平台开发者,今天我准备给大家介绍另一种异常处理机制:Windows平台下的Windows结构化异常处理(Structured exception handling, Windows SEH)。

SEH可以捕获到操作系统级别的异常事件。某些场景下可以帮助开发者更准确、更快速的定位问题。但SEH只能再Windows平台下工作。无法满足跨平台产品的需求。

(对于Windows平台下的开发者来说,学会使用SEH来分析缺陷仍有很大的必要性。)

02

应用

Windows SEH主要包含两个主要功能:终止处理、异常处理(重点)。

提供了以下关键字:__try、__finally、__except、__leave。

终止处理:使用try、finally、leave关键字。

当try中的代码逻辑无论以何种方式退出时,finally中的代码都被执行。

异常处理:使用try、except关键字。

当try中的代码逻辑产生异常事件时,except中可以接收到对应的异常事件。

下面介绍一下这两种功能的使用方式。

终止处理

我们先看以下代码片段:

代码1:

__try {

num = 5;   // step1

}

__finally {

num = 6;   // step2

}

return num;  // step3

代码2:

__try {

num = 5;         // step1

return num;  // step3

}

__finally {

num = 8;         // step2

}

num = 7;

return num;

代码3:

__try {

lock.Lock();   // step1

return ;          // step3

}

__finally {

lock.UnLock(); // step2

}

return;

代码1返回值是6,代码2返回值是8。从注释中我们不难看出执行的时序,无论try执行是完成,还是中途return,finally中的代码都将执行。这种机制确保函数退出前可以让开发者做一些事情。

例如:代码3中我们用这样的方式保证lock锁在return时,一定执行UnLock过程。 

当然,我们也可以定义一个class类型的局部类对象,利用作用域来控制解锁的时机。我们不对比哪种方法更优秀,开发者应根据具体情况来判断使用哪一种方式。

至于try-finally的实现,则是由编译器来完成。当进入和退出一个try时,编译器将产生特殊的代码,同时也将产生一些表,以支持处理SEH。需要一提的是代码2中step3。编译器再监测到try语句中包含return、goto、longjump时将生成额外的代码,这将产生不必要的开销。我们可以使用leave关键字来降低开销。

(如果是性能敏感的代码应该避免使用try-finally。)

使用leave关键字可以告诉编译器直接跳转到finally中,无需再进行监测及生成额外代码。leave的使用也比较简单。在代码3中step3处,直接用__leave替换return关键字即可。

     异常处理

异常处理用于捕获程序中产生的异常事件。这对于开发者来说实用性更高。异常事件又分为硬件异常、软件异常。

硬件异常:是指CPU产生的异常事件。

软件异常:是指代码中生成一个异常事件。

通过try-except捕获并处理这些异常事件。对调查某些内存异常问题帮助很大。

我们先看下代码片段:

     代码4:

__try {

num = num / 0;  // step1

}

__except (EXCEPTION_EXECUTE_HANDLER) {

num = 0;         // step2

}

return num;       // step3

      代码5:

__try {

num = num / 0;   // step1

}

__except (

  GetExceptionCode() ==EXCEPTION_IN_DIVIDE_BY_ZERO||

GetExceptionCode() ==EXCEPTION_FLT_DIVIDE_BY_ZERO) {

num = 0;   // step2

}

return num;  // step3

try-except的使用与C++的try、catch比较相似。代码5中,except过滤器通过简单的判断语句,便可以筛选出一个或多个异常事件进行处理。

代码5中的GetExceptionCode是一个宏定义,并且只能在异常过滤器里(即except之后的括号里)或异常处理代码里面调用。否则编译器会报错。

对于除0问题,CPU会抛出异常事件,我们在except中可以直接捕获到EXCEPTION_IN_DIVIDE_BY_ZERO或EXCEPTION_FLT_DIVIDE_BY_ZERO异常事件。并且进行了简单的纠正处理。这样程序仍可以继续运行。对于那种即使已经出错了,也要继续正确运行的软件来说,这样的机制尤为重要。

而C++往往只能判断除数是否为零,然后throw异常。这对于复杂的程序来说实际作用并不大。

下面,我们列举部分SEH支持的异常事件:

内存相关:

EXCFEPTION_ACCESS_VIOLATION:线程对一个虚拟地址读写而没有访问权。(常见)

EXCFEPTION_DATATYPE_MISALIGNMENT:线程读写未对齐数据。

EXCFEPTION_GUARD_PAGE:试图访问带有保护属性的内存页

EXCFEPTION_STACK_OVERFLOW:线程已经使用了所有栈。

调试相关:

EXCFEPTION_BREAKPOINT:遇到一个断点

EXCFEPTION_INVALID_HANDLE:向函数传递了无效的句柄

整数相关:

EXCFEPTION_INT_DIVIDE_BY_ZERO:线程试图用整数0去除另一个整数

浮点数相关:

EXCFEPTION_FLT_DIVIDE_BY_ZERO:线程试图用浮点数0去除另一个浮点数

EXCFEPTION_FLT_STACK_CHECK:浮点操作的结果导致栈上溢或下溢

上述所提供的异常事件可以帮助开发者分析锁定问题。

03

感谢

对于SEH更为复杂的应用,这里就不在介绍了。感兴趣的小伙伴可以翻看《windows核心编程》一书。关于SEH书中有更为全面的讲解与案例。

a715951ed8ad2cb50a94065108cda776.png
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值