2011-07-01 21 views
18

Chciałem zrobić bezpieczne wyjście dla mojej aplikacji konsolowej, która będzie działać na Linuksie przy użyciu mono, ale nie mogę znaleźć rozwiązania, które pozwoli wykryć, czy wysłano do niego sygnał, czy też użytkownik nacisnął ctrl + c.Wykryj, kiedy aplikacja konsolowa się zamyka/zabija?

W oknach znajduje się funkcja jądra SetConsoleCtrlHandler, która wykonuje zadanie, ale nie działa w trybie mono.

Jak uzyskać zdarzenie zamknięcia w aplikacji konsoli, aby je bezpiecznie zamknąć?

Odpowiedz

11

Trzeba użyć Mono.UnixSignal, istnieje duża próbka wysłane przez Jonathana Pryor: http://www.jprl.com/Blog/archive/development/mono/2008/Feb-08.html

Jest też krótsze przykład na stronie 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); }); 
    }}); 
+0

i jak będzie wyglądać invoke dla aplikacji konsoli? Brakuje Ci} od pewnego czasu – Prix

+0

Nie mogę znaleźć Mono.Posix w celu odniesienia się do mojego projektu mógłbyś dać mi dodatkowe wskazówki w tej sprawie ... oba linki są bardzo niekompletne;/ – Prix

+1

@Prix: Mono.Posix .dll jest instalowany przez standardowy instalator Mono 2.10.2, z jakiej dystrybucji korzystasz? Ponadto dla aplikacji konsolowej można pominąć 'Application.Invoke' i po prostu wywołać logikę czyszczenia z wątku sygnału. – skolima

7

Jako przykład zapewniając UNIX i implementacja okien patrz poniżej. Zauważ, że nadal możesz dołączyć dll Mono.Posix podczas korzystania z Visual Studio.

Dodałem także sygnał SIGTERM, ponieważ jest on uruchamiany przez systemd w systemie UNIX podczas zatrzymywania/restartowania aplikacji jako usługa.

Interfejs narazić zdarzenie wyjściowe realizację

public interface IExitSignal 
{ 
    event EventHandler Exit; 
} 

Unix

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

     }); 
    } 

} 

Okna

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

Należy zauważyć, że miałem problemy z Task.Factory.StartNew, która wydawała się być naprawiona za pomocą dedykowanego wątku tła (podczas korzystania z mono w kontenerze dokowania); czy ktoś inny to napotkał? –

Powiązane problemy