2015-09-27 17 views
7

在下面的代碼中,函數foo遞歸調用一次。內部調用會導致訪問衝突。外部調用捕獲異常。__finally應該在EXCEPTION_CONTINUE_SEARCH之後運行?

#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

這可能在技術上屬於未定義行爲的範圍,因爲您正在分配給空指針。你有沒有嘗試用'RaiseException'拋出一個常規異常? – OmnipotentEntity

+1

嗯,不好。這不是一個新問題,VS2013的行爲方式是一樣的。看起來像/ SAFESEH對我的結構性限制,具體到遞歸,它在非遞歸的情況下工作正常。相當可疑的任何人都可以解決這個問題,最好能ping通connect.microsoft.com。 –

+0

@OmnipotentEntity:就C++語言標準而言,分配一個空指針是* undefined behavior *。然而,在Windows平臺上,這是明確定義的:該指令引發訪問衝突,通過SEH異常傳遞給用戶代碼。 – IInspectable

回答

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)其修復這個bug在不同CRT庫實現

__C_specific_handler(在一些情況下,與靜態鏈接,:

和在文件可以查看現在下一個代碼將從vcruntime*.dllmsvcrt.dll(轉發給ntdll.dll))導入)。也ntdll.dll導出此功能 - 但在最新的win10構建(14393)它仍然不固定

相關問題