Detect when console application is closing/killed?

11,731

Solution 1

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

Solution 2

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


}
Share:
11,731
Prix
Author by

Prix

Looking forward to learn more and more :)

Updated on June 05, 2022

Comments

  • Prix
    Prix about 2 years

    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 user pressed ctrl+c.

    On windows there is the kernel function SetConsoleCtrlHandler which does the job but that doesnt work on mono.

    How do I get a closing event on my console application to safe exit it ?

  • Prix
    Prix about 13 years
    and how the invoke would look like for a console app ? You are missing a } from the while
  • Prix
    Prix about 13 years
    I can't find the Mono.Posix to reference on my project could you give me some extra directions on this matter... both links are very incomplete ;/
  • skolima
    skolima about 13 years
    @Prix: Mono.Posix.dll is installed by the standard Mono 2.10.2 installer, what distribution are you using? Also, for a console application, you can skip the Application.Invoke and just call the cleanup logic from the signal thread.
  • Prix
    Prix about 13 years
    I am developing the application on VS2010 hence i need it working on windows for testing before putting it on the linux server. I know the dll is on the linux server but it doesnt work if i get that dll and refernce it so i am clueless on how to proceed
  • skolima
    skolima about 13 years
    You won't be able to test this code on Windows, as it relies on OS-specific functionality. What you have to do is to implement both behaviours (using SetConsoleCtrlHandler for Windows and UnixSignal for Linux) and then choose one or the other during runtime based on Environment.OSVersion.Platform - use UnixSignal when running on Unix or MacOSX.
  • J Trana
    J Trana about 8 years
    Note that I had problems with Task.Factory.StartNew that seemed to be fixed with a dedicated background thread (while using mono in a docker container); has anyone else encountered this?