2017-09-05 2 views
0

J'ai une application qui lance plusieurs processus. Le but de cette application est de surveiller si elles sont en cours d'exécution une fois lancé et si non, alors tuer le processus s'il ne répond pas ou relancer s'il n'est pas en cours d'exécution. Exécution de chacun des processus que l'application sait lancer et vérifier s'ils sont exécutés par la fonction ObtenerProceso (j'ai les PID de chaque processus quand ils ont été lancés mais cela ne garantit pas que le processus est mort et qu'un autre processus a pris le même PID).comment détecter si un processus lancé par une autre application est en cours d'exécution?

public static Process ObtenerProceso(int pid, string ubicacion, string argumentos = "", string dominio = "", string usuario = "") 
{ 
    Process proceso = null; 
    Process procesoAux = null; 

    if (Process.GetProcesses().Any(x => x.Id == pid)) 
    { 
     procesoAux = Process.GetProcessById(pid); 
     if (procesoAux.MainModule.FileName.ToUpper() == ubicacion.ToUpper()) 
     { 
      ManagementObjectSearcher mos = new ManagementObjectSearcher($"select * from Win32_Process where ProcessId = {procesoAux.Id}"); 
      foreach (ManagementObject mo in mos.Get()) 
       if (mo["CommandLine"] != null && mo["CommandLine"].ToString().ToUpper().Replace($"\"{ubicacion.ToUpper()}\"", string.Empty).Trim() == argumentos.ToUpper().Trim()) 
       { 
        if (dominio.Trim() != string.Empty && usuario.Trim() != string.Empty) 
        { 
         string[] argList = new string[] { string.Empty, string.Empty }; 
         int respuesta = Convert.ToInt32(mo.InvokeMethod("GetOwner", argList)); 
         if (respuesta == 0 && $"{argList[1]}\\{argList[0]}".ToUpper() == $"{dominio}\\{usuario}".ToUpper()) 
          proceso = procesoAux; 
        } 
        else 
         proceso = procesoAux; 
       } 
     } 

    } 

    return proceso; 
} 

La fonction retourne null dans le cas où il ne trouve pas le processus.

Cette méthode a bien fonctionné pour moi, le problème est que cela prend un peu de temps pour le nombre de processus que je dois surveiller. Les consommations les plus élevées de temps ont été dans l'instruction select qui obtient des informations plus détaillées sur le processus en tant qu'utilisateur qui a exécuté le processus et la ligne de commande qui a été envoyée à l'exécutable.

Existe-t-il une méthode plus efficace pour cela?

Des précisions supplémentaires

D'un seul exécutable de l'application doit lancer plusieurs instances (mais avec des arguments d'initialisation), afin de capturer le processus par son nom n'est pas recommandé, dans les cas les différencient uniquement par la ligne de commande avec laquelle le processus a été exécuté. En outre, je vérifie si le processus répond par deux critères, d'abord avec la propriété Process.Responding et chaque seconde processus lancé met à jour de temps en temps une base de données SQLite que mon application demande de savoir quand était le dernier rapport du processus et de savoir si elle est Non verrouillé". Je vous laisse la classe Aplicacion qui représente un processus que je vais lancer et surveiller.

using System; 
using System.Data; 
using System.Diagnostics; 
using System.Globalization; 
using System.IO; 
using System.Security; 

namespace Yggdrasil 
{ 
    /// <summary> 
    /// Represents an Application to be monitored. 
    /// </summary> 
    internal class Aplicacion 
    { 
     #region Definition of private variables. 
     private int id; 
     private int idMaquina; 
     private int pid = -999999999; 
     private string nombre; 
     private string descripcion; 
     private string ubicacion; 
     private string argumentos; 
     private string dominio; 
     private string usuario; 
     private SecureString clave; 
     private bool activa; 
     private DateTime fechaCreacion; 
     #endregion 

     #region Properties. 
     /// <summary> 
     /// Gets the Application ID. This property can not be set. 
     /// </summary> 
     public int Id 
     { 
      get 
      { 
       return id; 
      } 
     } 

     /// <summary> 
     /// Gets the identification of the process of the Application. This property can not be set. 
     /// </summary> 
     public int PID 
     { 
      get 
      { 
       return pid; 
      } 
     } 

     /// <summary> 
     /// Gets the identification of the Machine where the Application is executed. This property can not be set. 
     /// </summary> 
     public int IdMaquina 
     { 
      get 
      { 
       return idMaquina; 
      } 
     } 

     /// <summary> 
     /// Gets the name of the Application. This property can not be set. 
     /// </summary> 
     public string Nombre 
     { 
      get 
      { 
       return nombre; 
      } 
     } 

     /// <summary> 
     /// Gets the description of the Application. This property can not be set. 
     /// </summary> 
     public string Descripcion 
     { 
      get 
      { 
       return descripcion; 
      } 
     } 

     /// <summary> 
     /// Gets the location of the Application executable. This property can not be set. 
     /// </summary> 
     public string Ubicacion 
     { 
      get 
      { 
       return ubicacion; 
      } 
     } 

     /// <summary> 
     /// Gets the start arguments for the application. This property can not be set. 
     /// </summary> 
     public string Argumentos 
     { 
      get 
      { 
       return argumentos; 
      } 
     } 

     /// <summary> 
     /// Determines whether the Application is active or inactive. This property can not be set. 
     /// </summary> 
     public bool Activa 
     { 
      get 
      { 
       return activa; 
      } 
     } 

     /// <summary> 
     /// Gets the user with which the application is executed. This property can not be set. 
     /// </summary> 
     public string Usuario 
     { 
      get 
      { 
       return usuario; 
      } 
     } 

     /// <summary> 
     /// Gets the domain in which the application runs. This property can not be set. 
     /// </summary> 
     public string Dominio 
     { 
      get 
      { 
       return dominio; 
      } 
     } 

     /// <summary> 
     /// Gets the password of the user with whom the application is running. This property can not be set. 
     /// </summary> 
     public SecureString Clave 
     { 
      get 
      { 
       return clave; 
      } 
     } 

     /// <summary> 
     /// Gets the last date the application responded. This property can not be set. 
     /// </summary> 
     public DateTime FechaResponde 
     { 
      get 
      { 
       return ObtenerUltimoRespondeProceso(); 
      } 
     } 

     /// <summary> 
     /// Gets the last date the application reported activity. This property can not be set. 
     /// </summary> 
     public DateTime FechaReporte 
     { 
      get 
      { 
       return ObtenerUltimoReporteProceso(); 
      } 
     } 

     /// <summary> 
     /// Gets the date of creation of the application record. This property can not be set. 
     /// </summary> 
     public DateTime FechaCreacion 
     { 
      get 
      { 
       return fechaCreacion; 
      } 
     } 
     #endregion 

     #region implementación de constructores. 
     /// <summary> 
     /// Initializes an object from the Application class. 
     /// </summary> 
     /// <param name="id">Identification of the application.</param> 
     public Aplicacion(int id) 
     { 
      Inicializar(id); 
     } 

     /// <summary> 
     /// Initializes an object from the Application class. 
     /// </summary> 
     /// <param name="id">Identification of the application.</param> 
     /// <param name="idMaquina">Identification of the machine where the application is running.</param> 
     /// <param name="nombre">Name of the application.</param> 
     /// <param name="descripcion">Description of the application.</param> 
     /// <param name="ubicacion">Location of the application executable.</param> 
     /// <param name="argumentos">Arguments with which the application is executed.</param> 
     /// <param name="dominio">User domain of the application.</param> 
     /// <param name="usuario">User with which the application is executed.</param> 
     /// <param name="clave">Password of the user with which the application is executed.</param> 
     /// <param name="activa">Indicates whether the application is active or inactive.</param> 
     /// <param name="fechaCreacion">Creation date of the application record.</param> 
     public Aplicacion(int id, int idMaquina, string nombre, string descripcion, string ubicacion, string argumentos, string dominio, string usuario, string clave, int pid, bool activa, DateTime fechaCreacion) 
     { 
      this.id = id; 
      this.idMaquina = idMaquina; 
      this.nombre = nombre; 
      this.descripcion = descripcion; 
      this.ubicacion = ubicacion; 
      this.argumentos = argumentos; 
      this.activa = activa; 
      this.fechaCreacion = fechaCreacion; 
      this.dominio = dominio; 
      this.usuario = usuario.ToUpper(); 
      this.clave = Utilidades.String2SecureString(clave); 
      this.pid = pid; 
     } 
     #endregion 

     #region Implementación de métodos privados. 
     /// <summary> 
     /// Initializes an object of the Application class knowing its identification. 
     /// </summary> 
     /// <param name="id">Identification of the Application.</param> 
     private void Inicializar(int id) 
     { 
      try 
      { 
       using (ControladorSQLite controladorBD = new ControladorSQLite(Controlador.UbicacionBDLocal)) 
       { 
        DataTable dtAplicacion = controladorBD.EjecutarLector($"SELECT * FROM aplicacion WHERE id_aplicacion = {id}"); 
        foreach (DataRow drAplicacion in dtAplicacion.Rows) 
        { 
         this.id = id; 
         idMaquina = Convert.ToInt32(drAplicacion["id_maquina"]); 
         nombre = drAplicacion["nombre_aplicacion"].ToString(); 
         descripcion = drAplicacion["descripcion"].ToString(); 
         ubicacion = drAplicacion["ubicacion"].ToString(); 
         argumentos = drAplicacion["argumentos"].ToString(); 
         dominio = drAplicacion["dominio"].ToString(); 
         usuario = drAplicacion["usuario"].ToString().ToUpper(); 
         clave = Utilidades.String2SecureString(drAplicacion["clave"].ToString()); 
         if (drAplicacion["activa"].ToString() == "S") 
          activa = true; 
         else 
          activa = false; 
         pid = Convert.ToInt32(drAplicacion["pid"]); 
         fechaCreacion = (DateTime)drAplicacion["fecha_creacion"]; 
        } 
       } 
      } 
      catch (Exception ex) 
      { 
       throw new Exception($"Error al inicializar un objeto Aplicacion. {ex.Message}"); 
      } 
     } 

     /// <summary> 
     /// Updates the PID of the Application. 
     /// </summary> 
     /// <param name="pid">New process identification for the Application.</param> 
     private void ActualizarPID(int pid) 
     { 
      try 
      { 
       using (ControladorSQLite controladorBD = new ControladorSQLite(Controlador.UbicacionBDLocal)) 
       { 
        controladorBD.Ejecutar($"UPDATE aplicacion SET pid = {pid} WHERE id_aplicacion = {id}"); 
        this.pid = pid; 
       } 
      } 
      catch (Exception ex) 
      { 
       throw new Exception($"Error al intentar actualizar el PID. {ex.Message}"); 
      } 
     } 

     /// <summary> 
     /// Gets the date of the last report of the process. 
     /// </summary> 
     /// <returns></returns> 
     private DateTime ObtenerUltimoReporteProceso() 
     { 
      DateTime fecha = DateTime.Now; 
      Process proceso = ObtenerProcesoActual(); 

      try 
      { 
       using (ControladorSQLite controladorBD = new ControladorSQLite(Controlador.UbicacionBDLocal)) 
       { 
        int cantidad = Convert.ToInt32(controladorBD.EjecutarLector($"SELECT COUNT(*) AS cantidad FROM reporte_proceso WHERE id_proceso = {proceso.Id} AND UPPER(ubicacion) = '{ubicacion.ToUpper()}'").Rows[0]["cantidad"]); 
        if (cantidad > 0) 
        { 
         if (cantidad > 1000) 
          controladorBD.Ejecutar($"DELETE FROM reporte_proceso WHERE id_proceso = {proceso.Id} AND UPPER(ubicacion) = '{ubicacion.ToUpper()}'"); 

         fecha = DateTime.ParseExact(controladorBD.EjecutarLector($"SELECT STRFTIME('%d-%m-%Y %H:%M:%S', DATETIME(x.fecha)) AS fecha FROM (SELECT MAX(fecha_creacion) AS fecha FROM reporte_proceso WHERE id_proceso = {proceso.Id} AND UPPER(ubicacion) = '{ubicacion.ToUpper()}') x").Rows[0]["fecha"].ToString(), "dd-MM-yyyy HH:mm:ss", CultureInfo.InvariantCulture); 
        } 
       } 
      } 
      catch (Exception ex) 
      { 
       throw new Exception($"Error al intentar obtener la fecha del último reporte de una aplicación. {ex.Message}"); 
      } 

      return fecha; 
     } 

     /// <summary> 
     /// Gets the date of the last time the application replied. 
     /// </summary> 
     /// <returns></returns> 
     private DateTime ObtenerUltimoRespondeProceso() 
     { 
      DateTime fecha = DateTime.Now; 

      try 
      { 
       using (ControladorSQLite controladorBD = new ControladorSQLite(Controlador.UbicacionBDLocal)) 
       { 
        object obj_fecha = controladorBD.EjecutarLector($"SELECT STRFTIME('%d-%m-%Y %H:%M:%S', DATETIME(fecha_responde)) AS fecha FROM aplicacion WHERE id_aplicacion = {id}").Rows[0]["fecha"]; 
        if (obj_fecha != null) 
         fecha = DateTime.ParseExact(Convert.ToString(obj_fecha), "dd-MM-yyyy HH:mm:ss", CultureInfo.InvariantCulture); 
       } 
      } 
      catch (Exception ex) 
      { 
       throw new Exception($"Error al intentar obtener la última fecha de respuesta de una aplicación {ex.Message}"); 
      } 

      return fecha; 
     } 

     /// <summary> 
     /// Gets the current application process. 
     /// </summary> 
     /// <returns></returns> 
     private Process ObtenerProcesoActual() 
     { 
      return Utilidades.ObtenerProceso(pid, ubicacion, argumentos, dominio, usuario); 
     } 
     #endregion 

     #region Implementation of public methods 
     /// <summary> 
     /// Inactiva el proceso. 
     /// </summary> 
     public void Inactivar() 
     { 
      try 
      { 
       using (ControladorSQLite controladorBD = new ControladorSQLite(Controlador.UbicacionBDLocal)) 
        controladorBD.Ejecutar($"UPDATE aplicacion SET activa = 'N' WHERE id_aplicacion = {id} AND activa = 'S'"); 
      } 
      catch (Exception ex) 
      { 
       throw new Exception($"Error al intentar inactivar una aplicación. {ex.Message}"); 
      } 
     } 

     /// <summary> 
     /// Activate the process. 
     /// </summary> 
     public void Activar() 
     { 
      try 
      { 
       using (ControladorSQLite controladorBD = new ControladorSQLite(Controlador.UbicacionBDLocal)) 
        controladorBD.Ejecutar($"UPDATE aplicacion SET activa = 'S' WHERE id_aplicacion = {id} AND activa = 'N'"); 
      } 
      catch (Exception ex) 
      { 
       throw new Exception($"Error al intentar activar una aplicación. {ex.Message}"); 
      } 
     } 

     /// <summary> 
     /// Updates the last date the application responded. 
     /// </summary> 
     public void ActualizarRespuesta() 
     { 
      try 
      { 
       using (ControladorSQLite controladorBD = new ControladorSQLite(Controlador.UbicacionBDLocal)) 
        controladorBD.Ejecutar($"UPDATE aplicacion SET fecha_responde = CURRENT_TIMESTAMP WHERE id_aplicacion = {id}"); 
      } 
      catch (Exception ex) 
      { 
       throw new Exception($"Error al intentar actualizar la fecha de respuesta de una aplicación. {ex.Message}"); 
      } 
     } 

     /// <summary> 
     /// Deletes the configuration application. 
     /// </summary> 
     public void Eliminar() 
     { 
      try 
      { 
       using (ControladorSQLite controladorBD = new ControladorSQLite(Controlador.UbicacionBDLocal)) 
        controladorBD.Ejecutar($"DELETE FROM aplicacion WHERE id_aplicacion = {id}"); 
      } 
      catch (Exception ex) 
      { 
       throw new Exception($"Error al intentar eliminar una aplicaión. {ex.Message}"); 
      } 
     } 

     /// <summary> 
     /// Checks if the application is running. 
     /// </summary> 
     /// <returns></returns> 
     public bool EnEjecucion() 
     { 
      return ObtenerProcesoActual() != null; 
     } 

     /// <summary> 
     /// Determines whether the application is responding. 
     /// </summary> 
     /// <returns></returns> 
     public bool EstaRespondiendo() 
     { 
      return ObtenerProcesoActual().Responding; 
     } 

     /// <summary> 
     /// Run the application. 
     /// </summary> 
     public void Ejecutar() 
     { 
      Process proceso = new Process() 
      { 
       StartInfo = new ProcessStartInfo() 
       { 
        FileName = ubicacion, 
        ErrorDialog = true, 
        UseShellExecute = false, 
        RedirectStandardOutput = true, 
        RedirectStandardError = true, 
        WorkingDirectory = Path.GetDirectoryName(ubicacion), 
        Arguments = argumentos, 
        Domain = dominio, 
        UserName = usuario, 
        Password = clave 
       } 
      }; 

      proceso.Start(); 
      ActualizarPID(proceso.Id); 
     } 

     /// <summary> 
     /// Kills the current application process. 
     /// </summary> 
     public void Matar() 
     { 
      ObtenerProcesoActual().Kill(); 
     } 
     #endregion 
    } 
} 
+0

Les poignées de traitement sont des objets attendus. (Voir par exemple https://msdn.microsoft.com/en-us/library/windows/desktop/ms682512(v=vs.85).aspx). Si votre application crée les processus, elle peut attendre qu'ils se terminent exactement comme dans l'exemple de code sur le lien que j'ai collé ci-dessus. Pas besoin d'interroger la liste des processus. – BitTickler

+0

Pourquoi ne pas aller par le nom du processus au lieu de PID? Le nom du processus sera plus approprié à cette fin. –

+0

Comment définissez-vous "en cours d'exécution" et "ne répond pas"? Le processus existe? Consomme du CPU? Effectue-t-il des E/S? Met à jour une base de données? Répond à l'activité, par ex. basculer un mutex ou envoyer un message via une file d'attente de messages? – HABO

Répondre

0

Pour tuer un processus par son nom;

private void TerminateAll() 
    { 
     foreach (var process in Process.GetProcessesByName("exenamewithoutext")) 
     { 
      process?.Kill(); 
     } 
    } 

Voici une idée pour refactoriser votre code.

public class ProcessRunner 
{ 
    /// <summary> 
    /// Gets or sets the running process. 
    /// </summary> 
    private Process RunningProcess { get; set; }  

    /// <summary> 
    /// The exepath of the process. 
    /// </summary> 
    private readonly string exePath; 

    public ProcessRunner(string exePath) 
    { 
     this.exePath = exePath; 
    } 

    /// <summary> 
    /// Runs the specified executable path. 
    /// </summary> 
    /// <param name="exePath">The executable path.</param> 
    public void Run() 
    { 
     var processInfo = new ProcessStartInfo { Arguments = "<Your Args>", FileName = exePath, WindowStyle = ProcessWindowStyle.Normal }; 
     try 
     { 
      this.RunningProcess = Process.Start(processInfo); 
     } 
     catch (Exception exception) 
     { 
      throw new ProcessRunnerException(exception.Message); 
     } 
    } 

    /// <summary> 
    /// Terminates this instance. 
    /// </summary> 
    public void Terminate() 
    { 
     if (this.RunningProcess != null) 
     {    
      this.RunningProcess.Kill(); 
      this.RunningProcess.Dispose(); 
     } 
    } 
} 


public class ProcessManager 
{ 
    private readonly IList<ProcessRunner> MyProcessors{ get; } 

    public ProcessManager() 
    { 
     MyProcessors = new List<ProcessRunner>(); 
     MyProcessors.Add(new ProcessRunner("myexe.exe")); // Add as many as you want. 
    } 

    public void RunAll() 
    { 
     foreach(var proc in MyProcessors) 
     { 
      proc.Run(); 
     } 
    } 

    public void KillAll() 
    { 
     foreach(var proc in MyProcessors) 
     { 
      proc.Terminate(); 
     } 
    } 
}