Captura la salida de la consola C #

Tengo una aplicación de consola que contiene bastantes hilos. Hay hilos que monitorean ciertas condiciones y terminan el progtwig si son verdaderas. Esta terminación puede suceder en cualquier momento.

Necesito un evento que pueda desencadenarse cuando el progtwig se cierre para que pueda limpiar todos los otros subprocesos y cerrar todas las combinaciones de archivos y conexiones correctamente. No estoy seguro de si ya hay uno incorporado en .NET Framework, entonces lo estoy preguntando antes de escribir el mío.

Me preguntaba si hubo un evento del tipo de:

MyConsoleProgram.OnExit += CleanupBeforeExit; 

No estoy seguro de dónde encontré el código en la web, pero lo encontré ahora en uno de mis proyectos anteriores. Esto le permitirá hacer un código de limpieza en su consola, por ejemplo, cuando está abruptamente cerrado o debido a un apagado …

 [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); ... } 

Actualizar

Para aquellos que no revisan los comentarios, parece que esta solución particular no funciona bien (o no funciona) en Windows 7 . El siguiente hilo habla sobre esto

Ejemplo de trabajo completo, funciona con ctrl-c, cierra las ventanas con X y mata:

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

Verifique también:

 AppDomain.CurrentDomain.ProcessExit 

Hay aplicaciones para WinForms;

 Application.ApplicationExit += CleanupBeforeExit; 

Para aplicaciones de consola, prueba

 AppDomain.CurrentDomain.DomainUnload += CleanupBeforeExit; 

Pero no estoy seguro de en qué punto se llama o si funcionará dentro del dominio actual. Sospecho que no.

Parece que tienes los hilos que terminan directamente la aplicación. Tal vez sería mejor tener un hilo de señal el hilo principal para decir que la aplicación debe ser terminada.

Al recibir esta señal, el hilo principal puede cerrar limpiamente los otros hilos y finalmente cerrarse.

Tuve un problema similar, solo la aplicación de mi consola se ejecutaba en bucle infinito con una statement preventiva en el medio. Aquí está mi solución:

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

El enlace mencionado anteriormente por Charle B en comentario a flq

En el fondo dice:

SetConsoleCtrlHandler no funcionará en Windows7 si enlaza con user32

En alguna otra parte del hilo, se sugiere colocar una ventana oculta. Así que creo un winform y en onload lo adjunto a la consola y ejecuto Main original. Y luego SetConsoleCtrlHandle funciona bien (se llama a SetConsoleCtrlHandle como lo sugiere flq)

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

Para aquellos interesados ​​en VB.net. (Busqué en Internet y no pude encontrar un equivalente para él) Aquí está traducido a vb.net.

   _ 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 

Visual Studio 2015 + Windows 10

  • Permitir la limpieza
  • Aplicación de instancia única
  • Algo de oro

Código:

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

La respuesta de ZeroKelvin funciona en Windows 10 x64, aplicación de consola .NET 4.6. Para aquellos que no necesitan lidiar con la enumeración CtrlType, aquí hay una manera muy simple de engancharse al cierre del marco:

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

Al devolver FALSE desde el controlador se le informa al marco que no estamos “manejando” la señal de control, y se utiliza la siguiente función del controlador en la lista de manejadores para este proceso. Si ninguno de los controladores devuelve TRUE, se llama al controlador predeterminado.

Tenga en cuenta que cuando el usuario realiza un cierre de sesión o se apaga, Windows no llama a la callback sino que termina inmediatamente.