¿Hay alguna manera de asegurarse de que un proceso en segundo plano generado por mi programa se elimine cuando finalice mi proceso?

StackOverflow https://stackoverflow.com/questions/632148

  •  08-07-2019
  •  | 
  •  

Pregunta

Básicamente, el proceso secundario se ejecuta indefinidamente hasta que se elimina en segundo plano, y quiero limpiarlo cuando mi programa finalice por cualquier motivo, es decir, a través del Administrador de tareas.

Actualmente tengo un tiempo (Process.GetProcessesByName (" ParentProcess "). Count () > 0) y salgo si el proceso padre no se está ejecutando, pero parece bastante frágil, y si quisiera trabajar bajo depurador en Visual Studio, tendría que agregar '' ParentProcess.vshost '' o algo.

¿Hay alguna manera de asegurarse de que el proceso secundario finalice sin requerir que el proceso secundario sepa sobre el proceso primario? Prefiero una solución en código administrado, pero si no hay una, puedo invocar.

Editar: Pasar el PID parece una solución más sólida, pero por curiosidad, ¿qué pasa si el proceso secundario no fue mi código sino algún exe sobre el que no tengo control? ¿Hay alguna manera de protegerse contra la posible creación de procesos secundarios huérfanos?

¿Fue útil?

Solución

Si el proceso secundario es su propio código, puede pasarle el PID del proceso primario cuando lo inicie. El proceso secundario podría obtener el proceso con Process.GetProcessById y suscríbase a su Salió evento con un controlador que cierra el resto del proceso (secundario) con gracia. Tenga en cuenta que debe configurar EnableRaisingEvents en el proceso para true .

Otros consejos

Si el proceso secundario no es su propio código, puede usar este código para buscar y eliminar todos los procesos secundarios:

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Util {
    public static class ProcessExtensions {
        public static void KillDescendants(this Process processToNotKillYet) {
            foreach (var eachProcess in Process.GetProcesses()) {
                if (eachProcess.ParentPid() == processToNotKillYet.Id) {
                    eachProcess.KillTree();
                }
            }
        }

        public static void KillTree(this Process processToKill) {
            processToKill.KillDescendants();
            processToKill.Kill();
        }

        public static PROCESS_BASIC_INFORMATION Info(this Process process) {
            var processInfo = new PROCESS_BASIC_INFORMATION();
            try {
                uint bytesWritten;
                NtQueryInformationProcess(process.Handle,
                                          0,
                                          ref processInfo,
                                          (uint)Marshal.SizeOf(processInfo),
                                          out bytesWritten); // == 0 is OK
            }
            catch (Win32Exception e) {
                if (!e.Message.Equals("Access is denied")) throw;
            }

            return processInfo;
        }

        public static int ParentPid(this Process process) {
            return process.Info().ParentPid;
        }

        [DllImport("ntdll.dll")]
        private static extern int NtQueryInformationProcess(
            IntPtr hProcess,
            int processInformationClass /* 0 */,
            ref PROCESS_BASIC_INFORMATION processBasicInformation,
            uint processInformationLength,
            out uint returnLength);

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_BASIC_INFORMATION {
            public int ExitStatus;
            public int PebBaseAddress;
            public int AffinityMask;
            public int BasePriority;
            public int Pid;
            public int ParentPid;
        }
    }
}

El término común para dicho proceso secundario en proceso huérfano . Consulte el artículo vinculado para conocer algunas posibles soluciones.

Aquí el código fuente de una pequeña aplicación de utilidad que construí (se basa en la solución de Alan Hensel, que me pareció bastante útil).

Se llama ChildrenProcessKiller y es un observador que permite eliminar todos los procesos descendientes de un proceso padre dado cuando el proceso padre se cierra (incluso si el proceso padre falla)

Uso:

ChildrenProcessKiller.exe parentProcessId

Advertencia: este código se proporciona "como está" y podría matar a bebés pequeños ;-)

ChildrenProcessKiller.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace ChildrenProcessKiller
{
  static class ChildrenProcessKiller
  {
    [STAThread]
    static void Main(string[] args)
    {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);

      string message = "This is a watcher that enables to kill all descendants process of a given parent process\n";
      message += "when the parent process exits (even if the parent process crashes) \n\n";
      message += "Usage : " + Application.ExecutablePath + " parentProcessId";

      if (args.Length != 1)
      {
        MessageBox.Show(message);
        System.Environment.Exit(1);
      }

      int parentProcessId;
      if (!Int32.TryParse(args[0], out parentProcessId))
      {
        MessageBox.Show(message);
        System.Environment.Exit(1);
      }

      try
      {
        mParentProcess = Process.GetProcessById(parentProcessId);
      }
      catch (System.ArgumentException ex)
      {
        //Parent process cannot be found!
        System.Environment.Exit(2);
      }
      Run();
    }

    private static List<Process> mChildrenProcesses;
    private static Process mParentProcess;

    private static void Run()
    {
      int thisProcessId = Process.GetCurrentProcess().Id;
      while ( ! mParentProcess.HasExited )
      {
        RefreshChildrenProcesses();
        System.Threading.Thread.Sleep(1000);
      }

      foreach (Process childProcess in mChildrenProcesses)
      {
        if ((!childProcess.HasExited) && (childProcess.Id != thisProcessId))
        {
          KillGracefullyThenViolently(childProcess);
        }
      }
    }

    private static void KillGracefullyThenViolently(Process process)
    {
      if (process.HasExited)
        return;

      try
      {
        process.CloseMainWindow();
      }
      catch (PlatformNotSupportedException)
      {}
      catch (InvalidOperationException)
      {}//do nothing : this app is meant to be "unstoppable", unless the parent process has exited

      for (int i = 0; i < 15; i++)
      {
        System.Threading.Thread.Sleep(100);
        if (process.HasExited)
          return;
      }

      try
      {
        process.Kill();
      }
      catch (System.ComponentModel.Win32Exception)
      {}
      catch(NotSupportedException)
      {}
      catch(InvalidOperationException)
      {} //same comment here
    }

    private static void RefreshChildrenProcesses()
    {
      if (mParentProcess.HasExited)
        return;
      List<Process> newChildren;
      try
      {
        newChildren = Utils.ProcessTree.GetProcessDescendants(mParentProcess);
        mChildrenProcesses = newChildren;
      }
      catch (System.Exception ex)
      {
        ; 
      }
    }


  }
}

ProcessTree.cs

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;

namespace Utils
{
  public static class ProcessTree
  {

    public static List<Process> GetProcessDescendants(Process process)
    {
      List<Process> result = new List<Process>();
      foreach (Process eachProcess in Process.GetProcesses())
      {
        if (ParentPid(eachProcess) == process.Id)
        {
          result.Add(eachProcess);
        }
      }
      return result;
    }

    public static void KillDescendants(Process processToNotKillYet) 
    {
      foreach (Process eachProcess in Process.GetProcesses()) 
      {
        if (ParentPid(eachProcess) == processToNotKillYet.Id) 
        {
          if (eachProcess.Id != Process.GetCurrentProcess().Id)
            KillTree(eachProcess);
        }
      }
    }

    public static void KillTree(Process processToKill) 
    {
      KillDescendants(processToKill);
      processToKill.Kill();
    }

    public static PROCESS_BASIC_INFORMATION Info(Process process) 
    {
      PROCESS_BASIC_INFORMATION processInfo = new PROCESS_BASIC_INFORMATION();
      try
      {
        uint bytesWritten;
        NtQueryInformationProcess(process.Handle,
                        0,
                        ref processInfo,
                        (uint)Marshal.SizeOf(processInfo),
                        out bytesWritten); // == 0 is OK
      }
      catch (Win32Exception e) 
      {
        if (!e.Message.Equals("Access is denied")) throw;
      }

      return processInfo;
    }

    public static int ParentPid(Process process) 
    {
      return Info(process).ParentPid;
    }

    [DllImport("ntdll.dll")]
    private static extern int NtQueryInformationProcess(
      IntPtr hProcess,
      int processInformationClass /* 0 */,
      ref PROCESS_BASIC_INFORMATION processBasicInformation,
      uint processInformationLength,
      out uint returnLength);

    [StructLayout(LayoutKind.Sequential)]
    public struct PROCESS_BASIC_INFORMATION 
    {
      public int ExitStatus;
      public int PebBaseAddress;
      public int AffinityMask;
      public int BasePriority;
      public int Pid;
      public int ParentPid;
    }
  }
}

Pase la identificación del proceso principal como parámetro de línea de comando al proceso secundario.

En el proceso secundario, use get Process by id y suscríbase a su evento Exit o cree un hilo y llame a Process.WaitForExit

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top