Is there a way to make sure that the background process generated by my program was killed when my process terminated?

Basically, the child process runs indefinitely until it is killed in the background, and I want to clear it when my program exits for some reason, that is, through the task manager.

I currently have some time (Process.GetProcessesByName ("ParentProcess"). Count ()> 0) loop and exit if the parent process is not running but it looks pretty fragile and if I wanted it to work under debugger in Visual Studio I need to add "ParentProcess.vshost" or something like that.

Is there a way to make sure the child process is complete without requiring the child process to know about the parent process? I would prefer a solution in managed code, but if not, I can PInvoke.

Edit: Passing PID seems like a more reliable solution, but for the sake of curiosity, what if the child process was not my code, but some exe that I have no control over? Is there a way to protect against the possible creation of orphaned child processes?

+7
c # child-process
source share
5 answers

If the child process is your own code, you can pass its PID to the parent process when it starts. Then the child process could receive the Process.GetProcessById process and subscribe to Exited with a handler that gracefully terminates the remaining (child) process. Note that you need to set the EnableRaisingEvents property to true .

+7
source share

If the child process is not your own code, you can use this code to search and kill all child processes:

 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; } } } 
+5
source share

A generic term for such a child process as an orphan process . See the related article for some possible solutions.

+3
source share

Here is the source code for a small utility application that I built (it is based on a solution by Alan Hensel, which I found very useful).

It is called ChildrenProcessKiller, and it is an observer that allows you to kill all the processes of the descendants of this parent process when the parent process terminates (even if the parent process fails)

Using:

 ChildrenProcessKiller.exe parentProcessId 

Warning: this code is provided "as is" and it can kill small children ;-)

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; } } } 
+1
source share

Pass the parent process identifier as the command line parameter for the child process.

In the process of the child process, get the process by id and subscribe to it. Exit the event or create a thread and call Process.WaitForExit

0
source share

All Articles