1

I have a simple library that overrides a console's shutdown events to complete various tasks before closing itself. This works whenever a user or external program closes the console (ctrl+C, close window, etc), however, when the console app uses Environment.Exit() it closes without being recognised as one of the shutdown events.

Here is the code for the shutdown handler:

namespace ShutdownLibrary
{
    public class ConsoleHandler
    {
        public bool ExitSystem = false; // Optional assistance for implementation.

        [DllImport("Kernel32")]
        private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
        private delegate bool EventHandler(CtrlType sig);
        private EventHandler _handler;

        /// <summary>
        /// Windows events to listen for.
        /// </summary>
        public enum CtrlType
        {
            CTRL_C_EVENT = 0,
            CTRL_BREAK_EVENT = 1,
            CTRL_CLOSE_EVENT = 2,
            CTRL_LOGOFF_EVENT = 5,
            CTRL_SHUTDOWN_EVENT = 6,
        }

        private bool Handler(CtrlType sig)
        {
            //Old .NET threading
            var result = Task.Factory.StartNew<bool>(ShutdownTasks);
            result.Wait();
            return result.Result;
        }

        /// <summary>
        /// Starts an EventHandler for console windows & calls overridable ShutdownTasks() when console is closed by any means.
        /// </summary>
        public void StartShutdownEventHandler()
        {
            _handler += new EventHandler(Handler);
            SetConsoleCtrlHandler(_handler, true);
        }


        /// <summary>
        /// Overridable method for tasks to action before the program is disposed.
        /// </summary>
        /// <returns>True when complete. False when not implemented.</returns>
        public virtual bool ShutdownTasks()
        {
            ExitSystem = false;
            return false;
        }
    }
}

The app itself consumes the shutdown handler (ConsoleHandler) like this:

    class ManagementServer : ServerManager
    {

        internal class ShutdownManager : ConsoleHandler
        {
            public override bool ShutdownTasks()
            {
                base.ShutdownTasks();
                for (var i = 5; i >= 0; i--)
                {
                    Thread.Sleep(1000);
                    Log.WriteToLog(string.Format("Management Server Shutting down in {0}", i));
                }
                Console.WriteLine("Good bye!");
                return true;
            }
        }

    //Override from ServerManager
    public override void ShutDownManagementServer()
    {
        base.ShutDownManagementServer();
        Environment.Exit(0);
    }

        static void Main(string[] args)
        {
            var sm = new ShutdownManager();
            sm.StartShutdownEventHandler();

            var manager = new ManagementServer();
            manager.StartServer();
        }
    }

I think the CtrlType enum in the ConsoleHandler class is the main culprit, but I can't seem to figure out what values are missing.

Any ideas?

Dezzamondo
  • 2,169
  • 2
  • 20
  • 33
  • Sure, it was designed to inform you about reasons your app will terminate that are beyond your control. Like Ctrl+C, Ctrl+Break, the user clicking the Close button, logging off or shutting down the machine. No other way you could find out. You of course do know when you terminate yourself, it doesn't bother to remind you about that. You could simply add another CtrlType and call your handler directly. – Hans Passant Jan 26 '15 at 17:17
  • Send a control-C to yourself... then it will shutdown using your handler. – Hogan Jan 26 '15 at 17:29
  • Some talk in this answer about why Environment.Exit() is not such a good idea. http://stackoverflow.com/questions/10286056/what-is-the-command-to-exit-a-console-application-in-c – Hogan Jan 26 '15 at 17:31

1 Answers1

0

Exit does not perform a graceful shutdown. Exit is similar to you pulling the power cord of your PC, whereas start->shutdown is graceful and informs the running apps the system is shutting down.

When you issue the Exit command, no message is sent to the running apps. You need to use application.Shutdown instead.

see what's difference between Environment.Exit() and Application.Shutdown()?

edit: sorry for not noticing it was a console app. You can try handling the AppDomain.ProcessExit event instead. But honestly I do not think this event work. This event is known not to trigger if a rude exit command (like end task) is issued. Exit is very rude.

Community
  • 1
  • 1
user2880486
  • 1,068
  • 7
  • 16
  • 2
    Application.Shutdown() does not make sense in a console mode app. It doesn't have an Application object, there is no dispatcher loop that can be shut down. – Hans Passant Jan 26 '15 at 17:20
  • @HansPassant is right, I can't access the Application.Shutdown() method from the context of a Console App, hence the sledgehammer approach with Environment.Exit() – Dezzamondo Jan 26 '15 at 17:24
  • That would be my bad on shutdown. But I don't think Exit will work since you need to trap the shutdown event but Exit is not supposed to raise one. – user2880486 Jan 26 '15 at 17:31