【问题标题】:Detect when console application is closing/killed?检测控制台应用程序何时关闭/终止?
【发布时间】:2011-09-26 15:40:04
【问题描述】:

我想为我的控制台应用程序安全退出,该应用程序将使用单声道在 linux 上运行,但我找不到解决方案来检测是否向其发送了信号或用户按下了 ctrl+c。

在 Windows 上,有一个内核函数 SetConsoleCtrlHandler 可以完成这项工作,但在单声道上不起作用。

如何在我的控制台应用程序上获得关闭事件以安全退出它?

【问题讨论】:

    标签: c# mono console signals kill


    【解决方案1】:

    您需要使用Mono.UnixSignal,Jonathan Pryor 发布了一个很好的示例:http://www.jprl.com/Blog/archive/development/mono/2008/Feb-08.html

    Mono 页面上还有一个更短的示例: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); });
        }});
    

    【讨论】:

    • 以及控制台应用程序的调用是什么样的?您在 while 中缺少一个 }
    • 我在我的项目中找不到要参考的 Mono.Posix,您能否就此事给我一些额外的指导……两个链接都非常不完整;/
    • @Prix:Mono.Posix.dll 由标准的 Mono 2.10.2 安装程序安装,您使用的是什么发行版?此外,对于控制台应用程序,您可以跳过Application.Invoke,只需从信号线程调用清理逻辑。
    • 我正在 VS2010 上开发应用程序,因此我需要它在 windows 上运行以进行测试,然后再将其放在 linux 服务器上。我知道 dll 在 linux 服务器上,但是如果我得到那个 dll 并引用它,它就不起作用,所以我对如何继续一无所知
    • 您将无法在 Windows 上测试此代码,因为它依赖于特定于操作系统的功能。您需要做的是实现这两种行为(Windows 使用SetConsoleCtrlHandler,Linux 使用UnixSignal),然后根据Environment.OSVersion.Platform 选择一个或另一个在运行时 - 使用UnixSignalUnixMacOSX 上运行时。
    【解决方案2】:

    作为提供 unix 和 windows 实现的示例,请参见下文。请注意,您仍然可以在使用 Visual Studio 时包含 Mono.Posix dll。

    我也添加了 SIGTERM 信号,因为它是由 unix 中的 systemd 在停止/重新启动您的应用作为服务时触发的。

    公开退出事件的接口

    public interface IExitSignal
    {
        event EventHandler Exit;
    }
    

    Unix 实现

    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 实现

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

    【讨论】:

    • 请注意,我在 Task.Factory.StartNew 上遇到了问题,似乎已通过专用后台线程修复(在 docker 容器中使用单声道时);有没有其他人遇到过这个?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-20
    • 1970-01-01
    相关资源
    最近更新 更多