it-swarm.com.de

Capture Console-Exit C #

Ich habe eine Konsolenanwendung, die ziemlich viele Threads enthält. Es gibt Threads, die bestimmte Bedingungen überwachen und das Programm beenden, wenn sie wahr sind. Diese Kündigung kann jederzeit erfolgen.

Ich brauche ein Ereignis, das ausgelöst werden kann, wenn das Programm geschlossen wird, sodass ich alle anderen Threads bereinigen und alle Dateihandles und Verbindungen ordnungsgemäß schließen kann. Ich bin mir nicht sicher, ob bereits eine in .NET Framework integriert ist. Daher frage ich, bevor ich meine eigene schreibe.

Ich habe mich gefragt, ob es ein Ereignis wie folgt gab:

MyConsoleProgram.OnExit += CleanupBeforeExit;
83
ZeroKelvin

Ich bin nicht sicher, wo ich den Code im Internet gefunden habe, aber ich habe ihn jetzt in einem meiner alten Projekte gefunden. Auf diese Weise können Sie den Bereinigungscode in Ihrer Konsole ausführen, z. wenn es plötzlich geschlossen wird oder aufgrund eines Herunterfahrens ...

[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)
{
  switch (sig)
  {
      case CtrlType.CTRL_C_EVENT:
      case CtrlType.CTRL_LOGOFF_EVENT:
      case CtrlType.CTRL_SHUTDOWN_EVENT:
      case CtrlType.CTRL_CLOSE_EVENT:
      default:
          return false;
  }
}


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

Update

Für diejenigen, die die Kommentare nicht überprüfen, scheint es, dass diese spezielle Lösung nicht auf Windows 7 gut (oder überhaupt) funktioniert. Der folgende Thread spricht darüber

89
flq

Voll funktionsfähiges Beispiel, arbeitet mit Strg-C und schließt die Fenster mit X und Kill:

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

Überprüfen Sie auch:

AppDomain.CurrentDomain.ProcessExit
7
jmservera

Es hört sich an, als hätten die Threads die Anwendung direkt beendet? Vielleicht wäre es besser, wenn ein Thread dem Haupt-Thread signalisiert, dass die Anwendung beendet werden soll.

Bei Erhalt dieses Signals kann der Haupt-Thread die anderen Threads sauber abschalten und sich schließlich schließen.

3
Rob

Es gibt für WinForms Apps;

Application.ApplicationExit += CleanupBeforeExit;

Versuchen Sie es für Konsolen-Apps

AppDomain.CurrentDomain.DomainUnload += CleanupBeforeExit;

Ich bin mir jedoch nicht sicher, an welchem ​​Punkt das aufgerufen wird oder ob es von der aktuellen Domäne aus funktioniert. Ich vermute nicht.

3
Rob Prouse

Ich hatte ein ähnliches Problem, nur meine Konsolen-App würde in einer Endlosschleife mit einer präventiven Anweisung in der Mitte ausgeführt. Hier ist meine Lösung:

class Program
{
    static int Main(string[] args)
    {
        // Init Code...
        Console.CancelKeyPress += Console_CancelKeyPress;  // Register the function to cancel event

        // I do my stuffs

        while ( true )
        {
            // Code ....
            SomePreemptiveCall();  // The loop stucks here wating function to return
            // Code ...
        }
        return 0;  // Never comes here, but...
    }

    static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
    {
        Console.WriteLine("Exiting");
        // Termitate what I have to terminate
        Environment.Exit(-1);
    }
}
3
João Portela

Die Antwort von ZeroKelvin funktioniert in der Konsolen-App Windows 10 x64, .NET 4.6. Für diejenigen, die sich nicht mit der CtrlType-Enumeration beschäftigen müssen, gibt es eine wirklich einfache Möglichkeit, sich in das Herunterfahren des Frameworks einzumischen: 

class Program
{
    private delegate bool ConsoleCtrlHandlerDelegate(int sig);

    [DllImport("Kernel32")]
    private static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandlerDelegate handler, bool add);

    static ConsoleCtrlHandlerDelegate _consoleCtrlHandler;

    static void Main(string[] args)
    {
        _consoleCtrlHandler += s =>
        {
            //DoCustomShutdownStuff();
            return false;   
        };
        SetConsoleCtrlHandler(_consoleCtrlHandler, true);
    }
}

Die Rückgabe von FALSE vom Handler teilt dem Framework mit, dass wir das Steuersignal nicht "handhaben" und die nächste Handlerfunktion in der Liste der Handler für diesen Prozess verwendet wird. Wenn keiner der Handler TRUE zurückgibt, wird der Standardhandler aufgerufen.

Wenn der Benutzer eine Abmeldung oder ein Herunterfahren durchführt, wird der Rückruf nicht von Windows aufgerufen, sondern sofort beendet. 

1
BCA

Das link von Charle B im Kommentar zu flq erwähnt

Tief unten sagt: 

SetConsoleCtrlHandler funktioniert nicht unter Windows7, wenn Sie eine Verknüpfung zu User32 herstellen

An anderen Stellen im Thread wird empfohlen, ein ausgeblendetes Fenster zu erstellen. Also erstelle ich eine Winform und im Onload habe ich die Konsole angehängt und das Original Main ..__ ausgeführt.

public partial class App3DummyForm : Form
{
    private readonly string[] _args;

    public App3DummyForm(string[] args)
    {
        _args = args;
        InitializeComponent();
    }

    private void App3DummyForm_Load(object sender, EventArgs e)
    {
        AllocConsole();
        App3.Program.OriginalMain(_args);
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool AllocConsole();
}
1
Jens

Für Interessenten an VB.net. (Ich habe im Internet gesucht und konnte keine Entsprechung dafür finden.) Hier wird es in vb.net übersetzt.

    <DllImport("kernel32")> _
    Private Function SetConsoleCtrlHandler(ByVal HandlerRoutine As HandlerDelegate, ByVal Add As Boolean) As Boolean
    End Function
    Private _handler As HandlerDelegate
    Private Delegate Function HandlerDelegate(ByVal dwControlType As ControlEventType) As Boolean
    Private Function ControlHandler(ByVal controlEvent As ControlEventType) As Boolean
        Select Case controlEvent
            Case ControlEventType.CtrlCEvent, ControlEventType.CtrlCloseEvent
                Console.WriteLine("Closing...")
                Return True
            Case ControlEventType.CtrlLogoffEvent, ControlEventType.CtrlBreakEvent, ControlEventType.CtrlShutdownEvent
                Console.WriteLine("Shutdown Detected")
                Return False
        End Select
    End Function
    Sub Main()
        Try
            _handler = New HandlerDelegate(AddressOf ControlHandler)
            SetConsoleCtrlHandler(_handler, True)
     .....
End Sub
0
dko

Visual Studio 2015 + Windows 10

  • Aufräumen zulassen
  • Einzelinstanz-App
  • Etwas Vergoldung

Code:

using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;

namespace YourNamespace
{
    class Program
    {
        // if you want to allow only one instance otherwise remove the next line
        static Mutex mutex = new Mutex(false, "YOURGUID-YOURGUID-YOURGUID-YO");

        static ManualResetEvent run = new ManualResetEvent(true);

        [DllImport("Kernel32")]
        private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);                
        private delegate bool EventHandler(CtrlType sig);
        static EventHandler exitHandler;
        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 ExitHandler(CtrlType sig)
        {
            Console.WriteLine("Shutting down: " + sig.ToString());            
            run.Reset();
            Thread.Sleep(2000);
            return false; // If the function handles the control signal, it should return TRUE. If it returns FALSE, the next handler function in the list of handlers for this process is used (from MSDN).
        }


        static void Main(string[] args)
        {
            // if you want to allow only one instance otherwise remove the next 4 lines
            if (!mutex.WaitOne(TimeSpan.FromSeconds(2), false))
            {
                return; // singleton application already started
            }

            exitHandler += new EventHandler(ExitHandler);
            SetConsoleCtrlHandler(exitHandler, true);

            try
            {
                Console.BackgroundColor = ConsoleColor.Gray;
                Console.ForegroundColor = ConsoleColor.Black;
                Console.Clear();
                Console.SetBufferSize(Console.BufferWidth, 1024);

                Console.Title = "Your Console Title - XYZ";

                // start your threads here
                Thread thread1 = new Thread(new ThreadStart(ThreadFunc1));
                thread1.Start();

                Thread thread2 = new Thread(new ThreadStart(ThreadFunc2));
                thread2.IsBackground = true; // a background thread
                thread2.Start();

                while (run.WaitOne(0))
                {
                    Thread.Sleep(100);
                }

                // do thread syncs here signal them the end so they can clean up or use the manual reset event in them or abort them
                thread1.Abort();
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.Write("fail: ");
                Console.ForegroundColor = ConsoleColor.Black;
                Console.WriteLine(ex.Message);
                if (ex.InnerException != null)
                {
                    Console.WriteLine("Inner: " + ex.InnerException.Message);
                }
            }
            finally
            {                
                // do app cleanup here

                // if you want to allow only one instance otherwise remove the next line
                mutex.ReleaseMutex();

                // remove this after testing
                Console.Beep(5000, 100);
            }
        }

        public static void ThreadFunc1()
        {
            Console.Write("> ");
            while ((line = Console.ReadLine()) != null)
            {
                if (line == "command 1")
                {

                }
                else if (line == "command 1")
                {

                }
                else if (line == "?")
                {

                }

                Console.Write("> ");
            }
        }


        public static void ThreadFunc2()
        {
            while (run.WaitOne(0))
            {
                Thread.Sleep(100);
            }

           // do thread cleanup here
            Console.Beep();         
        }

    }
}
0
A.J.Bauer