[转]C++异常处理 11

在现在,软件变的越来越大,如果不用SEH,要实现完全强壮的应用程序简直是不可能的。我们先来看一个样板程序,即C的运行时函数strcpy:这是一个相当简单的函数,它怎么会引起一个进程结束呢?如果调用者对这些参数中的某一个传递NULL(或任何无效的地址),strcpy就引起一个存取异常,并且导致整个进程结束。使用SEH,就可以建立一个完全强壮的strcpy函数:
char* RobustStrcpy(char* strDes, char* strSource)
{
__try
{
strcpy(strDes, strSource);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
file://don''''t do no thing
}
return strDes;
}
这个函数所做的一切就是将对strcpy的调用置于一个结构化的异常处理框架中。如果strcpy执行成功,函数就返回。如果strcpy引起一个存取异常,导致该线程执行异常处理程序代码。在这个函数中,处理程序代码什么也不做,RobustStrcpy只是返回到它的调用者,根本不会造成进程结束。

下面这段代码中的函数,返回一个字符串里的以空格分界的符号个数:
DWORD Func_RobustTokens(const char* str)
{
DWORD iTokens = -1;
char* strTemp = NULL;
__try
{
strTemp = (char*)malloc(strlen(str)+1);
strcpy(strTemp, str);
char* pszToken = strtok(strTemp, " ");
for(; pszToken!=NULL; pszToken = strtok(NULL," "))
iTokens++;
iTokens++;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
file://Do nothing for test
}
free(strTemp);
return iTokens;
}
看看在几个不同的情况下这个函数能任何做:
首先,如果调用者向函数传递了NULL(或任何无效的内存地址),iTokens被初始化成-1。在try块中对strlen的调用会引起存取异常。异常过滤器获得控制并将控制转移给except块,except块什么也不做。在except块之后,调用free来释放临时内存块。但是,这个内存从未分配,所以结束调用free,向它传递NULL作为参数。ANSI C明确说明用NULL作为参数调用free是合法的。这时free什么也不做,这并不是错误。最后,函数返回-1,指出失败。注意进程并没有结束。
其次,调用者可能向函数传递了一个有效的地址,但对malloc的调用(在try块中)可能失败并返回NULL。这将导致对strcpy的调用引起一个存取异常。同样,异常过滤器被调用,except块执行(什么也不做),free被调用,传递给它NULL(什么也不做),返回-1,告诉调用程序该函数失败。注意进程也没有结束。
最后,假定调用者向函数传递了一个有效的地址,并且对malloc的调用也成功了。这种情
况下,其余的代码也会成功地在iTokens变量中计算符号的数量。在try块的结尾,异常过滤器不会被求值, except块中代码不会被执行,临时内存缓冲区将被释放,并向调用者返
回iTokens。
使用SEH会感觉很好。

前面的几个代码在__except中都没有代码,现在我们来看这段代码:
PBYTE Func_RobustMemDup(PBYTE pbSrc, size_t cb)
{
PBYTE pbDup = NULL;
__try
{
pbDup = (PBYTE)malloc(cb);
memcpy(pbDup, pbSrc, cb);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
free(pbDup);
pbDup = NULL;
}
return pbDup;
}
这个函数分配一个内存缓冲区,并从源块向目的块复制字节。然后函数将复制的内存缓冲区
的地址返回给调用程序(如果函数失败则返回NULL)。希望调用程序在不需要缓冲区时释放它。
这是在except块中实际有代码的第一个例子。我们看一看这个函数在不同条件下是如何执行的。
• 如果调用程序对pbSrc参数传递了一个无效地址,或者如果对malloc的调用失败(返回NULL),memcpy将引起一个存取异常。该存取异常执行过滤器,将控制转移到except块。在except块内,内存缓冲区被释放, pbDup被设置成NULL以便调用程序能够知道函数失败。这里,注意ANSI C允许对free传递NULL。
• 如果调用程序给函数传递一个有效地址,并且如果对malloc的调用成功,则新分配内存
块的地址返回给调用程序

全局展开
当一个异常过滤器的值为EXCEPTION_EXECUTE_HANDLER时,系统必须执行一个全局展开(global unwind)。这个全局展开使所有那些在处理异常的try _ except块之后开始执行但未完成的try_finally块恢复执行。
关于全局展开在这里不在继续介绍了(写这篇文挡已经是第4天了,我倒~),全局展开也就是在try _ except的try中调用了含有try_finally的代码或函数。

另,通过在finally块中放入一个return语句,就可以阻止系统去完成一个全局展开。虽然MS通过这种设计,来让程序员把展开停止,让代码继续下去,但我曾经说过,尽量的避免在finally块中放入return 语句。

EXCEPTION_CONTINUE_EXECUTION
我们再仔细考察一下异常过滤器,看它是如何计算出定义在Excpt.h中的三个异常标识符之一的。在前面的代码中,我们直接对异常的过滤器设置值为EXCEPTION_EXECUTE_HANDLER,但实际上我们应该让根据具体的情况来得到过滤器的值,比如调用一个函数确定应该返回哪一个标识符。

发生一个异常,如果是EXCEPTION_CONTINUE_EXECUTION标志,代码会试图对发生异常的代码再执行一遍(这个时候__except块中的代码并没有被执行!),所以我们使用EXCEPTION_CONTINUE_EXECUTION的时候,一般都会在返回EXCEPTION_CONTINUE_EXECUTION过滤标志之前,把我们的错误代码修改回来,否则会进入到死循环中。下面这段代码就会导致死循环:
DWORD Func_SEHExceptionContinue()
{
DWORD dwTemp = 0;
__try{
dwTemp = 5/dwTemp;
dwTemp += 10;
}
__except(EXCEPTION_CONTINUE_EXECUTION){
MessageBeep(0);
}
return dwTemp;
}
在使用EXCEPTION_CONTINUE_EXECUTION的时候,一定要特别的小心,有一种情况可以保证每次使用EXCEPTION_CONTINUE_EXECUTION都能成功:
当离散地向一个保留区域提交存储区时。

将虚拟内存技术同结构化异常处理结合起来,可以编写一些执行非常快,非常高效的程序。

EXCEPTION_CONTINUE_SEARCH
EXCEPTION_CONTINUE_SEARCH:是告诉系统继续上溯调用树,去寻找另外的异常过滤器。(如果每个过滤器都返回EXCEPTION_CONTINUE_SEARCH时会出现什么情况呢?在这种情况下,就出现了所谓的“未处理异常”(Unhandled exception))

大家把下面这段代码跟踪一遍就明白了:
void Func_SEHPost(char* sz)
{
__try{
*sz = 0;
}
__except(EXCEPTION_CONTINUE_SEARCH){
file://Here never Exceute
cout<<"If have message, Here is Error"<<endl;
}
}

DWORD GetExceptionFliterCode(char** ppszBuffer)
{
if(*ppszBuffer == NULL)
{
*ppszBuffer = g_szBuffer;
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_EXECUTE_HANDLER;
}

DWORD Func_SEHExceptionSearch()
{
char* pszBuffer = NULL;
__try{
Func_SEHPost(pszBuffer);
}
__except(GetExceptionFliterCode(&pszBuffer))
{
cout<<"Exception Block Run"<<endl;
}
return TRUE;
}

GetExceptionCode
一个异常过滤器在确定要返回什么值之前,必须分析具体情况。例如,异常处理程序可能知道发生了除以0引起的异常时该怎么做,但是不知道该如何处理一个内存存取异常。异常过滤器负责检查实际情况并返回适当的值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值