2011-07-01 139 views
18

我想为我的控制台应用程序做一个安全的退出,该应用程序将在Linux上使用mono进行运行,但我无法找到解决方案来检测是否发送了一个信号,或者用户按下了ctrl + c。控制台应用程序关闭/终止时检测?

在windows上有内核函数SetConsoleCtrlHandler,它可以完成这项工作,但是它不适用于单声道。

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

回答

11

您需要使用Mono.UnixSignal,有张贴由乔纳森·普赖尔一个很好的例子:http://www.jprl.com/Blog/archive/development/mono/2008/Feb-08.html

还有单页上更短的例子: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

以及如何调用看起来像一个控制台应用程序?你错过了}从 – Prix

+0

我找不到在我的项目上引用的Mono.Posix,你可以给我一些额外的方向,在这个问题上...这两个链接是非常不完整的;/ – Prix

+1

@Prix:Mono.Posix .dll是由标准的Mono 2.10.2安装程序安装的,您使用的是什么发行版?另外,对于控制台应用程序,您可以跳过'Application.Invoke'并从信号线程中调用清除逻辑。 – skolima

7

作为提供UNIX的例子,窗口的实现见下文。请注意,使用Visual Studio时,您仍可以包含Mono.Posix dll。

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

接口暴露退出事件

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


} 
+0

请注意,我有Task.Factory.StartNew的问题,似乎与专用的后台线程(当在码头集装箱中使用单声道)修复;有人遇到过这种情况么? –

相关问题