2015-09-27 8 views
7

W poniższym kodzie funkcja foo wywołuje się rekurencyjnie jeden raz. Wewnętrzne wywołanie powoduje naruszenie prawa dostępu. Zewnętrzne wywołanie wychwytuje wyjątek.Czy __finally ma działać po 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; 
} 

oczekiwany wynik tutaj powinno być

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

Jednak outer finally 0 jest widoczny brakuje realnej produkcji. Czy to błąd, czy jest jakiś szczegół, który przeoczyłem?

Dla kompletności, dzieje się z VS2015, kompilacja dla x64. Zaskakująco nie dzieje się to na x86, co prowadzi mnie do przekonania, że ​​to naprawdę błąd.

+0

Może to technicznie należeć do zakresu niezdefiniowanych zachowań, jak przypisujesz wskaźnik zerowy. Czy próbowałeś rzucić zwykły wyjątek za pomocą 'RaiseException'? – OmnipotentEntity

+1

Cóż, nie dobrze. To nie jest nowy problem, VS2013 zachowuje się w ten sam sposób. Wygląda jak ograniczenie strukturalne/SAFESEH do mnie, specyficzne dla rekurencji, działa dobrze w przypadku nierekurencyjnym. Bardzo wątpliwe, czy ktoś tutaj może rozwiązać ten problem, najlepiej o ping connect.microsoft.com o tym. –

+0

@OmnipotentEntity: Przypisywanie do wskaźnika pustego jest * niezdefiniowanym zachowaniem *, jeśli chodzi o standard języka C++. Jednak na platformie Windows jest to dobrze zdefiniowane: instrukcja powoduje naruszenie zasad dostępu, które jest przekazywane do kodu użytkownika przez wyjątek SEH. – IInspectable

Odpowiedz

0

istnieć i prościej przykład (możemy usunąć wewnętrzną try/finally bloku.

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

z wyjściem

except 1 
finally 1 

więc nie finally 0 blok wykonany ale nie rekurencyjnego przypadku - nie błąd:

moc wyjściowa:

finally 0 
except 

to jest błąd w następnej funkcji

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

stary realizacja tej funkcji z błędem 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; 
        } 

jeśli mamy zainstalowane najnowsze VC kompilatora/biblioteki, szukaj chandler.c (w mojej instalacji w lokalizacji: \VC\crt\src\amd64\chandler.c)

w pliku można zobaczyć teraz następny kod:

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

więc dodatkowy warunek zostanie dodany IS_TARGET_UNWIND(ExceptionRecord->ExceptionFlags) które naprawić ten błąd

__C_specific_handler realizowany w różnych bibliotekach CRT (w niektórych przypadkach z linku statycznego, aw niektórych przypadkach zostanie zaimportowany z vcruntime*.dll lub msvcrt.dll (został przekazany do ntdll.dll)). również eksportuje tę funkcję - jednak w najnowszych kompilacjach win10 (14393) nie jest jeszcze ustalona

Powiązane problemy