¿Por qué Environment.Exit () ya no termina el programa?

134

Esto es algo que descubrí hace unos días, recibí la confirmación de que esta pregunta no solo se limita a mi máquina .

La forma más fácil de reproducirlo es iniciando una aplicación de formularios Windows Forms, agregue un botón y escriba este código:

    private void button1_Click(object sender, EventArgs e) {
        MessageBox.Show("yada");
        Environment.Exit(1);         // Kaboom!
    }

El programa falla después de que se ejecuta la instrucción Exit (). En Windows Forms aparece "Error al crear el identificador de ventana".

Habilitar la depuración no administrada deja en claro lo que está sucediendo. El bucle modal COM se está ejecutando y permite que se entregue un mensaje WM_PAINT. Eso es fatal en una forma dispuesta.

Los únicos hechos que he reunido hasta ahora son:

  • No se limita solo a ejecutar con el depurador. Esto también falla sin uno. También bastante mal, el cuadro de diálogo de bloqueo WER aparece dos veces .
  • No tiene nada que ver con la brevedad del proceso. La capa wow64 es bastante notoria, pero una compilación AnyCPU se bloquea de la misma manera.
  • No tiene nada que ver con la versión .NET, 4.5 y 3.5 se bloquean de la misma manera.
  • El código de salida no importa.
  • Llamar a Thread.Sleep () antes de llamar a Exit () no lo soluciona.
  • Esto sucede en la versión de 64 bits de Windows 8, y Windows 7 no parece verse afectado de la misma manera.
  • Este debería ser un comportamiento relativamente nuevo, no he visto esto antes. No veo actualizaciones relevantes entregadas a través de Windows Update , aunque el historial de actualizaciones ya no es preciso en mi máquina.
  • Este es un comportamiento grosero. Escribiría un código como este en un controlador de eventos para AppDomain.UnhandledException, y se bloquea de la misma manera.

Estoy particularmente interesado en lo que podrías hacer para evitar este bloqueo. Particularmente el escenario AppDomain.UnhandledException me desconcierta; No hay muchas maneras de terminar un programa .NET. Tenga en cuenta que llamar a Application.Exit () o Form.Close () no son válidos en un controlador de eventos para UnhandledException, por lo que no son soluciones alternativas.


ACTUALIZACIÓN: Mehrdad señaló que el hilo finalizador podría ser parte del problema. Creo que estoy viendo esto y también estoy viendo algunas pruebas para el tiempo de espera de 2 segundos que el CLR le da al hilo finalizador para finalizar la ejecución.

El finalizador está dentro de NativeWindow.ForceExitMessageLoop (). Hay una función Win32 de IsWindow () que corresponde aproximadamente con la ubicación del código, compensa 0x3c cuando se mira el código de la máquina en modo de 32 bits. Parece que IsWindow () está bloqueado. Sin embargo, no puedo obtener un buen seguimiento de la pila para las partes internas, el depurador cree que la llamada P / Invoke acaba de regresar. Esto es difícil de explicar. Si puede obtener un mejor seguimiento de la pila, me encantaría verlo. Mía:

System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.ForceExitMessageLoop() + 0x3c bytes
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Finalize() + 0x16 bytes
[Native to Managed Transition]
kernel32.dll!@BaseThreadInitThunk@12()  + 0xe bytes
ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes
ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes

Nada por encima de la llamada ForceExitMessageLoop, depurador no administrado habilitado.

Hans Passant
fuente
2
Acabo de probar esto con .NET 4, 4 Client Profile, 3.5, 3.5 Client Profile, 3.0 y 2.0, y no recibí ningún error en ninguno de ellos. Windows 7 de 64 bits es mi sistema operativo, usando VS2010.
Steve
2
¡@Steve This happens on the 64-bit version of Windows 8Hans lo ha dicho!
Parimal Raj
77
Puedo reprobar esto (Win 8, 64 bits), copiar / pegar su código y conectar un botón y obtengo los síntomas exactos descritos.
keyboardP
3
Una aplicación en modo consola no pudo demostrar este problema, nada puede salir mal cuando Exit () sigue bombeando mensajes.
Hans Passant
3
Me encontré con este tipo de comportamiento Exit(0)hace un poco con algunos Win7 de 64 bits, el cambio ExitCodeno ayuda ahora a usar Process.GetCurrentProcess().Kill()sin ningún problema, funciona
Sriram Sakthivel

Respuestas:

85

Me puse en contacto con Microsoft sobre este problema y parecía haber valido la pena. Al menos me gustaría pensar que sí :). Aunque no obtuve una confirmación de una resolución de ellos, el grupo de Windows es difícil de contactar directamente y tuve que usar un intermediario.

Una actualización entregada a través de Windows Update resolvió el problema. El notable retraso de 2 segundos antes del bloqueo ya no está presente, lo que sugiere que se resolvió el punto muerto IsWindow (). Y el programa se cierra de manera limpia y confiable. La actualización instaló parches para Windows Defender, wdboot.sys, wdfilter.sys, tcpip.sys, rpcrt4.dll, uxtheme.dll, crypt32.dll y wintrust.dll

Uxtheme.dll es el pato extraño. Implementa la API de temas de estilos visuales y es utilizada por este programa de prueba. No puedo estar seguro, pero mi dinero está en eso como la fuente del problema. La copia en C: \ WINDOWS \ system32 tiene el número de versión 6.2.9200.16660, creado el 14 de agosto de 2013 en mi máquina.

Caso cerrado.

Hans Passant
fuente
11
El historial de Windows Update ya no es preciso en mi máquina. Todo lo que sé es que se instaló el 14 de agosto.
Hans Passant
51

No sé por qué ya no funciona "más" , pero creo que Environment.Exitejecuta finalizadores pendientes. Environment.FailFastno lo hace

Es posible que (por alguna extraña razón) tenga finalizadores pendientes extraños que deben ejecutarse después, lo que hace que esto suceda.

usuario541686
fuente
2
Puede que tengas algo. El finalizador está ocupado ejecutando NativeWindow.ForceExitMessageLoop (). Curiosamente no está anidado en ninguna llamada.
Hans Passant
@HansPassant: Desearía poder reprobar el problema para poder investigarlo, pero no puedo. ¿Está la llamada NativeWindow.ForceExitMessageLoopatascada en código administrado o no administrado? ¿Está atascado o está ocupado esperando o esperando un mensaje u otra cosa?
user541686
Esto ciertamente parece apuntar al problema central. Creo que es la función winapi IsWindow () la que está en la raíz del problema. Creo que también estoy viendo el tiempo de espera de 2 segundos en el hilo finalizador, después de lo cual todo se va al infierno. El depurador no lo muestra ejecutando la llamada IsWindow () pero he visto a Windows jugar trucos con la pila antes, apagándola al ingresar código crítico dentro de Windows.
Hans Passant
44
Creo que el método Environment.FailFast (), para el caso dado de excepciones no controladas, es probablemente el mejor método para usar de todos modos. (No lo sabía, ¡gracias!) Sin embargo, hay un montón de código heredado que usaría Environment.Exit (), que desafortunadamente se bloqueará incómodamente :(
Ian Yates
2
Definitivamente estás en algo. En mi caso, había comenzado un IHost usando IHost.StartAsync para realizar algunas pruebas de integración y, sin embargo, después de llamar (y, por supuesto, en espera) a IHost.StopAsync, el proceso aún no finalizó. Solo después de llamar a IHost.Dispose, el proceso finaliza. Gracias por el consejo
Malte R
6

Esto no explica por qué está sucediendo, pero no llamaría Environment.Exita un controlador de eventos de botón como su muestra; en cambio, cierre el formulario principal como se sugiere en la respuesta de René .

En cuanto a un AppDomain.UnhandledExceptioncontrolador, tal vez podría configurar en Environment.ExitCodelugar de llamar Environment.Exit.

No estoy seguro de lo que estás tratando de lograr aquí. ¿Por qué desea devolver un código de salida de una aplicación de formularios Windows Forms? Normalmente, los códigos de salida son utilizados por las aplicaciones de consola.

Estoy particularmente interesado en lo que podría hacer para evitar este bloqueo. Calling Environment. La salida () es necesaria para evitar que se muestre el cuadro de diálogo WER.

¿Tienes un try / catch en el método Main? Para las aplicaciones de Windows Forms, siempre tengo un try / catch en el bucle de mensajes, así como los controladores de excepciones no controlados.

Joe
fuente
Estoy bastante seguro de que debes llamar en Application.Exitlugar de Environment.Exit.
user541686
77
Lo sentimos, esta no es una solución alternativa. Se requiere llamar a Environment.Exit () para evitar que se muestre el cuadro de diálogo WER. Tenga en cuenta el "hecho conocido" también, el código de salida no importa.
Hans Passant
77
@Hans: ¿está atrapando AppDomain.UnhandledException para intentar evitar el diálogo WER legítimo en primer lugar? Quiero decir, si hay una excepción no controlada, se supone que se mostrará el cuadro de diálogo WER , ¿no?
Harry Johnston
2

He encontrado el mismo problema en nuestra aplicación, lo hemos resuelto con la siguiente construcción:

Environment.ExitCode=1;
Application.Exit();
Jesse
fuente