Есть ли способ убедиться, что фоновый процесс, порожденный моей программой, убит, когда мой процесс завершится?

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

  •  08-07-2019
  •  | 
  •  

Вопрос

Как правило, дочерний процесс выполняется бесконечно, пока не будет уничтожен в фоновом режиме, и я хочу очистить его, когда моя программа по какой-либо причине завершится, т. е. через диспетчер задач.

В настоящее время у меня есть цикл while (Process.GetProcessesByName (" ParentProcess "). Count () > 0), который завершается, если родительский процесс не запущен, но кажется довольно хрупким, и если бы я захотел его работать под отладчиком в Visual Studio, мне нужно добавить " ParentProcess.vshost " или что-то.

Есть ли способ убедиться, что дочерний процесс завершается, не требуя, чтобы дочерний процесс знал о родительском процессе? Я бы предпочел решение в управляемом коде, но если его нет, я могу PInvoke.

Редактировать. Передача PID кажется более надежным решением, но ради любопытства, что если дочерний процесс был не моим кодом, а каким-то файлом, который я не могу контролировать? Есть ли способ защиты от возможного создания осиротевших дочерних процессов?

Это было полезно?

Решение

Если дочерний процесс является вашим собственным кодом, вы можете передать ему PID родительского процесса при запуске. Затем дочерний процесс может извлечь процесс с помощью Process.GetProcessById и подпишитесь на его Exited с обработчиком, который корректно завершает остальную часть (дочернего) процесса. Обратите внимание, что вам нужно установить EnableRaisingEvents для свойства процесса: true .

Другие советы

Если дочерний процесс не является вашим собственным кодом, вы можете использовать этот код для поиска и уничтожения всех дочерних процессов:

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

Общий термин для такого дочернего процесса как сиротский процесс . См. Связанную статью для некоторых возможных решений.

Здесь приведен исходный код небольшого служебного приложения, которое я создал (это основано на решении Алана Хензеля, которое я нашел довольно полезным).

Он называется ChildrenProcessKiller и является наблюдателем, который позволяет уничтожить все процессы-потомки данного родительского процесса при выходе из родительского процесса (даже в случае сбоя родительского процесса)

Использование:

ChildrenProcessKiller.exe parentProcessId

Предупреждение: этот код предоставляется "как есть" и может убить маленьких детей ;-)

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

Передайте идентификатор родительского процесса в качестве параметра командной строки дочернему процессу.

В дочернем процессе используйте get Process по id и подпишитесь на его событие Exit или создайте поток и вызовите Process.WaitForExit

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top