2011-07-01 7 views
18

Quería hacer una salida segura para mi aplicación de consola que se ejecutará en linux usando mono pero no puedo encontrar una solución para detectar si se le envió una señal o si el usuario presiona ctrl + c.¿Detecta cuándo la aplicación de la consola se está cerrando/matando?

En Windows existe la función de kernel SetConsoleCtrlHandler que hace el trabajo pero que no funciona en mono.

¿Cómo obtengo un evento de cierre en la aplicación de mi consola para salir de manera segura?

Respuesta

11

Es necesario utilizar Mono.UnixSignal, hay una buena muestra de publicado por Jonathan Pryor: http://www.jprl.com/Blog/archive/development/mono/2008/Feb-08.html

También hay un ejemplo más corto en la página 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

y cómo la invocación se vería como una aplicación de consola? Falta un} del tiempo – Prix

+0

No puedo encontrar el Mono.Posix para hacer referencia en mi proyecto. ¿Podría darme algunas indicaciones adicionales sobre este asunto? Ambos enlaces son muy incompletos;/ – Prix

+1

@Prix: Mono.Posix .dll es instalado por el instalador estándar de Mono 2.10.2, ¿qué distribución estás usando? Además, para una aplicación de consola, puede omitir 'Application.Invoke' y simplemente invocar la lógica de limpieza desde el hilo de señal. – skolima

7

Como ejemplo de proporcionar un UNIX y implementación de Windows ver a continuación. Tenga en cuenta que aún puede incluir el dll Mono.Posix cuando usa Visual Studio.

También he agregado la señal SIGTERM porque esto es activado por systemd en Unix al detener/reiniciar su aplicación como servicio.

interfaz para exponer un evento de salida aplicación

public interface IExitSignal 
{ 
    event EventHandler Exit; 
} 

Unix

aplicación
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); 
      } 

     }); 
    } 

} 

de 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

Tenga en cuenta que tuve problemas con Task.Factory.StartNew que parecían corregidos con un hilo de fondo dedicado (mientras usaba mono en un contenedor de docker); ¿Alguien más ha encontrado esto? –

Cuestiones relacionadas