2011-07-01 135 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的問題,似乎與專用的後臺線程(當在碼頭集裝箱中使用單聲道)修復;有人遇到過這種情況麼? –

相關問題