13

This has been asked before but I can't find a definitive answer, in code.

I open a process, ProcessA (with PID 1234). This process opens a child process, ProcessAB (PID 5678). After I'm done I terminate ProcessA but I still have the lingering of ProcessAB.

How do I terminate the whole process tree? What I mean, how do I make sure that if I terminate the process I opened I am also terminating all the associated processes?

Thanks

Code is appreciated.

wonderer
  • 3,487
  • 11
  • 49
  • 59

7 Answers7

11

Check this thread for grouping processes within a "job".

If that does not work for you, a home grown approach might go as follows:

  1. Get your main process ID
  2. Call CreateToolhelp32Snapshot to enumerateall the processes on the system
  3. Check the th32ParentProcessID member of the PROCESSENTRY32 structure on each process, if it matches your parent ID, then terminate the process (using TerminateProcess)
  4. After all children are terminated, terminate the main process

Sample code:

    DWORD myprocID = 1234; // your main process id

PROCESSENTRY32 pe;

memset(&pe, 0, sizeof(PROCESSENTRY32));
pe.dwSize = sizeof(PROCESSENTRY32);

HANDLE hSnap = :: CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if (::Process32First(hSnap, &pe))
{
    BOOL bContinue = TRUE;

    // kill child processes
    while (bContinue)
    {
        // only kill child processes
        if (pe.th32ParentProcessID == myprocID)
        {
            HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);

            if (hChildProc)
            {
                ::TerminateProcess(hChildProc, 1);
                ::CloseHandle(hChildProc);
            }               
        }

        bContinue = ::Process32Next(hSnap, &pe);
    }

    // kill the main process
    HANDLE hProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, myprocID);

    if (hProc)
    {
        ::TerminateProcess(hProc, 1);
        ::CloseHandle(hProc);
    }       
}
Community
  • 1
  • 1
Mike Marshall
  • 7,788
  • 4
  • 39
  • 63
  • I can't seem to get it right. DO you have a sample snippet? thanks – wonderer Jul 23 '09 at 19:28
  • Sorry, I had written that from memory. The first call should be CreateToolhelp32Snapshot, not EnumProcesses. Sample above. – Mike Marshall Jul 24 '09 at 16:04
  • you need to get debug privileges to open the process but it seems to be working other than that. thanks – wonderer Jul 30 '09 at 18:21
  • @mjmarsh Thanks for this. Note that for the homebrew case, you are missing a recursion. I answered below. – user2346536 Mar 18 '16 at 16:46
  • 1
    Beware there is an issue with this code since it *will* kill extra processes. Windows (at least XP) recycles PIDs quite fast. You have to check the start date of the processes you intend to kill to make sure they are really part of the process tree you originally spawned. – bltxd Apr 27 '16 at 07:56
  • The bottom issue here is that th32ParentProcessID will contain the PID of the parent even if said parent is already dead. So you have to make sure the parent PID was not reused by a later process by comparing start dates. Issue observed on Windows XP a few years ago. – bltxd Apr 27 '16 at 08:06
5

Use Job Objects.

It's the closest thing to a unix 'process group' that windows has to offer.

Job Objects allow you to indicate a child process (and all its children) can be managed together, esp. for being killed. Unlike unix, as of this writing 'job objects' cannot be nested. Which means if a parent creates a job object for a child, all that child's children cannot themselves use Job Objects (which is a /severe/ limitation IMHO, like a file system that only allows one level of sub directories).

seriss
  • 51
  • 1
  • 1
3

To kill a whole tree with ALL!!! childs:

bool __fastcall KillProcessTree(DWORD myprocID, DWORD dwTimeout)
{
  bool bRet = true;
  HANDLE hWnd;
  PROCESSENTRY32 pe;

  memset(&pe, 0, sizeof(PROCESSENTRY32));
  pe.dwSize = sizeof(PROCESSENTRY32);

  HANDLE hSnap = :: CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

  if (::Process32First(hSnap, &pe))
  {
    BOOL bContinue = TRUE;

    // kill child processes
    while (bContinue)
    {
      if (pe.th32ParentProcessID == myprocID)
      {
        ShowMessage ("Gleich - KILL PID: " + AnsiString(pe.th32ProcessID));

        // Rekursion
        KillProcessTree(pe.th32ProcessID, dwTimeout);

        HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);

        if (hChildProc)
        {
          GetWindowThreadProcessId(hWnd, &myprocID);
          // CLOSE Message s
          PostMessage(hWnd, WM_CLOSE, 0, 0) ;

          if (WaitForSingleObject(hChildProc, dwTimeout) == WAIT_OBJECT_0)
            bRet = true;
          else
          {
            bRet = TerminateProcess(hChildProc, 0);
          }
          ::CloseHandle(hChildProc);
        }
      }
      bContinue = ::Process32Next(hSnap, &pe);
    }

    // kill the main process
    HANDLE hProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, myprocID);

    if (hProc)
    {
        ::TerminateProcess(hProc, 1);
        ::CloseHandle(hProc);
    }
  }
  return bRet;
}
leonbloy
  • 73,180
  • 20
  • 142
  • 190
Michael
  • 31
  • 1
  • 2
    You may not call GetWindowThreadProcessId with hWnd undefined. While the code definitely demonstrates a good idea to try to close windows nicely, it does not work. – Alex Jul 10 '13 at 09:01
1

@mjmarsh answers needs recursion for the homebrew case, otherwise it is the right one. Creating job objects is better that the below when you can.

void KillProcessTree(DWORD myprocID)
{
    PROCESSENTRY32 pe;

    memset(&pe, 0, sizeof(PROCESSENTRY32));
    pe.dwSize = sizeof(PROCESSENTRY32);

    HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if (::Process32First(hSnap, &pe))
    {
        do // Recursion
        {
            if (pe.th32ParentProcessID == myprocID)
                KillProcessTree(pe.th32ProcessID);
        } while (::Process32Next(hSnap, &pe));
    }


    // kill the main process
    HANDLE hProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, myprocID);

    if (hProc)
    {
        ::TerminateProcess(hProc, 1);
        ::CloseHandle(hProc);
    }
}
user2346536
  • 1,464
  • 2
  • 21
  • 43
  • 1
    Same issue here, since Windows (at least XP) recycles PIDs quite fast, you have to be extra careful not to kill unrelated processes. These APIs will report PPIDs for long dead processes whose PIDs may have been recycled. Check the parent start date to make sure it is related to the processes you spawned. – bltxd Apr 27 '16 at 08:01
0

There's How To Kill a Process Tree, but it's in C#. I don't think it's too hard to port that to C.

See NtQueryInformationProcess Function and TerminateProcess Function.

Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
  • I tried converting that code yesterday but no luck. anyway, I am already using Ntquery and terminateprocess but those only take care of the parent and not the rest – wonderer Jul 23 '09 at 18:31
0

Pure C (if you don't count the exe call as non-C code) very quick & dirty approach: use system() with taskkill.exe

/* Kill process tree in C on windows: quick and very dirty solution */
void killtree(DWORD pid, char force, char tree) {
  char cmd[1024];
  char *forceflag = force ? " /F" : "";
  char *treeflag = tree ? " /T" : "";
  sprintf(cmd, "taskkill%s%s /PID %lu", forceflag, treeflag, pid);
  system(cmd);

  /* optional additional code-- see below */
  if(process_is_running(pid)) {
    fprintf(errlog, "couldn't kill %lu\r\n", pid);
    fflush(errlog);
  }
}

/* if you want, you might also want to use process_is_running() */
static char process_is_running(DWORD pid) {
  if(!pid)
    return 0;
  HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid);
  DWORD ret = WaitForSingleObject(process, 0);
  CloseHandle(process);
  return ret == WAIT_TIMEOUT;
}
mwag
  • 3,557
  • 31
  • 38
  • Presumably this was downvoted because it uses a system call with taskkill.exe... yes, it's ugly, and nonetheless it works, it answers the OP's question here, it is in C (not C++) and is not subject to "accidentally kill other process" caveats-- which is more than many other answers here can claim. – mwag Aug 21 '22 at 03:58
-1

The following is for Linux, but I hope it helps for Windows with some adaptation.

When you fork(), save the return value, which is the pid of the child process, then when the parent is about to exit, kill() the pid.

If you have multiple child processes, you can send the kill to the process group. By default, the child processes have the same pgid as the parent.

Mike Mu
  • 977
  • 6
  • 10
  • 1
    1) I wish i was working on Linux 2) I wish windows had fork() 3) The process I am opening is opening the child processes so I have no control over that but thanks anyway – wonderer Jul 23 '09 at 18:30