23.3 终止处理程序

23.3.1 程序的结构

(1)框架

  1. __try{
  2.  
  3. //被保护的代码块
  4. ……
  5. }
  6. __finally{
  7.  
  8. //终止处理
  9.  
  10. }

(2)__try/__finally的特点

  ①finally块总是保证,无论__try块中的代码有无异常,finally块总是被调用执行。

  ②try块后面只能跟一个finally块或except块,要跟多个时只能用嵌套,但__finally块不可以再嵌套SEH块,except块中可以嵌套SEH块。

  ③利用try/finally可以使代码的逻辑更清楚,在try块中完成正常的逻辑,finally块中完成清理工作,使代码可读性更强,更容易维护。

(3)__finally块

  ①当指令从__try块底部自然流出时,会执行finally块

  ②局部展开时:从try块中提前退出(由goto、longjump、continue、break、return等语句引发)将程序控制流强制转入finally块,这时就会进行局部展开(但ExitProcess、ExitThread、TerminateProcess、TerminateThread等原因导致的提前离开除外,因为这会直接终止线/进程,而不能展开)。说白了,局部展开就是将__finally块的代码提前到上述那几种语句之前执行。

  ③全局展开时也会引发finally块的执行。从Vista开始,须显示保护try/finally框架,以确保抛出异常时finally会被执行。即try/finally块外面的某层要使用try/except块保护且except中的过滤函数要返回EXCEPTION_EXECUTE_HANDLER。Vista以前的Windows,会在线程的入口点处用try/except加以保护,但Vista为了提高Windows错误报告(WER)记录的可靠性,将这个入口点的异常过滤程序返回为EXCEPTION_CONTINUE_SEARCH,最后进程会被终止,从而导致finally块没有机会被执行。(关于全局展开见第24章的相关的部分)。

  ④如果异常发生在异常过滤程序里,终止处理程序也不会被执行。

  ⑤finally块被执行的原因总是由以上三种情况之一引起。可以调用AbnormalTermination函数来查看原因。该函数是个内联函数,当正常流出时会返回FALSE,局部或全局展开时返回TRUE。

23.3.2 __leave关键字

  ①该关键字只能用在try/finally框架中,它会导致代码执行控制流跳转到到try块的结尾,也可以认为是try后的闭花括号处。

  ②这种情况下,代码执行是正常从try块进入finally,所以不会进行局部展开

  ③但一般需定义一个布尔变量,指令离开try块时,函数执行的结果是成功还是失败,然后在finally块中可根据这个(或这些)变量以决定资源是否需要释放。

23.3.3 局部展开实例分析

  1. //1、Funcenstein2:return引起的局部展开
  2. DWORD Funcenstein2(){
  3. DWORD dwTemp;
  4.  
  5. //1.处理一些其他事情
  6. //...
  7.  
  8. _try{
  9. //2.
  10. dwTemp = ;
  11.  
  12. //返回一个新的值。因执行return会提前结束try块会导致
  13. //局部展开,此时会先保存dwTemp,然后局部展开,执行finally,
  14. //最后返回try块来执行return。即,将finally提前到return之前执行!
  15. return (dwTemp);
  16. }
  17. __finally{
  18. //3.清除工作,这里简单输出一行文本
  19. printf("Finally块被执行!\n");
  20. }
  21.  
  22. //继续处理——以下这些代码将永远不会被执行
  23. dwTemp = ;
  24. printf("dwTemp = %d\n", dwTemp);
  25. }
  26.  
  27. //2、Funcenstein3:goto引起的局部展开
  28. DWORD Funcenstein3(){
  29. DWORD dwTemp;
  30.  
  31. //1.处理一些其他事情
  32. //...
  33.  
  34. _try{
  35. //2.
  36. dwTemp = ;
  37.  
  38. //试图利用goto跳过finally。这也会导致局部展开。
  39. //当遇到goto时,会先局部展开,先执行finally,
  40. //最后返回try块来执行goto语句。即,将finally提前到goto之前执行!
  41. goto ReturnValue;
  42. }
  43. __finally{
  44. //3.清除工作,这里简单输出一行文本
  45. printf("Finally块被执行!\n");
  46. }
  47.  
  48. //继续处理——以下这些代码将永远不会被执行
  49. dwTemp = ; //该行会被跳过
  50.  
  51. ReturnValue:
  52. printf("dwTemp = %d\n", dwTemp);
  53. return (dwTemp);
  54. }
  55.  
  56. //3.FuncaDoodleDoo:Continue、Break引起的局部展开
  57. DWORD FuncaDoodleDoo(){
  58. DWORD dwTemp = ;
  59. while (dwTemp <){
  60. __try{
  61. //第2次循环时,dwTemp会等于2。由于continue会导致局部展开,
  62. //所以在执行continue前先执行finally块,dwTemp增到3
  63. //然后continue,进行第3次循环
  64. if (dwTemp == )
  65. continue;
  66.  
  67. //第3次循环,这时dwTemp等于3。由于break会导致提前离开try块
  68. //所以进行局部展开,在break前先调用finally块,因此dwTemp增
  69. //到4,然后break跳出循环,所以finally块外面的dwTemp++没被执行
  70. if (dwTemp == )
  71. break;
  72. }
  73. __finally{
  74. dwTemp++;
  75. }
  76.  
  77. dwTemp++;
  78. }
  79. dwTemp += ;
  80. return (dwTemp);
  81. }
  82.  
  83. //4.Funcenstein4函数:finally块内执行return
  84. DWORD Funcenstein4(){
  85. DWORD dwTemp;
  86.  
  87. //1.处理一些其他事情
  88. //...
  89.  
  90. _try{
  91. //2.
  92. dwTemp = ;
  93.  
  94. //返回一个新的值。因执行return会提前结束try块会导致
  95. //局部展开,此时会先保存dwTemp,然后局部展开,执行finally,
  96. //最后返回try块来执行return。即,将finally提前到return之前执行!
  97. return (dwTemp);
  98. }
  99. __finally{
  100. //3.清除工作,这里简单输出一行文本
  101. printf("Finally块被执行!\n");
  102. //return (103); //在finally块中提前return这种行为是未定义行为。
  103. //VC编译器直接报错。但有些编译器会让通过。
  104. //此时在局部展开时,因try块时将返回值复制到一个
  105. //临时变量,在等待这里的finally块返回。但因finally
  106. //里面又要return,所以会将103写入那个临时变量。然后
  107. //函数退出!注意,不再回到try块里了。
  108. }
  109.  
  110. //继续处理——以下这些代码将永远不会被执行
  111. dwTemp = ;
  112. printf("dwTemp = %d\n", dwTemp);
  113. return (dwTemp);
  114. }

【SEHTerm程序】

//SEHTerm.cpp

  1. /************************************************************************
  2. Module: SEHTerm.cpp
  3. Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
  4. ************************************************************************/
  5.  
  6. //#include "../../CommonFiles/CmnHdr.h"
  7. #include <windows.h>
  8. #include <tchar.h>
  9.  
  10. //////////////////////////////////////////////////////////////////////////
  11. //判断操作系统是否是Vista及以上版本
  12. BOOL IsWindowsVistaAbove(){
  13. //第4章的代码
  14. //准备一个OSVERSIONINFOEX结构
  15. OSVERSIONINFOEX osver = { };
  16. osver.dwOSVersionInfoSize = sizeof(osver);
  17. osver.dwMajorVersion = ;
  18. osver.dwMinorVersion = ;
  19. osver.dwPlatformId = VER_PLATFORM_WIN32_NT;
  20.  
  21. //准备条件掩码
  22. DWORDLONG dwlConditionMask = ; //必须初始化为0
  23. VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); //Vista及以上
  24. VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL);
  25. VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);
  26.  
  27. //测试,返回TRUE时表示正好是Vista,否则不是Vista
  28. return VerifyVersionInfo(&osver, VER_MAJORVERSION | VER_MINORVERSION |
  29. VER_PLATFORMID, dwlConditionMask);
  30. }
  31.  
  32. //////////////////////////////////////////////////////////////////////////
  33. void TriggerException(){
  34. __try{
  35. int n;
  36.  
  37. int n = MessageBox(NULL, TEXT("执行非法内存访问吗?"),
  38. TEXT("SEHTerm:在try块内"), MB_YESNO);
  39. if (n == IDYES){
  40. *(PBYTE)NULL = ; //这将引发非法访问内存
  41. }
  42. }
  43. __finally{
  44. PCTSTR psz = AbnormalTermination() ?TEXT("异常结束") : TEXT("正常结束");
  45. MessageBox(NULL, psz, TEXT("SEHTerm:在finally块内"), MB_OK);
  46. }
  47.  
  48. MessageBox(NULL, TEXT("函数正常结束!"),
  49. TEXT("SEHTerm:在finally块之后"), MB_OK);
  50. }
  51.  
  52. //////////////////////////////////////////////////////////////////////////
  53. int WINAPI _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd){
  54.  
  55. //在Vista里,如果过滤函数返回EXCEPTION_EXECUTE_HANDLER时,会引发
  56. //全局展开。如果有一个未处理异常发生,进程会简单地结束从而导致
  57. //finally块不会被执行。
  58.  
  59. if (IsWindowsVistaAbove()){
  60. DWORD n = MessageBox(NULL, TEXT("要用try/except保护吗?"),
  61. TEXT("SEHTerm:工作流"), MB_YESNO);
  62. //DWORD n = IDYES;
  63. if (n == IDYES){ //代码要保护
  64. __try{
  65. TriggerException();
  66. }
  67. __except (EXCEPTION_EXECUTE_HANDLER){
  68. //因为代码被保护,系统对话框不会显示
  69. //所以弹出一个消息框
  70. MessageBox(NULL, TEXT("进程异常结束"),
  71. TEXT("main函数try/except处理器"), MB_OK);
  72.  
  73. //进程退出码
  74. return (-);
  75. }
  76. } else{ //代码不保护
  77. TriggerException();
  78. }
  79. } else{
  80. TriggerException();//其中系统默认是保护的。
  81. }
  82.  
  83. MessageBox(NULL, TEXT("进程正常结束"),
  84. TEXT("SEHTerm:即将退出主线程!"),MB_OK);
  85. return ;
  86. }

//resource.h

  1. //{{NO_DEPENDENCIES}}
  2. // Microsoft Visual C++ 生成的包含文件。
  3. // 供 23_SEHTerm.rc 使用
  4. //
  5. #define IDI_SEHTERM 101
  6.  
  7. // Next default values for new objects
  8. //
  9. #ifdef APSTUDIO_INVOKED
  10. #ifndef APSTUDIO_READONLY_SYMBOLS
  11. #define _APS_NEXT_RESOURCE_VALUE 102
  12. #define _APS_NEXT_COMMAND_VALUE 40001
  13. #define _APS_NEXT_CONTROL_VALUE 1001
  14. #define _APS_NEXT_SYMED_VALUE 101
  15. #endif
  16. #endif

//SEHTerm.rc

  1. // Microsoft Visual C++ generated resource script.
  2. //
  3. #include "resource.h"
  4.  
  5. #define APSTUDIO_READONLY_SYMBOLS
  6. /////////////////////////////////////////////////////////////////////////////
  7. //
  8. // Generated from the TEXTINCLUDE 2 resource.
  9. //
  10. #include "winres.h"
  11.  
  12. /////////////////////////////////////////////////////////////////////////////
  13. #undef APSTUDIO_READONLY_SYMBOLS
  14.  
  15. /////////////////////////////////////////////////////////////////////////////
  16. // 中文(简体,中国) resources
  17.  
  18. #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
  19. LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
  20.  
  21. #ifdef APSTUDIO_INVOKED
  22. /////////////////////////////////////////////////////////////////////////////
  23. //
  24. // TEXTINCLUDE
  25. //
  26.  
  27. TEXTINCLUDE
  28. BEGIN
  29. "resource.h\0"
  30. END
  31.  
  32. TEXTINCLUDE
  33. BEGIN
  34. "#include ""winres.h""\r\n"
  35. "\0"
  36. END
  37.  
  38. TEXTINCLUDE
  39. BEGIN
  40. "\r\n"
  41. "\0"
  42. END
  43.  
  44. #endif // APSTUDIO_INVOKED
  45.  
  46. /////////////////////////////////////////////////////////////////////////////
  47. //
  48. // Icon
  49. //
  50.  
  51. // Icon with lowest ID value placed first to ensure application icon
  52. // remains consistent on all systems.
  53. IDI_SEHTERM ICON "SEHTerm.ico"
  54. #endif // 中文(简体,中国) resources
  55. /////////////////////////////////////////////////////////////////////////////
  56.  
  57. #ifndef APSTUDIO_INVOKED
  58. /////////////////////////////////////////////////////////////////////////////
  59. //
  60. // Generated from the TEXTINCLUDE 3 resource.
  61. //
  62.  
  63. /////////////////////////////////////////////////////////////////////////////
  64. #endif // not APSTUDIO_INVOKED

第23章 SEH结构化异常处理(3)_终止处理程序的更多相关文章

  1. 第23章 SEH结构化异常处理(2)_编译器对系统SEH机制的封装

    23.2 编译器层面对系统SEH机制的封装 23.2.1 扩展的EXCEPTION_REGISTRATION级相关结构:VC_EXCEPTION_REGISTRATION (1)VC_EXCEPTIO ...

  2. 第23章 SEH结构化异常处理(1)_系统SEH机制

    23.1 基础知识 23.1.1 Windows下的软件异常 (1)中断和异常 ①中断是由外部硬件设备或异步事件产生的 ②异常是由内部事件产生的,可分为故障.陷阱和终止三类. (2)两种异常处理机制: ...

  3. 第25章 SEH结构化异常处理_未处理异常及向量化异常

    25.1 UnhandledExceptionFilter函数详解 25.1.1 BaseProcessStart伪代码(Kernel32内部) void BaseProcessStart(PVOID ...

  4. 第24章 SEH结构化异常处理_异常处理及软件异常

    24.1  程序的结构 (1)try/except框架 __try{ //被保护的代码块 …… } __except(except fileter/*异常过滤程序*/){ //异常处理程序 } (2) ...

  5. 异常处理第三讲,SEH(结构化异常处理),异常展开问题

    异常处理第三讲,SEH(结构化异常处理),异常展开问题 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 不知道昨天有木有 ...

  6. Windows内核读书笔记——SEH结构化异常处理

    SEH是对windows系统中的异常分发和处理机制的总称,其实现分布在很多不同的模块中. SEH提供了终结处理和异常处理两种功能. 终结处理保证终结处理块中的程序一定会被执行 __try { //要保 ...

  7. 深入研究 Win32 结构化异常处理(作者博客有许多SEH的研究文章)

    摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...

  8. [C++]深入解析结构化异常处理(SEH)

    http://www.cppblog.com/weiym/archive/2015/02/27/209884.html 尽管以前写过一篇SEH相关的文章<关于SEH的简单总结>, 但那真的 ...

  9. SEH:结构化异常处理 学习

    SEH:结构化异常处理 结构化异常处理机制提供了一个操作系统,用于优化结构的方案,为客户提供更强大的程序执行环境.试想一下,你写程序不用考虑内存访问错误,那里是空指针错误,一直在按照程序的逻辑结构来写 ...

随机推荐

  1. 如何选择RabbitMQ的消息保存方式?

    RabbitMQ对于queue中的message的保存方式有两种方式:disc和ram.如果采用disc,则需要对exchange/queue/delivery mode都要设置成durable模式. ...

  2. [TypeScript] JSON对象转TypeScript对象范例

    [TypeScript] JSON对象转TypeScript对象范例 Playground http://tinyurl.com/nv4x9ak Samples class DataTable { p ...

  3. JavaScript基础10——node对象

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  4. MySQL数据库的基本数据类型

    整数类型 数值型数据类型主要用来存储数字,包含的类型有: TINYINT.SMALLINT.MEDIUMINT. INT(INTEGER). BIGINT. 下面通过一个例子来查看各种类型的所占有的数 ...

  5. TXT记事本转换PDF文件

    使用的方式为,读取TXT记事本的内容,然后写入创建的PDF文件. static void Main(string[] args) { const string txtFile = "D:\\ ...

  6. SoapUI接口测试实例(webservice接口)

    接口测试步骤 注:以测试queryHistoryAccepts接口作举例. 1. 用户登录获取SessionKey实体信息 注:由于大部分的接口都需要SessionKey实体的信息,因此测试那些接口都 ...

  7. Effective Java 06 Eliminate obsolete object references

    NOTE Nulling out object references should be the exception rather than the norm. Another common sour ...

  8. Effective Java 14 In public classes, use accessor methods, not public fields

    Principle To offer the benefits of encapsulation you should always expose private field with public ...

  9. "ORA-12154: TNS:could not resolve the connect identifier specified"的解决办法

    添加环境变量解决: 变量名:TNS_ADMIN 变量值:D:\Ocl\product\11.2.0\dbhome_1\NETWORK\ADMIN tnsnames.ora所在的路径

  10. 微信开发(03)之新建按钮时报错 errcode 40054

    在微信开发新建公众号的按钮时,报错如下: {errcode:40054,errmsg:"invalid sub button url domain"} 经过仔细排查,发现是url地 ...