【问题标题】:C++, How to determine if a Windows Process is running?C++,如何确定 Windows 进程是否正在运行?
【发布时间】:2010-12-08 03:50:52
【问题描述】:

这与 Windows XP 进程有关。

我有一个进程正在运行,我们称之为 Process1。 Process1 创建一个新进程 Process2,并保存它的 id。

现在,在某些时候 Process1 想要 Process2 做某事,所以它首先需要确保 Process2 仍然活着并且用户没有杀死它。

如何检查此进程是否仍在运行? 由于我创建了它,我有进程 ID,我认为有一些类似于 IsProcessIDValid(id) 的库函数,但我在 MSDN 上找不到它

【问题讨论】:

  • DCOM 已经免费完成了所有这些工作,为什么还要重新发明轮子?
  • 由于固有的竞争条件,这个计划似乎被打破了。在您检查 Process2 是否还活着并且在您要求它完成您需要的工作之前(或在它完成您需要的工作之前),用户可能会杀死 Process2。您最好向 Process2 发出命令来完成工作,然后等待确认它已完成。在等待期间,您会注意到 Process2 是否消失。

标签: c++ windows process


【解决方案1】:

您永远无法检查进程是否正在运行,您只能检查进程是否在最近的某个时间正在运行。进程是不受您的应用程序控制的实体,可以随时退出。无法保证进程不会在检查是否正在运行和相应操作之间退出。

最好的方法是只执行所需的操作并捕获进程未运行时将引发的异常。

【讨论】:

  • @Michael,这里的意图很明确。 OP 明确表示他们想检查 Process2 是否还活着,以便他们可以用它做点什么。
  • 另一个问题是 PID 可以快速重复使用。因此,如果进程 2 死了,并且 PID 被回收,您可能会认为您的进程仍在运行,而实际上并非如此。
【解决方案2】:

进程句柄在退出时会发出信号。

因此以下内容将起作用(为简洁起见,删除了错误处理):

BOOL IsProcessRunning(DWORD pid)
{
    HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid);
    DWORD ret = WaitForSingleObject(process, 0);
    CloseHandle(process);
    return ret == WAIT_TIMEOUT;
}

请注意,进程 ID 可以回收 - 最好缓存从 CreateProcess 调用返回的句柄。

您还可以使用线程池 API(在 Vista+ 上为 SetThreadpoolWait,在旧平台上为 RegisterWaitForSingleObject)在进程退出时接收回调。

编辑:我错过了原始问题的“想要对流程做点什么”部分。如果可以在某些小窗口中保留可能陈旧的数据,或者如果您想在不尝试操作的情况下使操作失败,则可以使用此技术。您仍然需要处理由于进程退出而导致操作失败的情况。

【讨论】:

  • 这个应该叫WasProcessRunning,叫IsProcessRunning有点用词不当
【解决方案3】:

您可以使用GetExitCodeProcess。如果进程仍在运行(或者如果它碰巧以该退出代码退出:(),它将返回STILL_ACTIVE (259)。

【讨论】:

  • +1 用于使用GetExitCodeProcess,因为 OP 的 Process1 可以在调用 CreateProcess 后保存 Process2 的句柄(此处回复的所有其他人都错过了这一点)并且该句柄可以输入 @ 987654326@
  • 哇,这比使用 OpenProcess(PROCESS_QUERY_INFORMATION 谢谢!
  • GetExitCodeProcess 的链接文档中有一个重要说明:如果进程返回 STILL_ACTIVE 作为退出代码,您的应用程序可以将其解释为仍然处于活动状态,尽管它已经终止。
  • 是的,这是一个巨大的错误设计,真是令人难以置信。
  • 可以查看退出码是否为STILL_ACTIVE,如果是,可以调用WaitForSingleObject(processHandle, 0)查看返回值是否为WAIT_TIMEOUT。如果是,则该进程仍处于活动状态,否则该进程返回 259 作为退出代码。注意:你应该用SYNCHRONIZE期望的访问标志调用OpenProcess(),否则你会得到一个权限错误。
【解决方案4】:

调用EnumProcesses()并检查PID是否在列表中。

http://msdn.microsoft.com/en-us/library/ms682629%28VS.85%29.aspx

【讨论】:

【解决方案5】:

JaredPar 是正确的,因为您无法知道进程是否正在运行。您只能在检查时知道该进程是否正在运行。它可能已经死了。

您还必须注意 PID 可以很快回收。因此,仅仅因为您的 PID 存在一个进程,并不意味着它就是您的进程。

让进程共享一个 GUID。 (进程 1 可以生成 GUID 并将其传递给命令行上的进程 2。)进程 2 应该使用该 GUID 创建一个命名互斥锁。当进程 1 想要检查时,它可以在互斥体上执行 WaitForSingleObject,超时为 0。如果进程 2 消失了,返回码会告诉你互斥体被放弃了,否则你会得到一个超时。

【讨论】:

    【解决方案6】:

    另一种监控子进程的方法是创建一个工作线程:

    1. 调用 CreateProcess()
    2. call WaitForSingleObject() // 工作线程现在将等待子进程完成执行。也可以获取返回码(来自 main() 函数)。

    【讨论】:

      【解决方案7】:

      我今天找到了这个,它是 2003 年的。它通过名称找到一个进程,你甚至不需要 pid。

      \#include windows.h
      
      \#include tlhelp32.h
      
      \#include iostream.h
      
      int FIND_PROC_BY_NAME(const char *);
      
      int main(int argc, char *argv[])
      
      {
      
      //  Check whether a process is currently running, or not
      
      char szName[100]="notepad.exe";   // Name of process to find
      
      int isRunning;
      
          isRunning=FIND_PROC_BY_NAME(szName);
      
          // Note: isRunning=0 means process not found, =1 means yes, it is found in memor
          return isRunning;
      }
      
      int FIND_PROC_BY_NAME(const char *szToFind)
      
      // Created: 12/29/2000  (RK)
      
      // Last modified: 6/16/2003  (RK)
      
      // Please report any problems or bugs to kochhar@physiology.wisc.edu
      
      // The latest version of this routine can be found at:
      
      //     http://www.neurophys.wisc.edu/ravi/software/killproc/
      
      // Check whether the process "szToFind" is currently running in memory
      
      // This works for Win/95/98/ME and also Win/NT/2000/XP
      
      // The process name is case-insensitive, i.e. "notepad.exe" and "NOTEPAD.EXE"
      
      // will both work (for szToFind)
      
      // Return codes are as follows:
      
      //   0   = Process was not found
      
      //   1   = Process was found
      
      //   605 = Unable to search for process
      
      //   606 = Unable to identify system type
      
      //   607 = Unsupported OS
      
      //   632 = Process name is invalid
      
      // Change history:
      
      //  3/10/2002   - Fixed memory leak in some cases (hSnapShot and
      
      //                and hSnapShotm were not being closed sometimes)
      
      //  6/13/2003   - Removed iFound (was not being used, as pointed out
      
      //                by John Emmas)
      
      {
      
          BOOL bResult,bResultm;
          DWORD aiPID[1000],iCb=1000,iNumProc,iV2000=0;
          DWORD iCbneeded,i;
          char szName[MAX_PATH],szToFindUpper[MAX_PATH];
          HANDLE hProc,hSnapShot,hSnapShotm;
          OSVERSIONINFO osvi;
          HINSTANCE hInstLib;
          int iLen,iLenP,indx;
          HMODULE hMod;
          PROCESSENTRY32 procentry;      
          MODULEENTRY32 modentry;
      
          // PSAPI Function Pointers.
           BOOL (WINAPI *lpfEnumProcesses)( DWORD *, DWORD cb, DWORD * );
           BOOL (WINAPI *lpfEnumProcessModules)( HANDLE, HMODULE *,
              DWORD, LPDWORD );
           DWORD (WINAPI *lpfGetModuleBaseName)( HANDLE, HMODULE,
              LPTSTR, DWORD );
      
            // ToolHelp Function Pointers.
            HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD,DWORD) ;
            BOOL (WINAPI *lpfProcess32First)(HANDLE,LPPROCESSENTRY32) ;
            BOOL (WINAPI *lpfProcess32Next)(HANDLE,LPPROCESSENTRY32) ;
            BOOL (WINAPI *lpfModule32First)(HANDLE,LPMODULEENTRY32) ;
            BOOL (WINAPI *lpfModule32Next)(HANDLE,LPMODULEENTRY32) ;
      
          // Transfer Process name into "szToFindUpper" and
          // convert it to upper case
          iLenP=strlen(szToFind);
          if(iLenP<1 || iLenP>MAX_PATH) return 632;
          for(indx=0;indx<iLenP;indx++)
              szToFindUpper[indx]=toupper(szToFind[indx]);
          szToFindUpper[iLenP]=0;
      
          // First check what version of Windows we're in
          osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
          bResult=GetVersionEx(&osvi);
          if(!bResult)     // Unable to identify system version
              return 606;
      
          // At Present we only support Win/NT/2000 or Win/9x/ME
          if((osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) &&
              (osvi.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS))
              return 607;
      
          if(osvi.dwPlatformId==VER_PLATFORM_WIN32_NT)
          {
              // Win/NT or 2000 or XP
      
               // Load library and get the procedures explicitly. We do
               // this so that we don't have to worry about modules using
               // this code failing to load under Windows 95, because
               // it can't resolve references to the PSAPI.DLL.
               hInstLib = LoadLibraryA("PSAPI.DLL");
               if(hInstLib == NULL)
                  return 605;
      
               // Get procedure addresses.
               lpfEnumProcesses = (BOOL(WINAPI *)(DWORD *,DWORD,DWORD*))
                  GetProcAddress( hInstLib, "EnumProcesses" ) ;
               lpfEnumProcessModules = (BOOL(WINAPI *)(HANDLE, HMODULE *,
                  DWORD, LPDWORD)) GetProcAddress( hInstLib,
                  "EnumProcessModules" ) ;
               lpfGetModuleBaseName =(DWORD (WINAPI *)(HANDLE, HMODULE,
                  LPTSTR, DWORD )) GetProcAddress( hInstLib,
                  "GetModuleBaseNameA" ) ;
      
               if( lpfEnumProcesses == NULL ||
                  lpfEnumProcessModules == NULL ||
                  lpfGetModuleBaseName == NULL)
                  {
                     FreeLibrary(hInstLib);
                     return 605;
                  }
      
              bResult=lpfEnumProcesses(aiPID,iCb,&iCbneeded);
              if(!bResult)
              {
                  // Unable to get process list, EnumProcesses failed
                  FreeLibrary(hInstLib);
                  return 605;
              }
      
              // How many processes are there?
              iNumProc=iCbneeded/sizeof(DWORD);
      
              // Get and match the name of each process
              for(i=0;i<iNumProc;i++)
              {
                  // Get the (module) name for this process
      
                  strcpy(szName,"Unknown");
                  // First, get a handle to the process
                  hProc=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,FALSE,
                      aiPID[i]);
                  // Now, get the process name
                  if(hProc)
                  {
                     if(lpfEnumProcessModules(hProc,&hMod,sizeof(hMod),&iCbneeded) )
                     {
                        iLen=lpfGetModuleBaseName(hProc,hMod,szName,MAX_PATH);
                     }
                  }
                  CloseHandle(hProc);
                  // Match regardless of lower or upper case
                  if(strcmp(_strupr(szName),szToFindUpper)==0)
                  {
                      // Process found
                      FreeLibrary(hInstLib);
                      return 1;
                  }
              }
          }
      
          if(osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS)
          {
              // Win/95 or 98 or ME
      
              hInstLib = LoadLibraryA("Kernel32.DLL");
              if( hInstLib == NULL )
                  return FALSE ;
      
              // Get procedure addresses.
              // We are linking to these functions of Kernel32
              // explicitly, because otherwise a module using
              // this code would fail to load under Windows NT,
              // which does not have the Toolhelp32
              // functions in the Kernel 32.
              lpfCreateToolhelp32Snapshot=
                  (HANDLE(WINAPI *)(DWORD,DWORD))
                  GetProcAddress( hInstLib,
                  "CreateToolhelp32Snapshot" ) ;
              lpfProcess32First=
                  (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
                  GetProcAddress( hInstLib, "Process32First" ) ;
              lpfProcess32Next=
                  (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
                  GetProcAddress( hInstLib, "Process32Next" ) ;
              lpfModule32First=
                  (BOOL(WINAPI *)(HANDLE,LPMODULEENTRY32))
                  GetProcAddress( hInstLib, "Module32First" ) ;
              lpfModule32Next=
                  (BOOL(WINAPI *)(HANDLE,LPMODULEENTRY32))
                  GetProcAddress( hInstLib, "Module32Next" ) ;
              if( lpfProcess32Next == NULL ||
                  lpfProcess32First == NULL ||
                  lpfModule32Next == NULL ||
                  lpfModule32First == NULL ||
                  lpfCreateToolhelp32Snapshot == NULL )
              {
                  FreeLibrary(hInstLib);
                  return 605;
              }
      
              // The Process32.. and Module32.. routines return names in all uppercase
      
              // Get a handle to a Toolhelp snapshot of all the systems processes.
      
              hSnapShot = lpfCreateToolhelp32Snapshot(
                  TH32CS_SNAPPROCESS, 0 ) ;
              if( hSnapShot == INVALID_HANDLE_VALUE )
              {
                  FreeLibrary(hInstLib);
                  return 605;
              }
      
              // Get the first process' information.
              procentry.dwSize = sizeof(PROCESSENTRY32);
              bResult=lpfProcess32First(hSnapShot,&procentry);
      
              // While there are processes, keep looping and checking.
              while(bResult)
              {
                  // Get a handle to a Toolhelp snapshot of this process.
                  hSnapShotm = lpfCreateToolhelp32Snapshot(
                      TH32CS_SNAPMODULE, procentry.th32ProcessID) ;
                  if( hSnapShotm == INVALID_HANDLE_VALUE )
                  {
                      CloseHandle(hSnapShot);
                      FreeLibrary(hInstLib);
                      return 605;
                  }
                  // Get the module list for this process
                  modentry.dwSize=sizeof(MODULEENTRY32);
                  bResultm=lpfModule32First(hSnapShotm,&modentry);
      
                  // While there are modules, keep looping and checking
                  while(bResultm)
                  {
                      if(strcmp(modentry.szModule,szToFindUpper)==0)
                      {
                          // Process found
                          CloseHandle(hSnapShotm);
                          CloseHandle(hSnapShot);
                          FreeLibrary(hInstLib);
                          return 1;
                      }
                      else
                      {  // Look for next modules for this process
                          modentry.dwSize=sizeof(MODULEENTRY32);
                          bResultm=lpfModule32Next(hSnapShotm,&modentry);
                      }
                  }
      
                  //Keep looking
                  CloseHandle(hSnapShotm);
                  procentry.dwSize = sizeof(PROCESSENTRY32);
                  bResult = lpfProcess32Next(hSnapShot,&procentry);
              }
              CloseHandle(hSnapShot);
          }
          FreeLibrary(hInstLib);
          return 0;
      
      }
      

      【讨论】:

        【解决方案8】:

        您可以通过简单地通过CreateToolhelp32Snapshot 拍摄正在运行的进程的快照并在该快照上使用 Process32First 和 Process32Next 调用来遍历正在运行的进程,从而发现进程(根据其名称或 PID)是否正在运行。

        然后,您可以使用生成的 PROCESSENTRY32 结构的 th32ProcessID 字段或 szExeFile 字段,具体取决于您是要按 PID 还是可执行文件名称进行搜索。一个简单的实现可以在here找到。

        【讨论】:

        • 仅链接答案被认为没有那么有用,如果您可以扩展答案将是最好的。也许对我们可以在帖子中找到的内容进行简要说明。
        【解决方案9】:
        #include <cstdio>
        #include <windows.h>
        #include <tlhelp32.h>
        
        /*!
        \brief Check if a process is running
        \param [in] processName Name of process to check if is running
        \returns \c True if the process is running, or \c False if the process is not running
        */
        bool IsProcessRunning(const wchar_t *processName)
        {
            bool exists = false;
            PROCESSENTRY32 entry;
            entry.dwSize = sizeof(PROCESSENTRY32);
        
            HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
        
            if (Process32First(snapshot, &entry))
                while (Process32Next(snapshot, &entry))
                    if (!wcsicmp(entry.szExeFile, processName))
                        exists = true;
        
            CloseHandle(snapshot);
            return exists;
        }
        

        【讨论】:

        • 您可能会考虑在存在设置为 true 时添加中断以加快速度
        • 在这里使用 do...while() 循环会更好,因为 Process32First 找到的条目可能是正确的。
        • 正如@MarcoVeglio 所指出的,此解决方案跳过了第一个过程。但是作为Michael commented,第一个进程很可能是“SYSTEM”
        • 这里是一个solution,它考虑到了 Wonko 和 Marco 的言论
        【解决方案10】:

        在编写监控工具时,我采用了稍微不同的方法。

        仅仅为了使用 WaitForSingleObject 甚至是 RegisterWaitForSingleObject (它为你做这件事)而启动一个额外的线程感觉有点浪费。因为在我的情况下,我不需要知道进程关闭的确切时间,只是它确实已经关闭。

        我正在使用 GetProcessTimes() 代替:

        https://msdn.microsoft.com/en-us/library/windows/desktop/ms683223(v=vs.85).aspx

        GetProcessTimes() 仅当进程实际退出时才会返回进程的 ExitTime 的 FILETIME 结构。因此,只需检查 ExitTime 结构是否已填充以及时间是否不为 0;

        这个解决方案应该考虑一个进程已被杀死但它的 PID 被另一个进程重用的情况。 GetProcessTimes 需要进程的句柄,而不是 PID。所以操作系统应该知道句柄是一个在某个时刻正在运行但不再运行的进程,并给你退出时间。

        依靠 ExitCode 感觉很脏:/

        【讨论】:

        • 根据MSDN,“如果进程没有退出,这个结构的内容是未定义的”,其中“这个结构”是指FILETIME
        【解决方案11】:

        这是我过去使用过的解决方案。虽然这里的例子是在 VB.net 中——我已经在 c 和 c++ 中使用了这种技术。它绕过了进程 ID 和进程句柄的所有问题,并返回代码。无论 Process2 如何终止,Windows 都非常忠实地释放互斥锁。我希望它对某人有帮助...

        **PROCESS1 :-**
        
            Randomize()
            mutexname = "myprocess" & Mid(Format(CDbl(Long.MaxValue) * Rnd(), "00000000000000000000"), 1, 16)
            hnd = CreateMutex(0, False, mutexname)
        
            ' pass this name to Process2
            File.WriteAllText("mutexname.txt", mutexname)
        
            <start Process2>
            <wait for Process2 to start>
        
            pr = WaitForSingleObject(hnd, 0)
            ReleaseMutex(hnd)
        
            If pr = WAIT_OBJECT_0 Then
        
                 <Process2 not running>
        
            Else
        
                 <Process2 is running>
        
            End If
            ...
        
            CloseHandle(hnd)
            EXIT
        
            **PROCESS2 :-**
        
            mutexname = File.ReadAllText("mutexname.txt")
            hnd = OpenMutex(MUTEX_ALL_ACCESS Or SYNCHRONIZE, True, mutexname)
            ...
        
            ReleaseMutex(hnd)
            CloseHandle(hnd)
            EXIT
        

        【讨论】:

          【解决方案12】:

          solution provided by @user152949,正如评论中所指出的,跳过第一个过程,并且在“存在”设置为 true 时不会中断。让我提供一个固定版本:

          #include <windows.h>
          #include <tlhelp32.h>
          #include <tchar.h>
          
          bool IsProcessRunning(const TCHAR* const executableName) {
              PROCESSENTRY32 entry;
              entry.dwSize = sizeof(PROCESSENTRY32);
          
              const auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
          
              if (!Process32First(snapshot, &entry)) {
                  CloseHandle(snapshot);
                  return false;
              }
          
              do {
                  if (!_tcsicmp(entry.szExeFile, executableName)) {
                      CloseHandle(snapshot);
                      return true;
                  }
              } while (Process32Next(snapshot, &entry));
          
              CloseHandle(snapshot);
              return false;
          }
          

          【讨论】:

          • 两个小补充:1) 此代码假定您的项目是 Unicode。如果不是,请将 arg 更改为 const char * const processName。 2) arg 的名称令人困惑。它不是进程名称,而是.exe 文件的名称。
          • @Woody20,感谢您的评论。你说的对。我已经用固定版本更新了我的答案。现在它适用于 Unicode 和非 Unicode 构建配置
          • 我添加了一个 PID 检查以使其成为单实例应用程序的“IsProcessAlreadyRunning”检查:)
          猜你喜欢
          • 1970-01-01
          • 2023-03-14
          • 2011-07-09
          • 2015-06-10
          • 1970-01-01
          • 2015-05-28
          • 1970-01-01
          • 1970-01-01
          • 2012-02-25
          相关资源
          最近更新 更多