`
java-mans
  • 浏览: 11339192 次
文章分类
社区版块
存档分类
最新评论

《Windows核心编程》---终止处理程序

 
阅读更多

结构化异常处理(SEH)包含两方面的功能:终止处理和异常处理。

终止处理程序确保不管一个代码块是如何退出的,另一个代码块总能被调用和执行。终止处理的语法如下:

__try

{

// guarded body of code (被保护代码)

}

__finally

{

// __finally block (终止处理代码)

}

操作系统和编译器的协同工作保证了不管被保护代码部分是如何退出的---无论是在保护代码中使用return、还是goto、或者longjump语句---终止处理程序都会被调用,即__finally代码块都能执行。(除非在保护代码中调用ExitProcessExitThreadTerminateProcessTerminateThread来终止进程或线程)

实例代码如下:

LPTSTR lpBuffer = NULL;

CRITICAL_SECTION CriticalSection;

// EnterCriticalSection synchronizes code with other threads.

EnterCriticalSection(&CriticalSection);

__try

{

// Perform a task that may cause an exception.

lpBuffer = (LPTSTR) LocalAlloc(LPTR, 10);

StringCchCopy(lpBuffer, 10, TEXT("Hello"));

_tprintf(TEXT("%s/n"),lpBuffer);

LocalFree(lpBuffer);

}

__finally

{

// LeaveCriticalSection is called even if an exception occurred.

LeaveCriticalSection(&CriticalSection);

}

编译器怎样保证finally块可以在try代码块退出前被执行呢?原来当编译器检查程序代码时,如果发现try代码块里有一个return语句,于是编译器会生成一些代码先将返回值保存在由它创建的临时变量里,然后再执行finally代码块,这个过程称为局部展开。一旦finally代码块执行完毕,编译器所创建的变量的值就会返回给函数的调用者。实际上,应该尽量避免在try代码块中使用return语句,因为这对应用程序性能是有害的。

如果代码控制流正常地离开try代码块进入finally代码块,那么进入finally代码块的额外开销是最小的。为了尽可能避免写出让try块提前退出的代码,Microsoft为它的C/C++编译器加入了一个关键字:__leave

关键字__leave会导致代码执行控制流跳转到try块的结尾。因为在这种情况下,代码执行将正常地从try块进入finally块,所以不会产生额外的开销。

实例代码如下:

DWORD ASCEFunc()

{

HANDLE hFile = INVALID_HANDLE_VALUE;

PVOID pvBuf = NULLL;

BOOL bFunctionOk = FALSE;

__try

{

DWORD dwNumBytesRead;

BOOL bOk;

hFile = CreateFile(TEXT("asce.dat"), GENERIC_READ, FILE_SHARE_MODE,

NULL, OPEN_EXISTING, 0, NULL);

if(hFIle == NULL)

__leave;

pvBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);

if(pvBuf == NULL)

__leave;

bOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL);

if(!bOk || (dwNumBytesRead == 0))

__leave;

//Do some operation here

//成功返回标志

bFunctionOk = TRUE;

}

__finally

{

if(pvBuf != NULL)

VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);

if(hFile != INVALID_HANDLE_VALUE)

CloseHandle(hFile);

}

return bFunctionOk;

}

总结,三种会引起finally块执行的情形:

1)从try块到finally块的正常代码控制流;

2)局部展开:从try块的提前退出(由gotolongjumpcontinuebreakreturn等语句引起)将程序控制流强制转入finally块;

3)全局展开。

finally块的执行总是由以上三种情况之一引起的,要确定是哪一种,可以调用内在函数AbnormalTermination

BOOL AbnormalTermination(void);

注意:我们只能在finally块里调用这个内在函数,它将返回一个布尔值来表明一个与当前finally块相关的try块是否已经提前退出。即如果代码执行从try块正常流入finally块,函数的返回值是FALSE;如果控制流从try块中异常退出---通常是try块中gotobreakreturncontinue语句导致了局部展开,或者因为try块中代码抛出了内存访问违规或其他异常引起全局展开---那么AbnormalTermination函数返回TRUE。但进一步区分是全局展开还是局部展开是不可能的。

实例代码如下:

/******************************************************************************

Module: SEHTerm.cpp

Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre

******************************************************************************/

#include <windows.h>

#include <tchar.h>

///////////////////////////////////////////////////////////////////////////////

BOOL IsWindowsVista() {

// Prepare the OSVERSIONINFOEX structure to indicate Windows Vista.

OSVERSIONINFOEX osver = { 0 };

osver.dwOSVersionInfoSize = sizeof(osver);

osver.dwMajorVersion = 6;

osver.dwMinorVersion = 0;

osver.dwPlatformId = VER_PLATFORM_WIN32_NT;

// Prepare the condition mask.

DWORDLONG dwlConditionMask = 0; // You MUST initialize this to 0.

VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL);

VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL);

VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);

// Perform the version test.

if (VerifyVersionInfo(&osver, VER_MAJORVERSION | VER_MINORVERSION |

VER_PLATFORMID, dwlConditionMask)) {

// The host system is Windows Vista exactly.

return(TRUE);

} else {

// The host system is NOT Windows Vista.

return(FALSE);

}

}

void TriggerException() {

__try {

int n = MessageBox(NULL, TEXT("Perform invalid memory access?"),

TEXT("SEHTerm: In try block"), MB_YESNO);

if (n == IDYES) {

* (PBYTE) NULL = 5; // This causes an access violation

}

}

__finally {

PCTSTR psz = AbnormalTermination()

? TEXT("Abnormal termination") : TEXT("Normal termination");

MessageBox(NULL, psz, TEXT("SEHTerm: In finally block"), MB_OK);

}

MessageBox(NULL, TEXT("Normal function termination"),

TEXT("SEHTerm: After finally block"), MB_OK);

}

int WINAPI _tWinMain(HINSTANCE, HINSTANCE, PTSTR, int) {

// In Windows Vista, a global unwind occurs if an except filter

// returns EXCEPTION_EXECUTE_HANDLER. If an unhandled exception

// occurs, the process is simply terminated and the finally blocks

// are not exectuted.

if (IsWindowsVista()) {

DWORD n = MessageBox(NULL, TEXT("Protect with try/except?"),

TEXT("SEHTerm: workflow"), MB_YESNO);

if (n == IDYES) {

__try {

TriggerException();

}

__except (EXCEPTION_EXECUTE_HANDLER) {

// But the system dialog will not appear.

// So, popup a message box.

MessageBox(NULL, TEXT("Abnormal process termination"),

TEXT("Process entry point try/except handler"), MB_OK);

// Exit with a dedicated error code

return(-1);

}

} else {

TriggerException();

}

} else {

TriggerException();

}

MessageBox(NULL, TEXT("Normal process termination"),

TEXT("SEHTerm: before leaving the main thread"), MB_OK);

return(0);

}

使用终止处理程序的理由:

1)因为清理工作集中在一个地方执行,并且保证能得到执行,从而简化了错误处理;

2)提高代码的可读性;

3)让代码更容易维护;

4)如果正确使用,它对程序性能和体积的影响是微小的。

分享到:
评论

相关推荐

    精通Windows.API-函数、接口、编程实例.pdf

    2.3 Windows API核心DLL 21 2.3.1 Kernel32.dll 21 2.3.2 User32.dll 21 2.3.3 Gdi32.dll 22 2.3.4 标准C函数 22 2.3.5 其他Dll 22 2.4 Unicode和多字节 22 2.4.1 W版本和A版本的API 24 2.4.2 ...

    Windows应用程序捆绑核心编程光盘代码

    4.4.2 流式套接字编程流程 91 4.4.3 套接字调用基本函数 92 4.4.4 Winsock程序设计 95 4.4.5 一个通用套接字类 96 4.4.6 套接字服务器端与客户端间通信的实例 101 4.5 本章小结 106 参考文献 106 第5章 使用...

    精通WindowsAPI 函数 接口 编程实例

    2.3 Windows API核心DLL 21 2.3.1 Kernel32.dll 21 2.3.2 User32.dll 21 2.3.3 Gdi32.dll 22 2.3.4 标准C函数 22 2.3.5 其他Dll 22 2.4 Unicode和多字节 22 2.4.1 W版本和A版本的API 24 2.4.2 ...

    《Windows核心编程系列》进程的建立和终止

    首先要给大家介绍的是进程内核对象。进程大家都不陌生,它是资源和分配的基本单位,而进程内核对象是与进程相关联的一个数据结构。操作系统内核通过它管理进程,也是操作系统...  Windows支持两种类型的应用程序:GU

    Visual.Basic.2010.&.NET4.高级编程(第6版)-文字版.pdf

    第iii部分 智能客户端应用程序第14章 windows窗体 543 14.1 system.windows.forms名称空间 543 14.2 窗体的使用 544 14.2.1 设置启动窗体 544 14.2.2 通过sub main显示窗体 545 14.2.3 application类的更...

    Python编程入门经典

    1.2.1 在非Windows系统上安装 Python 3.1 5 1.2.2 使用Python Shell 5 1.3 开始使用Python——字符串 6 1.3.1 字符串概述 6 1.3.2 为什么需要引号 6 1.3.3 为什么有3种类型的引号 7 1.3.4 使用print()函数 7 1.3.5 ...

    win2000驱动程序设计指南

    网络驱动程序设计指南的向导 3 第二章 内核模式驱动程序的网络结构 6 2.1 Windows 2000 网络结构和OSI模型 6 2.2 NDIS驱动程序 7 2.2.1 NDIS微端口驱动程序 7 2.2.2 NDIS中间层驱动程序 8 2.2.3 ...

    MFC-李进九 电子书籍完整版

    1.1 MFC是一个编程框架 1 1.1.1 封装 1 1.1.2 继承 2 1.1.3 虚拟函数和动态约束 2 1.1.4 MFC的宏观框架体系 2 1.2 MDI应用程序的构成 3 1.2.1 构成应用程序的对象 3 1.2.2 构成应用程序的对象之间的关系 5 1.2.3 ...

    windows顽固文件删除工具最新版.rar

    同时作者及开发方不承担任何因用户操作引起的损失担负法律责任.3:程序是采用了中国国内汉语编程环境易语言开发,有一些杀毒软件或安全防护程序会因编译核心的特征码无法识别产生误报,但这并不说明程序包含恶意代码或...

    Access+2000中文版高级编程

    7.7.2 调用错误日志的错误处理程序的示例 157 7.7.3 每个错误的跟踪信息 158 7.7.4 检查实际的错误日志代码 159 7.7.5 首先记录到后端,必要时记录到前端 162 7.7.6 使用错误处理程序更新后端 163 7.8 ...

Global site tag (gtag.js) - Google Analytics