简体   繁体   English

C++,如何确定 Windows 进程是否正在运行?

[英]C++, How to determine if a Windows Process is running?

This is concerning Windows XP processes.这与 Windows XP 进程有关。

I have a process running, let's call it Process1.我有一个正在运行的进程,我们称它为 Process1。 Process1 creates a new process, Process2, and saves its id. Process1 创建一个新进程 Process2,并保存它的 id。

Now, at some point Process1 wants Process2 to do something, so it first needs to make sure that Process2 is still alive and that the user has not not killed it.现在,在某些时候 Process1 想要 Process2 做某事,所以它首先需要确保 Process2 还活着并且用户没有杀死它。

How can I check that this process is still running?如何检查此进程是否仍在运行? Since I created it, I have the Process ID, I would think there is some library function along the lines of IsProcessIDValid( id ) but I can't find it on MSDN由于我创建了它,我有进程 ID,我认为有一些库 function 沿着 IsProcessIDValid(id) 的行,但我在 MSDN 上找不到它

You can use GetExitCodeProcess .您可以使用GetExitCodeProcess It will return STILL_ACTIVE ( 259 ) if the process is still running (or if it happened to exit with that exit code :( ).如果进程仍在运行(或者如果它碰巧以该退出代码退出:(),它将返回STILL_ACTIVE ( 259 )。

The process handle will be signaled if it exits.如果它退出,进程句柄将被发出信号。

So the following will work (error handling removed for brevity):因此,以下将起作用(为简洁起见,删除了错误处理):

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

Note that process ID's can be recycled - it's better to cache the handle that is returned from the CreateProcess call.请注意,进程 ID 可以回收 - 最好缓存从 CreateProcess 调用返回的句柄。

You can also use the threadpool API's (SetThreadpoolWait on Vista+, RegisterWaitForSingleObject on older platforms) to receive a callback when the process exits.您还可以使用线程池 API(Vista+ 上的 SetThreadpoolWait,旧平台上的 RegisterWaitForSingleObject)在进程退出时接收回调。

EDIT: I missed the "want to do something to the process" part of the original question.编辑:我错过了原始问题的“想要对流程做些什么”部分。 You can use this technique if it is ok to have potentially stale data for some small window or if you want to fail an operation without even attempting it.如果可以为某个小窗口保留潜在的陈旧数据,或者如果您想在不尝试的情况下使操作失败,则可以使用此技术。 You will still have to handle the case where the action fails because the process has exited.您仍然需要处理由于进程已退出而导致操作失败的情况。

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

The solution provided by @user152949 , as it was noted in commentaries, skips the first process and doesn't break when "exists" is set to true.正如评论中所指出的, @user152949 提供解决方案会跳过第一个过程,并且在“exists”设置为 true 时不会中断。 Let me provide a fixed version:让我提供一个固定版本:

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

Another way of monitoring a child-process is to create a worker thread that will :另一种监视子进程的方法是创建一个工作线程,它将:

  1. call CreateProcess()调用 CreateProcess()
  2. call WaitForSingleObject() // the worker thread will now wait till the child-process finishes execution. call WaitForSingleObject() // 工作线程现在将等待子进程完成执行。 it's possible to grab the return code (from the main() function) too.也可以获取返回码(从 main() 函数)。

I found this today, it is from 2003. It finds a process by name, you don't even need the pid.我今天发现了这个,它是 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;

}

You can never check and see if a process is running, you can only check to see if a process was running at some point in the recent past.您永远无法检查并查看进程是否正在运行,您只能检查进程是否在最近的某个时间点正在运行。 A process is an entity that is not controlled by your application and can exit at any moment in time.进程是一个不受应用程序控制的实体,可以随时退出。 There is no way to guaranteed that a process will not exit in between the check to see if it's running and the corresponding action.没有办法保证进程在检查它是否正在运行和相应的操作之间不会退出。

The best approach is to just do the action required and catch the exception that would be thrown if the process was not running.最好的方法是只执行所需的操作并捕获进程未运行时将抛出的异常。

call EnumProcesses() and check if the PID is in the list.调用EnumProcesses()并检查 PID 是否在列表中。

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

JaredPar is right in that you can't know if the process is running. JaredPar 是对的,因为您无法知道进程是否正在运行。 You can only know if the process was running at the moment you checked.您只能知道在您检查时该进程是否正在运行。 It might have died in the mean time.在此期间它可能已经死亡。

You also have to be aware the PIDs can be recycled pretty quickly.您还必须意识到 PID 可以很快被回收。 So just because there's a process out there with your PID, it doesn't mean that it's your process.因此,仅仅因为有一个带有您的 PID 的进程,并不意味着它是您的进程。

Have the processes share a GUID.让进程共享一个 GUID。 (Process 1 could generate the GUID and pass it to Process 2 on the command line.) Process 2 should create a named mutex with that GUID. (进程 1 可以生成 GUID 并在命令行上将其传递给进程 2。)进程 2 应该使用该 GUID 创建一个命名的互斥锁。 When Process 1 wants to check, it can do a WaitForSingleObject on the mutex with a 0 timeout.当进程 1 要检查时,它可以对互斥锁执行WaitForSingleObject ,超时为 0。 If Process 2 is gone, the return code will tell you that the mutex was abandoned, otherwise you'll get a timeout.如果进程 2 消失了,返回码会告诉你互斥锁被放弃了,否则你会得到一个超时。

You may find if a process (given its name or PID) is running or not by iterating over the running processes simply by taking a snapshot of running processes via CreateToolhelp32Snapshot , and by using Process32First and Process32Next calls on that snapshot.您可以通过简单地通过CreateToolhelp32Snapshot 获取正在运行的进程的快照并在该快照上使用 Process32First 和 Process32Next 对正在运行的进程进行迭代来确定进程(给定其名称或 PID)是否正在运行。

Then you may use th32ProcessID field or szExeFile field of the resulting PROCESSENTRY32 struct depending on whether you want to search by PID or executable name.然后,您可以使用结果 PROCESSENTRY32 结构的 th32ProcessID 字段或 szExeFile 字段,具体取决于您是要按 PID 还是可执行名称进行搜索。 A simple implementation can be found here .一个简单的实现可以在这里找到。

While writing a monitoring tool, i took a slightly different approach.在编写监控工具时,我采用了稍微不同的方法。

It felt a bit wasteful to spin up an extra thread just to use WaitForSingleObject or even the RegisterWaitForSingleObject (which does that for you).仅仅为了使用WaitForSingleObject 甚至RegisterWaitForSingleObject(它为你做这件事)而启动一个额外的线程感觉有点浪费。 Since in my case i don't need to know the exact instant a process has closed, just that it indeed HAS closed.因为在我的情况下,我不需要知道进程关闭的确切时刻,只需知道它确实已经关闭。

I'm using the GetProcessTimes() instead:我正在使用 GetProcessTimes() 代替:

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

GetProcessTimes() will return a FILETIME struct for the process's ExitTime only if the process has actually exited.只有当进程实际退出时,GetProcessTimes() 才会为进程的 ExitTime 返回一个 FILETIME 结构。 So is just a matter of checking if the ExitTime struct is populated and if the time isn't 0;所以只是检查 ExitTime 结构是否已填充以及时间是否不为 0 的问题;

This solution SHOULD account the case where a process has been killed but it's PID was reused by another process.这个解决方案应该考虑一个进程被杀死但它的 PID 被另一个进程重用的情况。 GetProcessTimes needs a handle to the process, not the PID. GetProcessTimes 需要进程的句柄,而不是 PID。 So the OS should know that the handle is to a process that was running at some point, but not any more, and give you the exit time.所以操作系统应该知道句柄是在某个时刻运行的进程,但不再运行,并为您提供退出时间。

Relying on the ExitCode felt dirty :/依靠 ExitCode 感觉很脏:/

This is a solution that I've used in the past.这是我过去使用过的解决方案。 Although the example here is in VB.net - I've used this technique with c and c++.虽然这里的例子是在 VB.net 中 - 我已经在 c 和 c++ 中使用了这种技术。 It bypasses all the issues with Process IDs & Process handles, and return codes.它绕过了进程 ID 和进程句柄以及返回代码的所有问题。 Windows is very faithful in releasing the mutex no matter how Process2 is terminated.无论 Process2 如何终止,Windows 都非常忠实地释放互斥锁。 I hope it is helpful to someone...我希望它对某人有帮助...

**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
char tmp[200] = "taskkill /f /im chrome.exe && \"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe\"

while (1)
{
    FILE* f;
    f = _popen("tasklist", "r");

    char b[512];
    bzero(b, 512);

    while (fgets(b, 512, f) != NULL)
    {
        if (strncmp(b, "chrome.exe", 8) == 0)
        {
            printf("Chrome running!\n");
            system(tmp);
        }
        else
        {
            printf("Chrome NOT running!\n");
        }
    }
    Sleep(1000);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM