it-swarm.com.de

.NET Console Application-Exit-Ereignis

Gibt es in .NET eine Methode, z. B. ein Ereignis, um zu erkennen, wann eine Konsolenanwendung beendet wird? Ich muss einige Threads und COM-Objekte bereinigen.

In der Konsolenanwendung wird eine Nachrichtenschleife ohne Formular ausgeführt. Eine DCOM-Komponente, die ich verwende, scheint zu erfordern, dass die Anwendung Nachrichten pumpt.

Ich habe versucht, einen Handler zu Process.GetCurrentProcess.Exited und Process.GetCurrentProcess.Disposed hinzuzufügen.

Ich habe auch versucht, Application.ApplicationExit- und Application.ThreadExit-Ereignissen einen Handler hinzuzufügen, aber sie werden nicht ausgelöst. Vielleicht liegt das daran, dass ich kein Formular verwende.

63
user79755

Sie können das ProcessExit -Ereignis des AppDomain verwenden:

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");
    }
}

Update

Hier ist ein vollständiges Beispielprogramm mit einer leeren "Nachrichtenpumpe", die auf einem separaten Thread ausgeführt wird, mit dem der Benutzer einen Befehl zum Beenden in der Konsole eingeben kann, um die Anwendung ordnungsgemäß zu schließen. Nach der Schleife in MessagePump möchten Sie wahrscheinlich die vom Thread verwendeten Ressourcen auf nette Weise bereinigen. Dies ist aus mehreren Gründen besser als in ProcessExit:

  • Vermeiden Sie Cross-Threading-Probleme. Wenn im MessagePump-Thread externe COM-Objekte erstellt wurden, ist es dort einfacher, mit ihnen umzugehen.
  • ProcessExit ist zeitlich begrenzt (standardmäßig 3 Sekunden). Wenn die Bereinigung also zeitaufwändig ist, kann sie fehlschlagen, wenn sie in diesem Ereignishandler ausgeführt wird.

Hier ist der 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");
    }
}
62
Fredrik Mörk

Hier ist eine vollständige, sehr einfache .NET-Lösung, die in allen Windows-Versionen funktioniert. Fügen Sie es einfach in ein neues Projekt ein, führen Sie es aus und versuchen Sie STRG-C, um zu sehen, wie es damit umgeht:

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..");
        }
    }
 }
28
JJ_Coder4Hire

Die Anwendung ist ein Server, der einfach so lange ausgeführt wird, bis das System heruntergefahren wird oder Strg + C eingeht oder das Konsolenfenster geschlossen wird.

Aufgrund des außergewöhnlichen Charakters der Anwendung ist ein "ordnungsgemäßes" Beenden nicht möglich. (Es kann sein, dass ich eine andere Anwendung codieren könnte, die eine Meldung zum Herunterfahren des Servers sendet, dies jedoch für eine Anwendung zu viel ist und für bestimmte Umstände, z. B. wenn der Server (tatsächliches Betriebssystem) tatsächlich heruntergefahren wird, immer noch nicht ausreicht.)

Aufgrund dieser Umstände habe ich ein " ConsoleCtrlHandler " hinzugefügt, in dem ich meine Threads anhalte und meine COM-Objekte usw. bereinige.


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

Dieses Setup scheint perfekt zu funktionieren. Hier ist ein Link zu einem C # -Code für die gleiche Sache.

17
user79755

Für den Fall STRG + C können Sie Folgendes verwenden:

// 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);
}
8
EricLaw

Können Sie die WM_QUIT-Nachricht nicht verwenden, wenn Sie eine Konsolenanwendung verwenden und Nachrichten senden?

1
Mike Dinescu