2015-09-27 12 views
7

Nel seguente codice, la funzione foo si chiama in modo ricorsivo una volta. La chiamata interna provoca l'aumento di una violazione di accesso. La chiamata esterna cattura l'eccezione.È __finalmente necessario eseguire 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; 
} 

Il risultato atteso qui dovrebbe essere

inner finally 0 
outer finally 0 
inner finally 1 
except 1 
outer finally 1 

Tuttavia, outer finally 0 è vistosamente manca dalla produzione reale. È un bug o c'è qualche dettaglio che sto trascurando?

Per completezza, succede con VS2015, compilando per x64. Sorprendentemente non succede su x86, portandomi a credere che sia davvero un bug.

+0

Questo potrebbe tecnicamente rientrare nell'ambito di competenza di un comportamento non definito, come si sta assegnando a un puntatore nullo. Hai provato a lanciare un'eccezione normale usando 'RaiseException'? – OmnipotentEntity

+1

Beh, non va bene. Non è un nuovo problema, VS2013 si comporta allo stesso modo. Sembra una limitazione strutturale di/SAFESEH per me, specifica per la ricorsione, funziona bene in un caso non ricorsivo. Piuttosto dubbioso qui nessuno può risolvere questo problema, meglio eseguire il ping su connect.microsoft.com a riguardo. –

+0

@OnnipotentEntity: l'assegnazione a un puntatore nullo è * comportamento non definito *, per quanto riguarda lo standard di linguaggio C++. Sulla piattaforma Windows, tuttavia, questo è ben definito: l'istruzione solleva una violazione di accesso, che viene comunicata al codice utente tramite un'eccezione SEH. – IInspectable

risposta

0

esistono e più semplicemente esempio (si può rimuovere interno try/finally blocco:.

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); 
    } 
} 

con uscita

except 1 
finally 1 

così finally 0 blocco non eseguito tranne nel caso non ricorsivo - nessun errore:

__try 
{ 
    foo(0); 
} 
__except(EXCEPTION_EXECUTE_HANDLER) 
{ 
    printf("except\n"); 
} 

output:

finally 0 
except 

questo è bug nella funzione successiva

EXCEPTION_DISPOSITION 
__C_specific_handler (
    _In_ PEXCEPTION_RECORD ExceptionRecord, 
    _In_ PVOID EstablisherFrame, 
    _Inout_ PCONTEXT ContextRecord, 
    _Inout_ PDISPATCHER_CONTEXT DispatcherContext 
    ); 

vecchia implementazione di questa funzione con bug 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; 
        } 

se abbiamo tutte le VC compilatore/librerie installate, ricerca chandler.c (nella mia installazione in \VC\crt\src\amd64\chandler.c)

e nel file può visualizzare il codice ora successiva:

   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; 
       } 

condizione in modo supplementare si aggiunge IS_TARGET_UNWIND(ExceptionRecord->ExceptionFlags) che risolvere questo bug

__C_specific_handler implementato in diverse librerie CRT (in alcuni casi con il collegamento statica, in alcuni casi sarà importato da vcruntime*.dll o msvcrt.dll (è stato inoltrato a ntdll.dll)). anche ntdll.dll esportare questa funzione - tuttavia nelle ultime build di win10 (14393) non è ancora stata corretta