Завершить дерево процессов (C для Windows)

Об этом уже спрашивали, но я не могу найти окончательного ответа в коде.

Я открываю процесс ProcessA (с PID 1234). Этот процесс открывает дочерний процесс ProcessAB (PID 5678). После того, как я закончу, я прекращаю ProcessA, но у меня все еще есть ProcessAB.

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

Спасибо

Код приветствуется.


person wonderer    schedule 23.07.2009    source источник


Ответы (7)


Проверьте эту ветку для группировки процессов в рамках «задания».

Если это не сработает для вас, доморощенный подход может выглядеть следующим образом:

  1. Получите свой основной идентификатор процесса
  2. Вызов CreateToolhelp32Snapshot. перечислить все процессы в системе
  3. Проверьте член th32ParentProcessID структуры PROCESSENTRY32 для каждого процесса, если он соответствует вашему родительскому идентификатору, затем завершите процесс (используя TerminateProcess)
  4. После завершения всех дочерних процессов завершите основной процесс

Образец кода:

    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);
    }       
}
person Mike Marshall    schedule 23.07.2009
comment
Кажется, я не могу понять это правильно. У вас есть образец фрагмента? Благодарность - person wonderer; 23.07.2009
comment
Извините, я написал это по памяти. Первым вызовом должен быть CreateToolhelp32Snapshot, а не EnumProcesses. Образец выше. - person Mike Marshall; 24.07.2009
comment
вам нужно получить права отладки, чтобы открыть процесс, но, похоже, он работает иначе. Благодарность - person wonderer; 30.07.2009
comment
@mjmarsh Спасибо за это. Обратите внимание, что в случае домашнего пивоварения вам не хватает рекурсии. Я ответил ниже. - person user2346536; 18.03.2016
comment
Остерегайтесь проблемы с этим кодом, поскольку он убьет лишние процессы. Windows (по крайней мере, XP) очень быстро перерабатывает PID. Вы должны проверить дату начала процессов, которые вы собираетесь убить, чтобы убедиться, что они действительно являются частью дерева процессов, которое вы изначально создали. - person bltxd; 27.04.2016
comment
Основная проблема здесь в том, что th32ParentProcessID будет содержать PID родителя, даже если этот родитель уже мертв. Таким образом, вы должны убедиться, что родительский PID не был повторно использован более поздним процессом, сравнив даты начала. Проблема наблюдалась в Windows XP несколько лет назад. - person bltxd; 27.04.2016

Используйте объекты задания.

Это самое близкое к «группе процессов» unix, что может предложить Windows.

Объекты заданий позволяют указать, что дочерним процессом (и всеми его дочерними процессами) можно управлять вместе, особенно. за то, что его убили. В отличие от unix, на момент написания «объекты задания» не могли быть вложенными. Это означает, что если родитель создает объект задания для дочернего элемента, все дочерние элементы этого ребенка не могут сами использовать объекты задания (что является / серьезным / ограничением IMHO, как файловая система, которая допускает только один уровень подкаталогов).

person seriss    schedule 26.05.2011
comment
Поддержка вложенности объектов заданий была добавлена ​​в Windows 8 и Windows Server 2012. - person Esoteric Screen Name; 07.08.2012

Убить целое дерево ВСЕМИ !!! Дети:

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;
}
person Michael    schedule 28.01.2010
comment
Вы не можете вызывать GetWindowThreadProcessId с неопределенным hWnd. Хотя код определенно демонстрирует хорошую идею попытаться красиво закрыть окна, он не работает. - person Alex; 10.07.2013

Есть Как убить дерево процессов, но он написан на C #. Я не думаю, что это слишком сложно перенести на C.

См. функцию NtQueryInformationProcess и Функция TerminateProcess.

person Eugene Yokota    schedule 23.07.2009
comment
Вчера я попытался преобразовать этот код, но безуспешно. в любом случае, я уже использую Ntquery и terminateprocess, но они заботятся только о родителе, а не об остальных - person wonderer; 23.07.2009

Ответы @mjmarsh нуждаются в рекурсии для домашнего пивоварения, в противном случае это правильный вариант. Создавать рабочие объекты лучше, чем описанные ниже, когда вы можете.

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);
    }
}
person user2346536    schedule 18.03.2016
comment
Здесь та же проблема, поскольку Windows (по крайней мере, XP) очень быстро перерабатывает PID, вы должны быть особенно осторожны, чтобы не убить несвязанные процессы. Эти API-интерфейсы будут сообщать PPID для давно мертвых процессов, PID которых, возможно, были переработаны. Проверьте родительскую дату начала, чтобы убедиться, что она связана с созданными вами процессами. - person bltxd; 27.04.2016

Чистый C - очень быстрый и грязный подход: используйте system() с 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;
}
person mwag    schedule 09.04.2020

Следующее относится к Linux, но я надеюсь, что это поможет для Windows с некоторой адаптацией.

Когда вы используете fork (), сохраните возвращаемое значение, которое является pid дочернего процесса, а затем, когда родительский процесс собирается выйти, kill () pid.

Если у вас несколько дочерних процессов, вы можете отправить уничтожение группе процессов. По умолчанию дочерние процессы имеют тот же pgid, что и родительский.

person Mike Mu    schedule 23.07.2009
comment
1) Мне жаль, что я не работал в Linux 2) Я бы хотел, чтобы в Windows была fork () 3) Процесс, который я открываю, открывает дочерние процессы, поэтому я не могу это контролировать, но все равно спасибо - person wonderer; 23.07.2009