【问题标题】:Terminate a process tree (C for Windows)终止进程树(C for Windows)
【发布时间】:2010-11-13 11:40:52
【问题描述】:

以前有人问过这个问题,但我在代码中找不到明确的答案。

我打开一个进程 ProcessA(PID 为 1234)。此进程打开一个子进程 ProcessAB (PID 5678)。 完成后,我终止了 ProcessA,但我仍然有 ProcessAB 的挥之不去。

如何终止整个进程树?我的意思是,我如何确保如果我终止我打开的进程,我也会终止所有关联的进程?

谢谢

感谢代码。

【问题讨论】:

    标签: c windows process terminate


    【解决方案1】:

    检查 this thread 以在“作业”中对进程进行分组。

    如果这对您不起作用,则可以采用以下本土方法:

    1. 获取您的主进程 ID
    2. 调用CreateToolhelp32Snapshot枚举系统上的所有进程
    3. 检查每个进程的 PROCESSENTRY32 结构的 th32ParentProcessID 成员,如果它与您的父 ID 匹配,则终止进程(使用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);
        }       
    }
    

    【讨论】:

    • 我似乎无法正确理解。你有样品sn-p吗?谢谢
    • 对不起,我是凭记忆写的。第一个调用应该是 CreateToolhelp32Snapshot,而不是 EnumProcesses。上面的示例。
    • 您需要获得调试权限才能打开该进程,但它似乎除此之外还在工作。谢谢
    • @mjmarsh 谢谢你。请注意,对于自制案例,您缺少递归。我在下面回答。
    • 请注意此代码存在问题,因为它杀死额外的进程。 Windows(至少是 XP)回收 PID 的速度非常快。您必须检查您打算终止的进程的开始日期,以确保它们确实是您最初生成的进程树的一部分。
    【解决方案2】:

    使用Job Objects

    这是最接近 windows 必须提供的 unix“进程组”的东西。

    作业对象允许您指示子进程(及其所有子进程)可以一起管理,尤其是。因为被杀。与 unix 不同,在撰写本文时,“作业对象”不能嵌套。这意味着如果父母为孩子创建作业对象,则该孩子的所有孩子自己都不能使用作业对象(恕我直言,这是 / 严重 / 限制,就像只允许一级子目录的文件系统)。

    【讨论】:

    • Windows 8 和 Windows Server 2012 添加了对嵌套作业对象的支持。
    【解决方案3】:

    用 ALL 杀死一棵树!!!孩子们:

    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;
    }
    

    【讨论】:

    • 您不能在未定义 hWnd 的情况下调用 GetWindowThreadProcessId。虽然该代码确实展示了一个尝试很好地关闭窗口的好主意,但它不起作用。
    【解决方案4】:

    How To Kill a Process Tree,但它在 C# 中。我认为将它移植到 C 中并不难。

    请参阅 NtQueryInformationProcess FunctionTerminateProcess Function

    【讨论】:

    • 我昨天尝试转换该代码,但没有成功。无论如何,我已经在使用 Ntquery 和 terminateprocess 但那些只照顾父母而不是其他人
    【解决方案5】:

    @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);
        }
    }
    

    【讨论】:

    • 这里也是同样的问题,因为 Windows(至少是 XP)回收 PID 的速度非常快,所以您必须格外小心,不要杀死不相关的进程。这些 API 将报告其 PID 可能已被回收的长期死进程的 PPID。检查父开始日期以确保它与您生成的进程相关。
    【解决方案6】:

    纯 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;
    }
    

    【讨论】:

      【解决方案7】:

      以下内容适用于 Linux,但我希望它对 Windows 有所帮助。

      fork()时,保存返回值,即子进程的pid,然后在父进程即将退出时,kill()该pid。

      如果您有多个子进程,您可以将 kill 发送到进程组。默认情况下,子进程的 pgid 与父进程相同。

      【讨论】:

      • 1) 我希望我在 Linux 上工作 2) 我希望 windows 有 fork() 3) 我正在打开的进程正在打开子进程,所以我无法控制它,但无论如何谢谢跨度>
      猜你喜欢
      • 1970-01-01
      • 2011-08-02
      • 1970-01-01
      • 2013-06-29
      • 2011-01-15
      • 1970-01-01
      • 2012-04-24
      • 2010-11-26
      • 1970-01-01
      相关资源
      最近更新 更多