2010-11-16 20 views
18

Ok, este es un tema un poco extraño y espero que alguien pueda arrojar algo de luz. Tengo el siguiente código:Finalmente el bloque no se está ejecutando?

static void Main(string[] args) 
{ 
    try 
    { 
     Console.WriteLine("in try"); 
     throw new EncoderFallbackException(); 
    } 
    catch (Exception) 
    { 
     Console.WriteLine("in Catch"); 
     throw new AbandonedMutexException(); 
    } 
    finally 
    { 
     Console.WriteLine("in Finally"); 
     Console.ReadLine(); 
    } 
} 

ahora cuando compilar este objetivo a 3,5 (2,0 CLR) se abrirá una ventana que dice "XXX ha dejado de funcionar". Si ahora hago clic en el botón Cancelar, ejecutaré finalmente, Y si espero hasta que termine de buscar y haga clic en el botón Cerrar programa, también ejecutará finalmente.

Ahora lo que es interesante y confuso es que si hago lo mismo compilado con 4.0 Al hacer clic en el botón Cancelar se ejecutará el bloque finally y haciendo clic en el botón Cerrar programa no lo hará.

Mi pregunta es: ¿Por qué finalmente se ejecuta en 2.0 y no en 4.0 cuando se pulsa el botón Cerrar programa? ¿Cuáles son las repercusiones de esto?

EDITAR: Estoy ejecutando esto desde un símbolo del sistema en modo de lanzamiento (construido en modo de lanzamiento) en Windows 7 de 32 bits. Mensaje de error: El primer resultado a continuación se ejecuta en 3.5 al acercarse cuando Windows busca el problema, segundo es cuando lo ejecuto en 4.0 y hago lo mismo.

alt text

+4

Cuando compilo esto con .NET 4 y lo ejecuto, obtengo el diálogo estándar de Informes de errores de Windows; si cierro esto o hago clic en "No enviar", la consola imprime "en Finalmente". Parece que funciona bien! –

+1

¿Está ejecutando esto desde Visual Studio? Intente ejecutarlo manualmente desde el símbolo del sistema. –

+0

También podría haber una diferencia entre los sistemas 64/32. – Kobi

Respuesta

2

Creo que esto tiene algo que ver con cambios en la forma se adjunta el depurador.

Del documento .NET Framework 4 Migration Issues:

Ya no son notificados cuando el depurador no se puede iniciar, o cuando no hay un depurador registrada que debe iniciarse.

Lo que pasa es que elige iniciar el depurador, pero lo cancela. Creo que esto cae dentro de esta categoría y la aplicación simplemente se detiene debido a esto.

+0

El problema parece ocurrir sin ningún tipo de depurador. –

+0

Gracias por la información, no lo sé, pero solo estoy ejecutando esto desde el símbolo del sistema –

+0

El mensaje de error muestra el número de línea, sugiriendo que tiene el archivo pdb en el mismo directorio. No estoy seguro de que sea importante, no puedo reproducirlo de ninguna manera. – Kobi

20

Puedo reproducir el comportamiento ahora (no recibí los pasos exactos de su pregunta cuando lo leía la primera vez).

Una diferencia que puedo observar es la forma en que el tiempo de ejecución de .NET maneja la excepción no controlada. El CLR 2.0 ejecuta un asistente llamado Informe de errores de Microsoft .NET Shim (dw20.exe) mientras que el CLR 4.0 inicia Informe de errores de Windows (WerFault.exe).

Supongo que los dos tienen un comportamiento diferente con respecto a la finalización del proceso de bloqueo. WerFault.exe obviamente mata el proceso .NET inmediatamente mientras que .NET Error Reporting Shim de alguna manera cierra la aplicación para que el bloque finally aún se ejecute.

también echar un vistazo al Visor de sucesos: WerFault registra un error de aplicación notifica que el proceso se terminó estrellado:

 
Application: ConsoleApplication1.exe 
Framework Version: v4.0.30319 
Description: The process was terminated due to an unhandled exception. 
Exception Info: System.Threading.AbandonedMutexException 
Stack: 
    at Program.Main(System.String[]) 

dw20.exe sin embargo, sólo registra un elemento de información con el identificador de evento 1001 en el registro de eventos y no termina el proceso.

+0

¡Esta es una gran respuesta! –

0

Lo ejecuté tanto en versión como en depuración, tanto en el framework 3.5 como en el 4.0, veo "in Finally" en todas las instancias, sí ejecutándolo desde la línea de comando, llegué a cerrar mis sesiones vs, tal vez sea algo en su máquina o como señaló Kobi, tal vez relacionada con la plataforma (estoy en Win7 x64)

9

Piensen en lo horrible que es esta situación: algo inesperado ha sucedido y nadie ha escrito código para manejar. Es lo que hay que hacer en esa situación al ejecutar aún más el código, que probablemente fue también ¿no está diseñado para manejar esta situación? Posiblemente no. A menudo, lo que se debe hacer aquí es no intentar ejecutar los bloques finally porque hacerlo empeorará la situación. Ya sabes que el proceso está descendiendo; sacarlo de su miseria de inmediato.

En un escenario en el que una excepción no controlada va a acabar con el proceso, puede pasar cualquier cosa. Está definido por la implementación lo que sucede en este caso: si el error se informa al informe de errores de Windows, si se inicia un depurador, y así sucesivamente. El CLR está perfectamente dentro de sus derechos para intentar ejecutar finalmente los bloques, y también está perfectamente dentro de sus derechos de fallar rápidamente. En este escenario, todas las apuestas están apagadas; diferentes implementaciones pueden elegir hacer cosas diferentes.

6

Todo mi conocimiento sobre este tema está tomado de este artículo aquí: http://msdn.microsoft.com/en-us/magazine/cc793966.aspx - tenga en cuenta que está escrito para .NET 2.0, pero tengo la sensación de que tiene sentido para lo que estábamos experimentando en este caso (más que "porque decidido" de todos modos)

rápida 'no tengo tiempo para leer ese artículo' respuesta (aunque se debe, que es una muy buena):

la solución al problema (si usted absolutamente tiene que tener su finalmente bloquea ejecutar) sería a) poner un controlador de error global o b) forzar .NET para ejecutar finalmente bloques y hacer las cosas de la forma en que lo hizo (posiblemente de manera incorrecta) en .NET 1.1 - Coloque lo siguiente en su aplicación .config:

<legacyUnhandledExceptionPolicy enabled="1"> 

La razón para ello: Cuando se produce una excepción en .NET que empieza a caminar hacia atrás a través de la pila en busca de controladores de excepciones y cuando lo encuentra, entonces hace un segundo camino de vuelta a través de la pila en funcionamiento bloques finally antes de ejecutar el contenido de la captura. Si no encuentra una captura, esta segunda caminata nunca ocurre, por lo que los bloques finally nunca se ejecutan aquí, por lo que un manejador global de excepciones siempre ejecutará cláusulas finally ya que la CLR las ejecutará cuando encuentre la captura, NO cuando la ejecute (lo cual creo significa que incluso si haces una captura/lanzamiento tus bloques finalmente se ejecutarán).

La razón por la que la reparación de app.config funciona es porque para .NET 1.0 y 1.1 la CLR tenía una captura global que se tragaría Excepciones antes de que no se administraran, lo que, por supuesto, activaría los bloques finalmente correr. Por supuesto, no hay forma de que el marco pueda saber lo suficiente sobre dicha Excepción para manejarlo, por ejemplo, un desbordamiento de pila, por lo que esta es probablemente la forma incorrecta de hacerlo.

El siguiente paso es donde se pone un poco pegajoso, y estoy haciendo suposiciones basadas en lo que dice el artículo aquí.

Si se encuentra en .NET 2.0+ sin el manejo de excepciones heredado entonces su excepción caería en el sistema de manejo de excepciones de Windows (SEH) que parece bastante similar al CLR, en el sentido de que retrocede a través de marcos hasta que no encuentra una captura y luego llama a una serie de eventos llamados el Filtro de excepción no controlada (UEF).Este es un evento al que puede suscribirse, pero solo puede tener UNA cosa suscrita a la vez, de modo que cuando algo se suscribe, Windows le entrega la dirección de la devolución de llamada que estaba allí antes, lo que le permite configurar una cadena de UEF. manejadores - PERO NO TIENEN QUE HONRAR esa dirección, deben llamar ellos mismos a la dirección, pero si uno rompe la cadena, bap, no obtendrá más manejo de errores. Supongo que esto es lo que sucede cuando cancelas el informe de errores de Windows, rompe la cadena UEF, lo que significa que la aplicación se cierra inmediatamente y los bloques finalmente no se ejecutan, sin embargo, si lo dejas correr hasta el final y lo cierras, llamará al próximo UEF en la cadena. .NET habrá registrado uno que es de lo que se llama el AppDomain.UnhandledException (por lo tanto, incluso este evento no está garantizado), que supongo que también es el lugar desde donde se obtienen los bloques finalmente llamados, ya que no puedo ver cómo pasar nunca. de nuevo en el CLR puede ejecutarse un bloque administrado finalmente (el artículo no entra en este bit)

+3

momento OCD serio allí ... Debería haber salido de la oficina hace 3 horas. –

Cuestiones relacionadas