c# .net winapi interop pinvoke

c# - ¿Por qué el manejo de las excepciones de CloseHandle es diferente entre.NET 4 y 3.5?



winapi interop (2)

Estoy encontrando una situación en la que una llamada de CloseHandle a CloseHandle está lanzando una SEHException en una aplicación .NET 4 cuando se ejecuta bajo un depurador. A diferencia de otros que han tenido problemas similares al migrar de 3.5 a 4 , el comportamiento no me molesta particularmente y ya he localizado el problema (una biblioteca de terceros que llama a CloseHandle dos veces en el mismo identificador). Sin embargo, estoy perplejo en cuanto a por qué este comportamiento no ocurre en una aplicación .NET 3.5.

El siguiente ejemplo, pequeño pero completo, muestra el comportamiento que estoy experimentando (probado tanto en XP SP3 como en Win 7 x64, siempre compilado como 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); }

Cuando se ejecuta como una aplicación .NET 4, se lanza una SEHException en el segundo CloseHandle . Según la documentación de CloseHandle , este es el comportamiento esperado:

Si la aplicación se ejecuta bajo un depurador, la función lanzará una excepción si recibe un valor de identificador que no es válido o un valor de pseudo manejador. Esto puede suceder si cierra un identificador dos veces o si llama a CloseHandle en un identificador devuelto por la función FindFirstFile en lugar de llamar a la función FindClose.

Sin embargo, cuando se compila como una aplicación .NET 3.5 (o CLR 2.0), no se produce ninguna excepción en la segunda llamada CloseHandle , y se imprime el mensaje "No exception" .

Según este artículo , el CLR actualizado publicado para .NET 4 tiene un comportamiento predeterminado diferente con excepciones de bajo nivel que pueden corromper el estado del proceso. Sin embargo, por lo que puedo entender de ese artículo, no se menciona nada del comportamiento anterior de CLR que haría que la excepción fuera completamente ignorada.

¿Por qué una aplicación .NET 3.5 (o CLR 2.0) no presenta el comportamiento documentado de CloseHandle que está presente en .NET 4?


No hay una respuesta realmente ''buena'' para esto ... 3.5 tuvo un error en el SEH que se solucionó en 4.0 ... Se estaba comiendo la excepción. Tuve un problema similar y abrimos un ticket con MSFT, su respuesta fue "corrección de errores".


Windows solo genera la excepción SEH cuando ve que hay un depurador adjunto. Un depurador nativo . Hubo un cambio en el depurador administrado de .NET 4 que ahora hace que Windows vea tal depurador. No puedo encontrar ningún enlace decente que documente los detalles exactos de este nuevo comportamiento, lo siento.

Edit: encontré uno decente. La viñeta 8 al final de esta publicación del blog :

Bajo el capó, estamos integrados en la tubería de depuración nativa. En el modo v2-compat, ICD sigue siendo el propietario de la tubería al proceso objetivo (ya que era el modelo V2), pero esa tubería ya no es una colección de objetos IPC compartidos con El proceso de destino, pero en cambio es el mismo canal que utiliza un depurador nativo. Específicamente, nos unimos a un proceso llamando a kernel32! DebugActiveProcess, y obtenemos nuestros eventos administrados (cosas que resultan en llamadas a ICorDebugManagedCallback) usando kernel32! WaitForDebugEvent. Esto también significa que kernel32! IsDebuggerPresent ahora devuelve verdadero cuando se realiza la depuración solo administrada. Esto también tiene el efecto colateral de evitar el problema de realizar la depuración solo administrada cuando se habilita un depurador del kernel (el sistema operativo asume que las instrucciones de punto de interrupción que se producen cuando un depurador no está conectado deberían causar una ruptura en el depurador del kernel).

Esto no es solo una solución cosmética por cierto, aunque el manejo de los ataques de reciclaje es algo que mantiene a los empleados de Microsoft despiertos por la noche. La versión .NET 4 del CLR se construye con la versión CRT que verifica los desbordamientos de búfer. Cuando se detecta uno, el CRT termina inmediatamente el programa. No AppDomain.UnhandledException, es una falla inmediata en el escritorio. Sin embargo, este código hace lo mismo que Windows, busca un depurador nativo y genera un punto de interrupción cuando se adjunta uno. Por lo tanto, era muy importante que el depurador administrado empezara a parecer nativo, la única forma de diagnosticar realmente el bloqueo.