在以下代码中,函数foo
以递归方式调用自身一次。内部调用导致引发访问冲突。外部调用捕获异常。
#include <windows.h>
#include <stdio.h>
void foo(int cont)
{
__try
{
__try
{
__try
{
if (!cont)
*(int *)0 = 0;
foo(cont - 1);
}
__finally
{
printf("inner finally %d\n", cont);
}
}
__except (!cont? EXCEPTION_CONTINUE_SEARCH: EXCEPTION_EXECUTE_HANDLER)
{
printf("except %d\n", cont);
}
}
__finally
{
printf("outer finally %d\n", cont);
}
}
int main()
{
__try
{
foo(1);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
printf("main\n");
}
return 0;
}
此处的预期输出应为
inner finally 0
outer finally 0
inner finally 1
except 1
outer finally 1
然而,outer finally 0
在实际输出中明显缺失。这是一个错误还是我忽略了一些细节?
为了完整性,VS2015会进行,编译为x64。令人惊讶的是,它不会发生在x86上,让我相信它确实是一个错误。
答案 0 :(得分:0)
存在且更简单的例子(我们可以删除内部try/finally
块:
void foo(int cont)
{
__try
{
__try
{
if (!cont) *(int *)0 = 0;
foo(cont - 1);
}
__except (cont? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
printf("except %d\n", cont);
}
}
__finally
{
printf("finally %d\n", cont);
}
}
带输出
except 1
finally 1
所以finally 0
阻止没有执行。但在没有递归的情况下 - 没有错误:
__try
{
foo(0);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
printf("except\n");
}
输出:
finally 0
except
这是下一个功能中的错误
EXCEPTION_DISPOSITION
__C_specific_handler (
_In_ PEXCEPTION_RECORD ExceptionRecord,
_In_ PVOID EstablisherFrame,
_Inout_ PCONTEXT ContextRecord,
_Inout_ PDISPATCHER_CONTEXT DispatcherContext
);
此函数的旧实现与错误here:
//
// try/except - exception filter (JumpTarget != 0).
// After the exception filter is called, the exception
// handler clause is executed by the call to unwind
// above. Having reached this point in the scan of the
// scope tables, any other termination handlers will
// be outside the scope of the try/except.
//
if (TargetPc == ScopeTable->ScopeRecord[Index].JumpTarget) { // bug
break;
}
如果我们安装了最新的VC编译器/库,请搜索chandler.c
(在我位于\VC\crt\src\amd64\chandler.c
的安装中)
并在文件中可以查看下一个代码:
if (TargetPc == ScopeTable->ScopeRecord[Index].JumpTarget
// Terminate only when we are at the Target frame;
// otherwise, continue search for outer finally:
&& IS_TARGET_UNWIND(ExceptionRecord->ExceptionFlags)
) {
break;
}
因此添加IS_TARGET_UNWIND(ExceptionRecord->ExceptionFlags)
附加条件来修复此错误
__C_specific_handler
在不同的crt库中实现(在某些情况下使用静态链接,在某些情况下会从vcruntime*.dll
或msvcrt.dll
导入(已转发到ntdll.dll
)) 。也ntdll.dll
导出此函数 - 但是在最新的win10版本(14393)中,它仍未修复