Detect when console application is closing/killed?

后端 未结 2 971
一个人的身影
一个人的身影 2020-12-11 14:28

I wanted to make a safe exit for my console application that will be running on linux using mono but I can\'t find a solution to detect wether a signal was sent to it or the

相关标签:
2条回答
  • 2020-12-11 15:21

    As an example of providing a unix and windows implementation see below. Note that you can still include the Mono.Posix dll when using Visual Studio.

    I have added the SIGTERM signal as well because this is fired by systemd in unix when stopping / restarting your app as a service.

    Interface to expose an exit event

    public interface IExitSignal
    {
        event EventHandler Exit;
    }
    

    Unix implementation

    public class UnixExitSignal : IExitSignal
    {
        public event EventHandler Exit;
    
        UnixSignal[] signals = new UnixSignal[]{
            new UnixSignal(Mono.Unix.Native.Signum.SIGTERM), 
            new UnixSignal(Mono.Unix.Native.Signum.SIGINT),
            new UnixSignal(Mono.Unix.Native.Signum.SIGUSR1)
        };
    
        public UnixExitSignal()
        {
            Task.Factory.StartNew(() => 
            {
                // blocking call to wait for any kill signal
                int index = UnixSignal.WaitAny(signals, -1);
    
                if (Exit != null)
                {
                    Exit(null, EventArgs.Empty);
                }
    
            });
        }
    
    }
    

    Windows implementation

    public class WinExitSignal : IExitSignal
    {
        public event EventHandler Exit;
    
        [DllImport("Kernel32")]
        public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
    
        // A delegate type to be used as the handler routine
        // for SetConsoleCtrlHandler.
        public delegate bool HandlerRoutine(CtrlTypes CtrlType);
    
        // An enumerated type for the control messages
        // sent to the handler routine.
        public enum CtrlTypes
        {
            CTRL_C_EVENT = 0,
            CTRL_BREAK_EVENT,
            CTRL_CLOSE_EVENT,
            CTRL_LOGOFF_EVENT = 5,
            CTRL_SHUTDOWN_EVENT
        }
    
        /// <summary>
        /// Need this as a member variable to avoid it being garbage collected.
        /// </summary>
        private HandlerRoutine m_hr;
    
        public WinExitSignal()
        {
            m_hr = new HandlerRoutine(ConsoleCtrlCheck);
    
            SetConsoleCtrlHandler(m_hr, true);
    
        }
    
        /// <summary>
        /// Handle the ctrl types
        /// </summary>
        /// <param name="ctrlType"></param>
        /// <returns></returns>
        private bool ConsoleCtrlCheck(CtrlTypes ctrlType)
        {
            switch (ctrlType)
            {
                case CtrlTypes.CTRL_C_EVENT:
                case CtrlTypes.CTRL_BREAK_EVENT:
                case CtrlTypes.CTRL_CLOSE_EVENT:
                case CtrlTypes.CTRL_LOGOFF_EVENT:
                case CtrlTypes.CTRL_SHUTDOWN_EVENT:
                    if (Exit != null)
                    {
                        Exit(this, EventArgs.Empty);
                    }
                    break;
                default:
                    break;
            }
    
            return true;
        }
    
    
    }
    
    0 讨论(0)
  • 2020-12-11 15:27

    You need to use Mono.UnixSignal, there's a good sample posted by Jonathan Pryor : http://www.jprl.com/Blog/archive/development/mono/2008/Feb-08.html

    There's also a shorter example on Mono page: FAQ / Technical / Operating System Questions / Signal Handling:

    // Catch SIGINT and SIGUSR1
    UnixSignal[] signals = new UnixSignal [] {
        new UnixSignal (Mono.Unix.Native.Signum.SIGINT),
        new UnixSignal (Mono.Unix.Native.Signum.SIGUSR1),
    };
    
    Thread signal_thread = new Thread (delegate () {
        while (true) {
            // Wait for a signal to be delivered
            int index = UnixSignal.WaitAny (signals, -1);
    
            Mono.Unix.Native.Signum signal = signals [index].Signum;
    
            // Notify the main thread that a signal was received,
            // you can use things like:
            //    Application.Invoke () for Gtk#
            //    Control.Invoke on Windows.Forms
            //    Write to a pipe created with UnixPipes for server apps.
            //    Use an AutoResetEvent
    
            // For example, this works with Gtk#    
            Application.Invoke (delegate () { ReceivedSignal (signal); });
        }});
    
    0 讨论(0)
提交回复
热议问题