簡體   English   中英

__finally應該在EXCEPTION_CONTINUE_SEARCH之后運行嗎?

[英]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*.dllmsvcrt.dll導入(轉發到ntdll.dll ))。 還有ntdll.dll導出這個函數 - 但是在最新的win10版本(14393)中,它仍然沒有修復

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM