Capture console exit C#

95,945

Solution 1

I am not sure where I found the code on the web, but I found it now in one of my old projects. This will allow you to do cleanup code in your console, e.g. when it is abruptly closed or due to a shutdown...

[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

For those not checking the comments it seems that this particular solution does not work well (or at all) on Windows 7. The following thread talks about this

Solution 2

Full working example, works with ctrl-c, closing the windows with X and 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 boilerplate 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..");
        }
    }
}

Solution 3

Check also:

AppDomain.CurrentDomain.ProcessExit

Solution 4

I've had a similar problem, just my console App would be running in infinite loop with one preemptive statement on middle. Here is my solution:

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

Solution 5

ZeroKelvin's answer works in Windows 10 x64, .NET 4.6 console app. For those who do not need to deal with the CtrlType enum, here is a really simple way to hook into the framework's shutdown:

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

Returning FALSE from the handler tells the framework that we are not "handling" the control signal, and the next handler function in the list of handlers for this process is used. If none of the handlers returns TRUE, the default handler is called.

Note that when the user performs a logoff or shutdown, the callback is not called by Windows but is instead terminated immediately.

Share:
95,945
maljee
Author by

maljee

Updated on July 08, 2022

Comments

  • maljee
    maljee almost 2 years

    I have a console application that contains quite a lot of threads. There are threads that monitor certain conditions and terminate the program if they are true. This termination can happen at any time.

    I need an event that can be triggered when the program is closing so that I can cleanup all of the other threads and close all file handles and connections properly. I'm not sure if there is one already built into the .NET framework, so I'm asking before I write my own.

    I was wondering if there was an event along the lines of:

    MyConsoleProgram.OnExit += CleanupBeforeExit;
    
  • Randolpho
    Randolpho over 15 years
    I have to agree with this answer. Forcing application exit and then trying to clean up afterward isn't he way to go. Control your application, Noit. Don't let it control you.
  • maljee
    maljee over 15 years
    A thread spawned by me directly isn't necessarily the only thing that can close my application. Ctrl-C and the "close button" are other ways that it can end. The code posted by Frank, after minor modifications, fits perfectly.
  • Rob Parker
    Rob Parker about 15 years
    The help docs for DomainUnload say "The EventHandler delegate for this event can perform any termination activities before the application domain is unloaded." So it sounds like it does work within the current domain. However, it may not work for his need because his threads may keep the domain up.
  • ingh.am
    ingh.am about 14 years
    Can you use this to cancel the exit? Other than for when it's shutting down!
  • flq
    flq about 14 years
    james, I have no idea but the Handler signature returns a bool, so I'd try out what happens when you return true/false
  • Cipi
    Cipi about 14 years
    This works great, only bool Handler() must return false; (it returns nothing in the code) so it would work. If it returns true, windows prompts "Terminate Process Now" dialog. =D
  • CharlesB
    CharlesB over 13 years
    It looks like this solution does not work with Windows 7 for shutdown event, see social.msdn.microsoft.com/Forums/en/windowscompatibility/thr‌​ead/…
  • Maxim
    Maxim over 13 years
    Be aware that if you put a breakpoint in the 'Handler' method it will throw a NullReferenceException. Checked in VS2010, Windows 7.
  • flq
    flq over 13 years
    That's awesome, Charles, and shocking...looks like this answer is slowly expiring, and I can't quite see an alternative :/
  • dko
    dko over 13 years
    This doesn't work in Windows 7 at all, Unless I converted it wrong to vb.net Although I wasn't ever able to properly translate the _handler part of it
  • Zing-
    Zing- over 12 years
  • Kit10
    Kit10 over 11 years
    This only appears to catch exits from return or Environment.Exit, it does not catch CTRL+C, CTRL+Break, nor the actual close button on the console.
  • Kit10
    Kit10 over 11 years
    This only handles CTRL+C and CTRL+Close, it doesn't catch exists via returning, Environment.Exit nor clicking the close button.
  • BrainSlugs83
    BrainSlugs83 over 10 years
    This worked great for me on Windows 7 (64-bit). Not sure why everyone's saying it doesn't. The only major modifications I made was to get rid of the enum and switch statement, and to "return false" from the method -- I do all my cleanup in the body of the method.
  • Acaz Souza
    Acaz Souza over 9 years
    This code is not fired if you use End Process in Task Manager.
  • Cardin
    Cardin over 9 years
    Tried it in Win7 using .NET 4.5, it does fire even if closed via Task Manager.
  • Marcus Mangelsdorf
    Marcus Mangelsdorf over 8 years
    Possible source: MSDN forums
  • Mike Keskinov
    Mike Keskinov over 7 years
    Actually this doesn't work. I have multi-window WFP app and I use console (AllocConsole as in your example) to show some additional information. The problem is that the whole app (all Windows) get closed if user clicks on the (X) on Console Window. The SetConsoleCtrlHandler works, but the app halts anyway before any code in the handler executed (I see breakpoints fired and right then the app halts).
  • Mike Keskinov
    Mike Keskinov over 7 years
    But I found solution which works for me -- I simple DISABLED close button. See: stackoverflow.com/questions/6052992/…
  • Antonios Hadjigeorgalis
    Antonios Hadjigeorgalis about 7 years
    I tested this on windows 7 with everything commented out of Handler except for the return true and a while loop to count seconds. The application continues to run on ctrl-c but closes after 5 seconds when closing with the X.
  • glant
    glant over 6 years
    Above solution does not work for me vb.net 4.5 framework ControlEventType is not getting resolved. I was able to use this idea as solution stackoverflow.com/questions/15317082/…
  • Steve Benz
    Steve Benz almost 6 years
    There's a very subtle gotcha that this code avoids. You might look at this and think that the static _handler variable is not necessary, but it is. If you delete this, the handler may get garbage collected (because the unmanaged code doesn't hold a reference to it). If that happens, you'll get null reference exceptions when ctrl+C events are intercepted.
  • starbeamrainbowlabs
    starbeamrainbowlabs over 5 years
    Doesn't catch CTRL + C for me with Mono on Linux.
  • John Zabroski
    John Zabroski almost 5 years
    Interesting that this seems to be the most robust answer. However, be careful about changing your console buffer size: if the buffer height is less than the window height, the program will throw an exception on startup.
  • Konard
    Konard over 4 years
    If you handle CTRL+C separately using Console.CancelKeyPress then ProcessExit event actually raised after all CancelKeyPress event handlers execution.
  • Giacomo Pirinoli
    Giacomo Pirinoli about 4 years
    I am sorry but using this code I am able to get "Cleanup complete" only if I press Ctrl+C, not if I close with 'X' button; in the latter case I get only "Exiting system due to external CTRL-C, or process kill, or shutdown" but then it seems the console closes before executing the remaining part of Handler method {using Win10, .NET Framework 4.6.1}
  • Giacomo Pirinoli
    Giacomo Pirinoli about 4 years
    I am sorry but this code seems to work when pressing Ctrl+C, not when clicking 'X' button; in the latter case if I put a sleep in the first line of Handler method I am not able to get it completed because the console closes before, even with debugger attached and breakpoint set after 1 or 2 seconds the console closes {using Win10, .NET Framework 4.6.1}
  • JJ_Coder4Hire
    JJ_Coder4Hire over 3 years
    on windows 10 works for me CTRL-C, X on the window AND end process in Task Manager.
  • Robin Hartmann
    Robin Hartmann over 3 years
    @Konard I couldn't get ProcessExit to work, even if I registered a handler for CancelKeyPress. Could it be that your handler for CancelKeyPress does the same as your ProcessExit handler, so it only seemed like ProcessExit got called?
  • Robin Hartmann
    Robin Hartmann over 3 years
    @JJ_Coder4Hire does it work for you if you select the root/parent console process in Task Manager and click End Process? For me it only works if I select the child console process (see my question for more information).
  • Konard
    Konard over 3 years
    @RobinHartmann I have retested that, and I got the same result - repl.it/@Konard/CultivatedForsakenQueryoptimizer These are two separate events and ProcessExit event is triggered after CancelKeyPress events.
  • Robin Hartmann
    Robin Hartmann over 3 years
    @Konard Thanks for getting back to me, I also retested and this time I got ProcessExit to work, even without CancelKeyPress. What I mean by that is ProcessExit gets called when the console is closed using the close button, even if CancelKeyPress isn't registered. But you need CancelKeyPress, if you want to handle CTRL+C and CTRL+Break, because ProcessExit doesn't get called for those.
  • Dan Randolph
    Dan Randolph about 3 years
    Thank you. it works great for me on Windows 10.