/*
结论* try块和finally块中不允许出现return break continue goto等改变执行流程的语句
1 如果try块中有return语句 finally不会因为这条语句失去执行机会
但是会让编译器生成额外的代码 因此要跳出try块应该使用__leave
2 如果try块和finally块中同时有return语句
编译器会做局部展开(分配栈空间) 保存try块中的返回值 然后执行finally中的语句
当执行到finally块的return语句时 会将finally的返回值写入到局部展开内存中
覆盖try块中的返回值 因此finally中不应该使用return语句
*/
/***********************规范的try finally用法***********************/
HANDLE hFile = INVALID_HANDLE_VALUE;
PVOID pvBuf = NULL;
BOOL bFunctionOk = FALSE;
__try
{
//---------------------打开文件---------------------
hFile = CreateFile(TEXT("some data.DAT"), GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
__leave;
//---------------------分配内存---------------------
pvBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (pvBuf == NULL)
__leave;
//---------------------将文件内容读入内存---------------------
DWORD dwNumBytesRead;
BOOL bOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL);
if (!bOK || (dwNumBytesRead == 0))
__leave;
bFunctionOk = TRUE;
}
__finally
{
if (pvBuf != NULL)
VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);
if (hFIle != INVALID_HANDLE_VALUE)
CloseHandel(hFile);
}
return bFunctionOk;
扩展知识点——局部展开和全局展开
局部展开就是指某个函数中的__try块中的代码因为执行了return,break,goto,continue等指令使得指令流要提前流出__try块或者因为指令流到了__try块底部要自然流出,这时进行局部展开,说白了就是执行__finally块中的代码,以保证__finally语义的正确性。这里注意局部展开是代码的自然执行过程,而不是由异常等引起的。
全局展开则是由异常引起的,所以这里先大致说下SEH对异常的处理过程,当某个__try块中的代码触发了异常时(也可能是__try块中调用的函数中引发异常),操作系统会从最靠近引发异常代码的地方开始从下层往上层查找__except块(这里的层是指__try块的嵌套层),对于找到的每一个__except块,会先计算它的异常过滤器,如果过滤器返回EXCEPTION_CONTINUE_SEARCH,则说明此__except块不处理此类异常,需要继续往上层查找,如果某过滤器返回EXCEPTION_EXECUTE_HANDLER则说明此__except块可以处理此类异常,即找到了异常的处理代码,此时停止查找,但是在执行该__except块中的异常处理代码之前,要先进行全局展开。全局展开的过程与查找__except块的过程类似,只不过这次是查找从底层向上查找__finally块,查找过程中遇到的每一个__finally块中的代码都被执行,直到查找到前面说的处理异常的__except块那一层停止,这时全局展开完成,然后执行__except块中的异常处理代码,执行完异常处理代码之后,指令流从__except块后的第一条指令开始。从这里也可以看出全局展开也是为了保证__finally语义的正确性,因为指令流从引发异常代码转到到__except异常处理代码时也导致了指令流从__try块嵌套层中所有与__finally对应的__try块中流出,由前面的__finally语义说明可知,此时必须要执行全局展开过程以包成__finally语义的正确性。
这里另外做一个特别说明,前面提到了两种过滤器返回值(EXCEPTION_CONTINUE_SEARCH和EXCEPTION_EXECUTE_HANDLER),只有过滤器返回EXCEPTION_EXECUTE_HANDLER才可能会引发全局展开,在windows SEH中还有一种过滤器返回值是EXCEPTION_CONTINUE_EXECUTION,它的意思是指令流重新返回引发异常的代码,重新执行一次。这个返回值也不会引起全局展开,因为既然指令流返回了引发异常代码处,表明没有流出__try块,所以不需要展开。
代码规范之旅一 __try __finally __leave
最新推荐文章于 2021-11-15 21:54:02 发布