How To Kill a Process Tree

Using Task Manager it's easy to kill a process and all processes started by it. For the Console Process class I needed a way to do this programmatically. It seems easy enough, especially since the Process class in System.Diagnostics comes with a handy method called GetProcesses() that lists all running processes on the system. So it's just a question of enumerating those, finding out which ones depend on the process we want to kill and killing those too, right?

Wrong. There is no .NET way to determine the process ID of the parent of a process. For that, you need a rather old fashioned looking method in NTDLL.DLL called NtQueryInformationProcess(). It allows you to get information about a process in several ways. One of those is to get a structure of type PROCESS_BASIC_INFORMATION filled with info about the process — and lo and behold: there's our parent ID in InheritedFromUniqueProcessId.

The rest is straightforward: enumerate the processes on the system, being careful to catch exceptions that occur if we try to query information about a system process. Find out which processes have a parent process-ID of the process we want to kill, and call the same method on those, too.

Here's the code:

///<summary>
/// Terminate a process tree
///</summary>
///<param name="hProcess">The handle of the process</param>
///<param name="processID">The ID of the process</param>
///<param name="exitCode">The exit code of the process</param>
public void TerminateProcessTree(IntPtr hProcess, uint processID, int exitCode)
{
  // Retrieve all processes on the system
  Process[] processes = Process.GetProcesses();
  foreach (Process p in processes)
  {
    // Get some basic information about the process
    PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
    try
    {
      uint bytesWritten;
      NtQueryInformationProcess(p.Handle,
        0, ref pbi, (uint)Marshal.SizeOf(pbi),
        out bytesWritten); // == 0 is OK
 
      // Is it a child process of the process we're trying to terminate?
      if (pbi.InheritedFromUniqueProcessId == processID)
        // The terminate the child process and its child processes
        TerminateProcessTree(p.Handle, pbi.UniqueProcessId, exitCode);
    }
    catch (Exception /* ex */)
    {
      // Ignore, most likely 'Access Denied'
    }
  }
 
  // Finally, termine the process itself:
  TerminateProcess((uint)hProcess, exitCode);
}

Of course you need to import the System.Diagnostics namespace, but you also need the following Windows API-stuff:

///<summary>
/// Terminate a process tree
///</summary>
///<param name="hProcess">The handle of the process</param>
///<param name="processID">The ID of the process</param>
///<param name="exitCode">The exit code of the process</param>
public void TerminateProcessTree(IntPtr hProcess, uint processID, int exitCode)
{
  // Retrieve all processes on the system
  Process[] processes = Process.GetProcesses();
  foreach (Process p in processes)
  {
    // Get some basic information about the process
    PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
    try
    {
      uint bytesWritten;
      NtQueryInformationProcess(p.Handle,
        0, ref pbi, (uint)Marshal.SizeOf(pbi),
        out bytesWritten); // == 0 is OK
 
      // Is it a child process of the process we're trying to terminate?
      if (pbi.InheritedFromUniqueProcessId == processID)
        // The terminate the child process and its child processes
        TerminateProcessTree(p.Handle, pbi.UniqueProcessId, exitCode);
    }
    catch (Exception /* ex */)
    {
      // Ignore, most likely 'Access Denied'
    }
  }
 
  // Finally, termine the process itself:
  TerminateProcess((uint)hProcess, exitCode);
}