108 votes

Événement de sortie de l'application console .NET

Dans .NET, existe-t-il une méthode, telle qu'un événement, pour détecter la sortie d'une application console ? J'ai besoin de nettoyer certains threads et COM objets.

J'exécute une boucle de messages, sans formulaire, à partir de l'application console. A DCOM que j'utilise semble exiger que l'application pompe des messages.

J'ai essayé d'ajouter un gestionnaire à Process.GetCurrentProcess.Exited et Process.GetCurrentProcess.Disposed.

J'ai également essayé d'ajouter un gestionnaire à Application.ApplicationExit et Application.ThreadExit mais ils ne se déclenchent pas. C'est peut-être parce que je n'utilise pas de formulaire.

2 votes

L'événement Process.GetCurrentProcess().Exited doit se déclencher si vous avez d'abord défini Process.EnableRaisingEvents = true ; sinon, il ne se déclenchera pas.

2 votes

2 votes

@Triynko comment ça se fait ? Process.Exited est déclenché après la sortie du processus. Et si la propre application est quittée, aucun code n'est exécuté. Plutôt inutile, je pense.

116voto

Fredrik Mörk Points 85694

Vous pouvez utiliser le ProcessExit de l'événement AppDomain :

class Program
{
    static void Main(string[] args)
    {
        AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);           
        // do some work

    }

    static void CurrentDomain_ProcessExit(object sender, EventArgs e)
    {
        Console.WriteLine("exit");
    }
}

Mise à jour

Voici un exemple complet de programme avec une "pompe à messages" vide fonctionnant sur un thread séparé, qui permet à l'utilisateur d'entrer une commande quit dans la console pour fermer l'application de manière élégante. Après la boucle dans MessagePump, vous voudrez probablement nettoyer les ressources utilisées par le thread d'une manière agréable. Il est préférable de le faire ici plutôt que dans ProcessExit pour plusieurs raisons :

  • Évitez les problèmes liés à l'inter-filière ; si des objets COM externes ont été créés sur le fil d'exécution de MessagePump, il est plus facile de les traiter à cet endroit.
  • Il y a une limite de temps sur ProcessExit (3 secondes par défaut), donc si le nettoyage prend du temps, il peut échouer s'il est effectué dans ce gestionnaire d'événement.

Voici le code :

class Program
{
    private static bool _quitRequested = false;
    private static object _syncLock = new object();
    private static AutoResetEvent _waitHandle = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
        // start the message pumping thread
        Thread msgThread = new Thread(MessagePump);
        msgThread.Start();
        // read input to detect "quit" command
        string command = string.Empty;
        do
        {
            command = Console.ReadLine();
        } while (!command.Equals("quit", StringComparison.InvariantCultureIgnoreCase));
        // signal that we want to quit
        SetQuitRequested();
        // wait until the message pump says it's done
        _waitHandle.WaitOne();
        // perform any additional cleanup, logging or whatever
    }

    private static void SetQuitRequested()
    {
        lock (_syncLock)
        {
            _quitRequested = true;
        }
    }

    private static void MessagePump()
    {
        do
        {
            // act on messages
        } while (!_quitRequested);
        _waitHandle.Set();
    }

    static void CurrentDomain_ProcessExit(object sender, EventArgs e)
    {
        Console.WriteLine("exit");
    }
}

11 votes

Merci pour la suggestion Fredrik. Cet événement ne semble pas se déclencher non plus.

0 votes

Comment votre processus se termine-t-il ? Appelez-vous un code qui le fait sortir ?

0 votes

Actuellement, la seule façon de sortir est de cliquer sur le bouton de fermeture de la fenêtre de la console ou d'appuyer sur ctrl+c.

62voto

JJ_Coder4Hire Points 301

Voici une solution .NET complète, très simple, qui fonctionne dans toutes les versions de Windows. Il suffit de la coller dans un nouveau projet, de l'exécuter et de l'essayer. Ctrl + C pour voir comment il le traite :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace TestTrapCtrlC{
    public class Program{
        static bool exitSystem = false;

        #region Trap application termination
        [DllImport("Kernel32")]
        private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);

        private delegate bool EventHandler(CtrlType sig);
        static EventHandler _handler;

        enum CtrlType {
         CTRL_C_EVENT = 0,
         CTRL_BREAK_EVENT = 1,
         CTRL_CLOSE_EVENT = 2,
         CTRL_LOGOFF_EVENT = 5,
         CTRL_SHUTDOWN_EVENT = 6
         }

        private static bool Handler(CtrlType sig) {
            Console.WriteLine("Exiting system due to external CTRL-C, or process kill, or shutdown");

            //do your cleanup here
            Thread.Sleep(5000); //simulate some cleanup delay

            Console.WriteLine("Cleanup complete");

            //allow main to run off
             exitSystem = true;

            //shutdown right away so there are no lingering threads
            Environment.Exit(-1);

            return true;
        }
        #endregion

        static void Main(string[] args) {
            // Some biolerplate to react to close window event, CTRL-C, kill, etc
            _handler += new EventHandler(Handler);
            SetConsoleCtrlHandler(_handler, true);

            //start your multi threaded program here
            Program p = new Program();
            p.Start();

            //hold the console so it doesn’t run off the end
            while(!exitSystem) {
                Thread.Sleep(500);
            }
        }

        public void Start() {
            // start a thread and start doing some processing
            Console.WriteLine("Thread started, processing..");
        }
    }
 }

3 votes

Cela semble être la meilleure solution C# qui fonctionne pour tous les scénarios de sortie ! Elle fonctionne parfaitement ! Merci :)

1 votes

Le code ne fonctionne pas sous Windows xp sp3. Si nous fermons l'application à partir du gestionnaire de tâches.

8 votes

Ce code fonctionnera-t-il sous Linux dans une application console .NET Core ? Je n'ai pas essayé mais "Kernel32" ne me semble pas portable...

21voto

user79755 Points 995

L'application est un serveur qui fonctionne simplement jusqu'à ce que le système s'éteigne ou qu'il reçoive un message d'erreur. Ctrl + C ou la fenêtre de la console est fermée.

En raison de la nature extraordinaire de l'application, il n'est pas possible d'effectuer une sortie "gracieuse". (Il se peut que je puisse coder une autre application qui enverrait un message "d'arrêt du serveur", mais ce serait trop pour une seule application et encore insuffisant pour certaines circonstances comme lorsque le serveur (en fait le système d'exploitation) s'arrête réellement).

En raison de ces circonstances, j'ai ajouté un " ConsoleCtrlHandler " où j'arrête mes threads et nettoie mes objets COM, etc...

Public Declare Auto Function SetConsoleCtrlHandler Lib "kernel32.dll" (ByVal Handler As HandlerRoutine, ByVal Add As Boolean) As Boolean

Public Delegate Function HandlerRoutine(ByVal CtrlType As CtrlTypes) As Boolean

Public Enum CtrlTypes
  CTRL_C_EVENT = 0
  CTRL_BREAK_EVENT
  CTRL_CLOSE_EVENT
  CTRL_LOGOFF_EVENT = 5
  CTRL_SHUTDOWN_EVENT
End Enum

Public Function ControlHandler(ByVal ctrlType As CtrlTypes) As Boolean
.
.clean up code here
.
End Function

Public Sub Main()
.
.
.
SetConsoleCtrlHandler(New HandlerRoutine(AddressOf ControlHandler), True)
.
.
End Sub

Cette configuration semble fonctionner parfaitement. Voici un lien à du code C# pour la même chose.

1 votes

Merci, ça m'a aidé :) +1 Vous devez marquer votre propre réponse comme étant LA réponse.

4 votes

Il semble qu'il y ait une limite de 3 secondes pour que le manipulateur termine. Si vous ne terminez pas à temps, vous serez disqualifié sans ménagement. Cela vaut également si vous êtes assis sur un point d'arrêt dans le débogueur !

0 votes

Cela fonctionne mais un exemple complet de fonctionnement se trouve ci-dessous en C#

11voto

EricLaw Points 28850

Pour le cas CTRL+C, vous pouvez utiliser ceci :

// Tell the system console to handle CTRL+C by calling our method that
// gracefully shuts down.
Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);

static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
            Console.WriteLine("Shutting down...");
            // Cleanup here
            System.Threading.Thread.Sleep(750);
}

0 votes

Est-ce que cela gère l'arrêt de la connexion ctrl+c etc... ?

1 votes

Je l'ai essayé, l'événement ne se déclenche jamais. Comme pour les autres événements.

2voto

Miky Dinescu Points 22380

Si vous utilisez une application console et que vous pompez des messages, vous pouvez peut-être utiliser le message WM_QUIT.

1 votes

Je m'interroge aussi à ce sujet. Je suppose que pourrait Il se peut que Windows n'envoie pas le message à une application console, mais cela me semble bizarre. On pourrait penser que l'exigence est d'avoir une pompe à messages, et non pas de savoir si elle a une interface graphique ou non...

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