133 votes

Pourquoi Environment.Exit() ne termine-t-il plus le programme ?

J'ai découvert ce phénomène il y a quelques jours et j'ai eu la confirmation qu'il n'était pas limité à ma machine grâce à cette question .

La façon la plus simple de le reproduire est de démarrer une application Winforms, d'ajouter un bouton et d'écrire ce code :

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

Le programme échoue après l'instruction Exit() s'exécute. Sur Winforms, vous obtenez "Error creating window handle".

L'activation du débogage non géré permet de comprendre ce qui se passe. La boucle modale COM s'exécute et permet la délivrance d'un message WM_PAINT. C'est fatal sur un formulaire disposé.

Les seuls faits que j'ai recueillis jusqu'à présent sont :

  • Ce n'est pas seulement limité à l'exécution avec le débogueur. Cela échoue également sans celui-ci. La boîte de dialogue de plantage WER s'affiche également de manière assez médiocre. deux fois .
  • Cela n'a rien à voir avec le bitness du processus, la couche wow64 est assez connue mais une construction AnyCPU se plante de la même façon.
  • Cela n'a rien à voir avec la version .NET, les versions 4.5 et 3.5 se plantent de la même façon.
  • Le code de sortie n'a pas d'importance.
  • Appeler Thread.Sleep() avant d'appeler Exit() ne résout pas le problème.
  • Cela se produit sur la version 64 bits de Windows 8, Windows 7 ne semble pas être affecté de la même manière.
  • Ce comportement doit être relativement nouveau, je ne l'ai jamais vu auparavant. Je ne vois aucune mise à jour pertinente fournie par Windows Update, bien que l'historique des mises à jour ne soit plus exact sur ma machine.
  • Il s'agit d'une rupture flagrante du comportement, vous écrivez un code comme celui-ci dans un gestionnaire d'événement pour AppDomain.UnhandledException et il se plante de la même manière.

Je suis particulièrement intéressé par ce que vous pourriez faire pour éviter cet accident. En particulier, le scénario AppDomain.UnhandledException me laisse perplexe, il n'y a pas beaucoup de façons de mettre fin à un programme .NET. Veuillez noter que l'appel à Application.Exit() ou Form.Close() n'est pas valable dans un gestionnaire d'événements pour UnhandledException et qu'il ne s'agit donc pas de solutions de rechange.


MISE À JOUR : Mehrdad a signalé que le fil de discussion du finisseur pourrait faire partie du problème. Je pense que c'est ce que je vois, et je vois aussi des preuves du délai de 2 secondes que le CLR donne au thread finalizer pour terminer l'exécution.

Le finalisateur se trouve dans NativeWindow.ForceExitMessageLoop(). Il y a une fonction winapi IsWindow() qui correspond à peu près à l'emplacement du code, offset 0x3c en regardant le code machine en mode 32 bits. Il semble que IsWindow() soit en train de se bloquer. Je ne peux pas obtenir une bonne trace de la pile pour les internes cependant, le débogueur pense que l'appel pinvoke vient de retourner. C'est difficile à expliquer. Si vous pouvez obtenir une meilleure trace de pile, j'aimerais la voir. Le mien :

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

Rien au-dessus de l'appel ForceExitMessageLoop, débogueur non géré activé.

83voto

Hans Passant Points 475940

J'ai contacté Microsoft à propos de ce problème et cela semble avoir porté ses fruits. Du moins, j'aime à le penser :). Bien que je n'aie pas reçu de confirmation de résolution de leur part, le groupe Windows est difficile à contacter directement et j'ai dû passer par un intermédiaire.

Une mise à jour effectuée via Windows Update a résolu le problème. Le délai de 2 secondes avant le plantage n'est plus présent, ce qui suggère fortement que le blocage de IsWindow() a été résolu. Et le programme s'arrête proprement et de manière fiable. La mise à jour a installé des correctifs pour Windows Defender, wdboot.sys, wdfilter.sys, tcpip.sys, rpcrt4.dll, uxtheme.dll, crypt32.dll et wintrust.dll.

Uxtheme.dll est l'intrus, il met en œuvre l'api de thématisation de Visual Styles et est utilisé par ce programme de test. Je ne peux pas en être sûr mais je parie que c'est la source du problème. La copie dans c : \windows\system32 a pour numéro de version 6.2.9200.16660, créé le 14 août 2013 sur ma machine.

Affaire classée.

50voto

Mehrdad Points 70493

Je ne sais pas pourquoi ça ne marche pas. "plus" mais je pense que Environment.Exit exécute les finaliseurs en attente. Environment.FailFast ne le fait pas.

Il se peut que (pour une raison bizarre) vous ayez des finaliseurs en attente qui doivent être exécutés après, ce qui provoque ce problème.

6voto

Joe Points 60749

Ça n'explique pas pourquoi ça se produit, mais je n'appellerais pas ça de l'aide. Environment.Exit dans un gestionnaire d'événement de bouton comme votre exemple - au lieu de fermer le formulaire principal comme suggéré dans réponse de rene .

Quant à un AppDomain.UnhandledException vous pourriez peut-être simplement définir Environment.ExitCode plutôt que d'appeler Environment.Exit .

Je ne suis pas sûr de ce que vous essayez d'accomplir ici, pourquoi voulez-vous retourner un code de sortie d'une application WinForms ? Normalement, les codes de sortie sont utilisés par les applications de la console.

Je suis particulièrement intéressé par ce que vous pourriez faire pour éviter cette panne. L'appel à Environment.Exit() est nécessaire pour empêcher l'affichage de la boîte de dialogue WER.

Avez-vous un try/catch dans la méthode Main ? Pour les applications WinForms, j'ai toujours un try/catch autour de la boucle de message ainsi que des gestionnaires d'exceptions non gérées.

4voto

J'ai trouvé le même problème dans notre application, nous l'avons résolu avec la construction suivante :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X