=============================================================
标题:简记Windows结构化异常处理之“终止处理”
备注:
日期:2011.3.19
姓名:朱铭雷
=============================================================
结构化异常处理(SEH),可以让代码的功能部分同异常处理或者资源回收部分分离开来,使功能部分的代码更清晰可读,同时让代码的健壮性更好。SEH包含两种结构,一种叫做“终止处理”,一种叫做“异常处理”。
终止处理:
__try
{}
__finally
{}
通常在这种结构中,__try块中的代码为主要功能代码,__finally块中的代码回收资源或处理异常和错误。__finally块中的代码肯定会被执行(除非线程或进程被终止),即使__try块中的代码包含return,goto等语句,或者存在内存访问异常。
“终止处理”结构示例1:
BOOL ReadFileData()
{
HANDLE hFile = INVALID_HANDLE_VALUE;
PVOID pvBuf = NULL;
BOOL bRet = FALSE;
__try
{
hFile = CreateFile(_T("filedata.dat"), GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
__leave;
const int PAGESIZE = 4096;
pvBuf = VirtualAlloc(NULL, PAGESIZE, MEM_COMMIT, PAGE_READWRITE);
if (pvBuf == NULL)
__leave;
BOOL bOk = FALSE;
DWORD dwNumberOfBytesRead = 0;
bOk = ReadFile(hFile, pvBuf, PAGESIZE, &dwNumberOfBytesRead, NULL);
if (!bOk)
__leave;
bRet = TRUE;
}
__finally
{
if (pvBuf != NULL)
VirtualFree(pvBuf, 0, MEM_DECOMMIT | MEM_RELEASE);
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
}
return bRet;
}
这段程序将资源回收,全部放在了__finally块,使程序更加整洁,可读性更好。__leave使控制流跳转到__try块结尾,然后正常进入__finally块,这样程序开销很小。
“终止处理”结构示例2:
DWORD ProcessData()
{
DWORD dwTemp = 0;
__try
{
WaitForSingleObject(g_hSem, INFINITE);
dwTemp = FunA(g_dwProtectedData);
}
__finally
{
ReleaseSemaphore(g_hSem, 1, NULL);
}
return dwTemp;
}
这段程序如果没有使用SEH结构,如果FunA函数中存在非法内存访问,Windows将弹出WER对话框。如果点击取消,进程终止,将导致信号量得不到释放,其他等待该信号量的线程很可能永远得不到时间片。
通常不要在__try块中和__finally块中,使用return,goto,continue,break语句,这些语句会造成“提前退出”和“局部展开”,一是对性能有影响,二是有些语句可能得不到执行。
“终止处理”结构示例3:
DWORD ProcessData()
{
DWORD dwTemp = 0;
__try
{
WaitForSingleObject(g_hSem, INFINITE);
dwTemp = FunA(g_dwProtectedData);
}
__finally
{
ReleaseSemaphore(g_hSem, 1, NULL);
if (!AbnormalTermination())
{
// 控制流从__try块正常退出,进入__finally块
// 相关处理操作
}
else
{
// 控制流从__try块异常跳出,进入__finally块
// 相关处理操作
}
}
return dwTemp;
}
通过AbnormalTermination函数可以判断出,控制流是正常还是异常退出__try块的,该函数由编译器产生内联代码。异常退出包括__try块中的代码因含有return,goto,continue,break语句而“提前退出”造成“局部展开”,或者__try块中的代码存在非法内存访问,或者其他异常导致“全局展开”等等。