C++ 在异常继续搜索后,是否最终应该运行?
在下面的代码中,函数C++ 在异常继续搜索后,是否最终应该运行?,c++,winapi,seh,structured-exception,C++,Winapi,Seh,Structured Exception,在下面的代码中,函数foo递归调用自身一次。内部调用导致引发访问冲突。外部调用捕获异常 #include <windows.h> #include <stdio.h> void foo(int cont) { __try { __try { __try { if (!cont) *(int *)0 = 0
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;
}
然而,0
明显地从实际输出中丢失。这是一个bug还是我忽略了一些细节
为完整起见,VS2015针对x64进行编译。令人惊讶的是,它没有在x86上发生,这让我相信它确实是一个bug。存在,更简单的例子是(我们可以删除内部
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
);
此函数的旧实现存在错误:
如果我们安装了最新的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\u TARGET\u UNWIND(ExceptionRecord->ExceptionFlags)
来修复此错误
在不同的crt库中实现的特定于C++的处理器(在某些情况下使用静态链接,在某些情况下将从
vcruntime*.dll
或msvcrt.dll
(被转发到ntdll.dll
)。同时ntdll.dll
导出此函数-但在最新的win10版本中(14393)它仍然没有修复嗯,不好。这不是一个新问题,VS2013的行为也一样。在我看来,它像是/SAFESEH的结构限制,具体到递归,它在非递归情况下工作得很好。这里的任何人都很难修复这个问题,最好是ping connect.microsoft.com。@IInspectable它是编译器,而不是使其定义良好的平台。@IInspectable No.是编译器决定发出代码。它可以发出代码以在空解引用上执行不同的操作。没有编译器发出代码。@DavidFeffernan:编译器不发出任何特殊的命令。*(int*)0=0;
生成指令mov dword ptr[0],0
。SEH是一个系统服务,操作系统会很好地定义结果。@IInspectable,除非编译器决定在取消引用nullptr时发出代码来执行不同的操作
//
// 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;
}
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;
}