How terminate child processes when parent process terminated in C#

LionSoft picture LionSoft · Jul 13, 2010 · Viewed 24.5k times · Source

Task: Auto kill all child processes if parent process terminate. Parent procees can be terminated not only in correct way, but also by killing in ProcessExplorer, for example. How can I do it?

Similar question in С topic advice to use Job objects. How to use it in C# without exporting external DLL?


I tried to use Job Objects. But this code doesn't work properly:

  var job = PInvoke.CreateJobObject(null, null);
  var jobli = new PInvoke.JOBOBJECT_BASIC_LIMIT_INFORMATION();

  jobli.LimitFlags = PInvoke.LimitFlags.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
                   | PInvoke.LimitFlags.JOB_OBJECT_LIMIT_PRIORITY_CLASS
                   | PInvoke.LimitFlags.JOB_OBJECT_LIMIT_JOB_TIME
                   | PInvoke.LimitFlags.JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION
                   | PInvoke.LimitFlags.JOB_OBJECT_LIMIT_JOB_MEMORY;

  var res = PInvoke.SetInformationJobObject(job, PInvoke.JOBOBJECTINFOCLASS.JobObjectBasicLimitInformation, jobli, 48);

  if (!res)
  {
    int b = PInvoke.GetLastError();
    Console.WriteLine("Error " + b);
  }

  var Prc = Process.Start(...);

  PInvoke.AssignProcessToJobObject(job, Prc.Handle);

PInvoke.SetInformationJobObject returns with error. GetLastError returns error 24. However, PInvoke.AssignProcessToJobObject works and child process added to Job Queue (I can see it in ProcessExplorer). But, because PInvoke.SetInformationJobObject don't work - spawned process stay alive when I kill parent one.

What do I have incorrect in this code?

Answer

milkplus picture milkplus · Apr 15, 2011

To kill a process tree on windows, given only the parent process or process id, you'll need to walk the process tree.

For that, you'll need a way to get the parent process id for a given process.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Management;

namespace KillProcessTree
{

public static class MyExtensions
{
    public static int GetParentProcessId(this Process p)
    {
        int parentId = 0;
        try
        {
            ManagementObject mo = new ManagementObject("win32_process.handle='" + p.Id + "'");
            mo.Get();
            parentId = Convert.ToInt32(mo["ParentProcessId"]);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
            parentId = 0;
        }
        return parentId;
    }
}

Once you have that, actually killing the tree is not hard.

class Program
{
    /// <summary>
    /// Kill specified process and all child processes
    /// </summary>
    static void Main(string[] args)
    {
        if (args.Length < 1)
        {
            Console.WriteLine("Usage: KillProcessTree <pid>");
            return;
        }

        int pid = int.Parse(args[0]);

        Process root = Process.GetProcessById(pid);
        if (root != null)
        {
            Console.WriteLine("KillProcessTree " + pid);

            var list = new List<Process>();
            GetProcessAndChildren(Process.GetProcesses(), root, list, 1);

            // kill each process
            foreach (Process p in list)
            {
                try
                {
                    p.Kill();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
        }
        else
        {
            Console.WriteLine("Unknown process id: " + root);
        }
    }

    /// <summary>
    /// Get process and children
    /// We use postorder (bottom up) traversal; good as any when you kill a process tree </summary>
    /// </summary>
    /// <param name="plist">Array of all processes</param>
    /// <param name="parent">Parent process</param>
    /// <param name="output">Output list</param>
    /// <param name="indent">Indent level</param>
    private static void GetProcessAndChildren(Process[] plist, Process parent, List<Process> output, int indent)
    {
        foreach (Process p in plist)
        {
            if (p.GetParentProcessId() == parent.Id)
            {
                GetProcessAndChildren(plist, p, output, indent + 1);
            }
        }
        output.Add(parent);
        Console.WriteLine(String.Format("{0," + indent*4 + "} {1}", parent.Id, parent.MainModule.ModuleName));
    }
}
} // namespace