2012-03-26 7 views
18

Zdaję się sytuacji, w której wywołanie PInvoke do CloseHandle rzuca SEHException w aplikacji .NET 4, gdy jest uruchomiony w debugerze. W przeciwieństwie do others who have encountered similar issues migrating from 3.5 to 4, nie jestem szczególnie zaniepokojony tym zachowaniem i już zlokalizowałem problem (trzecia strona biblioteki wywołująca dwa razy na tym samym uchwycie). Jednak jestem zakłopotany, dlaczego to zachowanie nie występuje w aplikacji .NET 3.5.Dlaczego obsługa wyjątków z programu CloseHandle różni się między platformami .NET 4 i 3.5?

Poniżej mały, ale kompletny przykład demonstruje zachowanie mam przeżywa (testowane zarówno na XP SP3 i Win 7 x64, zawsze kompilowany jako x86):

class Program 
{ 
    static void Main(string[] args) 
    { 
     try 
     { 
      var hFileMapping = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, 0x04 /* read write */, 0, 0x1000, null); 
      CloseHandle(hFileMapping); 
      CloseHandle(hFileMapping); 
      Console.WriteLine("No exception"); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex); 
     } 

     Console.ReadKey(); 
    } 

    [DllImport("kernel32", SetLastError = true)] 
    static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpAttributes, int flProtect, int dwMaximumSizeHigh, int dwMaximumSizeLow, string lpName); 

    [DllImport("kernel32", SetLastError = true)] 
    static extern bool CloseHandle(IntPtr handle); 
} 

gdy uruchamiane jako aplikacja .NET 4, SEHException jest wyrzucane na drugim CloseHandle. Zgodnie z documentation for CloseHandle, spodziewane jest zachowanie:

Jeśli aplikacja działa pod debugera funkcja wyjątek, jeżeli odbiera zarówno wartość uchwyt nie jest ważne lub wartości pseudo uchwytu . Może się to zdarzyć, jeśli dwa razy zamkniesz uchwyt lub jeśli wywołasz CloseHandle na uchwycie zwróconym przez funkcję FindFirstFile zamiast wywoływać funkcję FindClose.

Jednakże, kiedy zestawiane jako NET 3,5 aplikacji (lub CLR 2,0), nie jest wyjątek w drugim CloseHandle połączenia i komunikat "No exception" wydrukowaniu.

Zgodnie z this article, zaktualizowana wersja CLR wydana dla .NET 4 ma inne domyślne zachowanie z wyjątkami niskiego poziomu, które mogą uszkodzić stan procesu. Jednak, o ile mogę zrozumieć z tego artykułu, nie ma nic wspominanego o poprzednim zachowaniu CLR, które spowodowałoby całkowite zignorowanie tego wyjątku.

Dlaczego aplikacja .NET 3.5 (lub CLR 2.0) nie wykazuje udokumentowanego zachowania CloseHandle obecnego w .NET 4?

+2

Po pierwsze cieszę się, że MS dokonało pewnych ulepszeń na tej arenie. Od lat borykałem się z problemami: http://social.msdn.microsoft.com/Forums/en-US/adodotnetdataproviders/thread/b5b7a179-3737-4380-b6cf-843f3e71b317/ – Brannon

+0

Może w wersji .NET 4 były po cichu łapie i ignoruje te wyjątki –

+0

Co jest dokładnie tym, co mnie interesuje, aby dowiedzieć się - dlaczego tak być może jest :) – jeffora

Odpowiedz

9

System Windows generuje wyjątek SEH, gdy widzi dołączony debugger. Debugger natywny macierzysty. W debugerze zarządzanym w .NET 4 nastąpiła zmiana, która teraz powoduje, że Windows widzi taki debugger. Nie mogę znaleźć żadnych przyzwoitych linków dokumentujących dokładne szczegóły tego nowego zachowania, przepraszam.

Edytuj: Znalazłem przyzwoity. Bullet 8 na dnie this blog post:

Pod maską mamy zbudowany na rodzimym debugowania rurociągu W trybie v2-compat, ICD nadal właścicielem rurociągu do procesu docelowego (ponieważ to był model V2), ale ten potok nie jest już zbiorem współużytkowanych obiektów IPC z procesem docelowym, ale zamiast tego jest tym samym potokiem, z którego korzysta rodzimy debugger. W szczególności przyłączamy się do procesu, wywołując kernel32! DebugActiveProcess i otrzymujemy nasze zarządzane zdarzenia (rzeczy, które powodują wywołania ICorDebugManagedCallback) za pomocą kernel32! WaitForDebugEvent. Oznacza to również, że kernel32! IsDebuggerPresent zwraca teraz wartość true podczas debugowania zarządzanego. Ma to również pozytywny efekt uboczny uniknięcia problemu z wykonaniem debugowania jądra zarządzanego tylko wtedy, gdy włączony jest debugger jądra (system operacyjny zakłada, że ​​wszelkie instrukcje dotyczące punktów przerwania, które występują, gdy debugger nie jest dołączony, powinny spowodować przerwanie debuggera jądra) .

To nie jest po prostu kosmetyczna poprawka, choć atak recyklingu to coś, co pozwala pracownikom firmy Microsoft obudzić się w nocy. Wersja CLR środowiska .NET 4 została zbudowana w wersji CRT, która sprawdza przepełnienie bufora. Gdy zostanie wykryty, CRT natychmiast kończy program. Bez AppDomain.UnhandledException, to natychmiastowa awaria na pulpicie. Ten kod działa jednak tak samo, jak system Windows, sprawdza rodzimy debugger i generuje punkt przerwania, gdy jest podłączony. Było więc bardzo ważne, że zarządzany debugger zaczął wyglądać jak natywny, jedyny sposób, aby naprawdę zdiagnozować awarię.

+0

To również tłumaczy, dlaczego nagle zaczyna się włamać do .NET 3.5, gdy "Własność kodu debugowania" jest wybrana we właściwościach projektu (o czym dowiedziałem się tamtej nocy, gdy próbowałem ponownie się temu przyjrzeć). Dzięki! – jeffora

+0

Tak, włączenie obsługi niezarządzanego debugowania we wcześniejszych wersjach powoduje, że debugger wywołuje funkcję WaitForDebugEvent(). –

1

Naprawdę nie "dobra" odpowiedź na to pytanie. 3.5 miało błąd w SEH, który został naprawiony w wersji 4.0. Był to wyjątek. Miałem podobny problem i otworzyliśmy na nim bilet z MSFT - ich odpowiedzią była "bugfix".

+0

Czy otworzyłeś żądanie połączenia? Jeśli tak, czy możesz dodać do niego link? –

+0

@JohnSaunders - Hans Passant mówi również, że był błąd, co oznacza, że ​​wystąpił błąd, połączenie z biletem niczego nie udowodni. –

+0

Nie mamy Connect, otworzyliśmy jednorazowy bilet wsparcia. Wygląda na to, że Hans podał szczegóły dotyczące tej poprawki również poniżej. – XeroxDucati

Powiązane problemy