[英]Is __finally supposed to run after EXCEPTION_CONTINUE_SEARCH?
在下面的代碼中,函數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上,讓我相信它確實是一個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
);
舊的實現與錯誤此功能在這里 :
//
// 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)
修復了這個bug
__C_specific_handler
在不同的crt庫中實現(在某些情況下使用靜態鏈接,在某些情況下將從vcruntime*.dll
或msvcrt.dll
導入(轉發到ntdll.dll
))。 還有ntdll.dll
導出這個函數 - 但是在最新的win10版本(14393)中,它仍然沒有修復
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.