C++, как определить, запущен ли процесс Windows?

Это касается процессов Windows XP.

У меня запущен процесс, назовем его Process1. Process1 создает новый процесс, Process2, и сохраняет его идентификатор.

Теперь, в какой-то момент Process1 хочет, чтобы Process2 что-то сделал, поэтому он должен сначала убедиться, что Process2 все еще жив и что пользователь не уничтожил его.

Как я могу проверить, что этот процесс все еще выполняется? Так как я создал его, у меня есть идентификатор процесса, я думаю, что есть некоторая библиотечная функция по аналогии с IsProcessIDValid( id), но я не могу найти ее в MSDN

14 ответов

Ты можешь использовать GetExitCodeProcess, Он вернется STILL_ACTIVE (259), если процесс все еще выполняется (или если он завершился с этим кодом выхода:().

Дескриптор процесса будет сигнализирован, если он выйдет.

Таким образом, будет работать следующее (для краткости устранена обработка ошибок):

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

Обратите внимание, что идентификаторы процессов могут быть переработаны - лучше кэшировать дескриптор, который возвращается из вызова CreateProcess.

Вы также можете использовать API пула потоков (SetThreadpoolWait на Vista+, RegisterWaitForSingleObject на старых платформах) для получения обратного вызова при выходе из процесса.

РЕДАКТИРОВАТЬ: я пропустил часть "хочу сделать что-то с процессом" в первоначальном вопросе. Вы можете использовать эту технику, если нормально иметь потенциально устаревшие данные для небольшого окна или если вы хотите завершить операцию, даже не пытаясь ее выполнить. Вам все равно придется обрабатывать случай, когда действие завершается неудачей, потому что процесс завершился.

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

Решение, предоставляемое @user152949, как это было отмечено в комментариях, пропускает первый процесс и не ломается, когда "существует" установлено значение верно. Приведу фиксированную версию:

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

Я нашел это сегодня, это с 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;

}

Другой способ мониторинга дочернего процесса - создать рабочий поток, который будет:

  1. вызвать CreateProcess ()
  2. call WaitForSingleObject () // рабочий поток теперь будет ждать, пока дочерний процесс завершит выполнение. также можно получить код возврата (из функции main ()).

Вы никогда не сможете проверить, запущен ли процесс, вы можете только проверить, запущен ли процесс в какой-то момент в недавнем прошлом. Процесс - это объект, который не контролируется вашим приложением и может выйти в любой момент времени. Невозможно гарантировать, что процесс не завершится между проверкой, чтобы увидеть, запущен ли он, и соответствующим действием.

Лучший подход - просто выполнить требуемое действие и перехватить исключение, которое будет выдано, если процесс не запущен.

Вызов EnumProcesses() и проверьте, есть ли PID в списке.

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

JaredPar прав в том, что вы не можете знать, запущен ли процесс. Вы можете знать только, запущен ли процесс в тот момент, когда вы проверили. Возможно, он умер за это время.

Вы также должны знать, что PID могут быть переработаны довольно быстро. То, что у вас есть процесс с вашим PID, не означает, что это ваш процесс.

У процессов есть общий идентификатор GUID. (Процесс 1 может сгенерировать GUID и передать его процессу 2 в командной строке.) Процесс 2 должен создать именованный мьютекс с этим GUID. Когда процесс 1 хочет проверить, он может сделать WaitForSingleObject на мьютексе с таймаутом 0 Если процесс 2 завершен, код возврата сообщит вам, что мьютекс был отменен, в противном случае вы получите тайм-аут.

TL;DR Использование GetProcessVersion.

Все эти функции доступны в Windows XP [настольные приложения | Приложения UWP].

GetProcessVersion использует идентификатор процесса и возвращает 0, если процесс с данным идентификатором не запущен.

GetExitCodeProcess использует дескриптор процесса и предоставляет код выхода процесса, если код STILL_ACTIVE (259), процесс все еще выполняется, поэтому вы можете проверить, не является ли он STILL_ACTIVE (259), что означает, что процесс не запущен. Это, вероятно, сработает практически в любой ситуации, если только процесс не завершится с кодом 259.

WaitForSingleObject использует дескриптор процесса с правом доступа SYNCHRONIZE и возвращает 0, если процесс не запущен. Вы не должны указывать INFINITE для параметра dwMilliseconds, потому что функция не вернется, пока состояние процесса не станет сигнальным (процесс завершен).

При написании инструмента мониторинга я выбрал немного другой подход.

Было немного расточительно вращать дополнительный поток, просто чтобы использовать WaitForSingleObject или даже RegisterWaitForSingleObject (который делает это для вас). Поскольку в моем случае мне не нужно знать точный момент, когда процесс закрылся, просто он действительно закрылся.

Я использую GetProcessTimes() вместо этого:

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

GetProcessTimes() вернет структуру FILETIME для ExitTime процесса, только если процесс фактически завершился. Так что это просто вопрос проверки, если структура ExitTime заполнена и если время не 0;

Это решение ДОЛЖНО учитывать случай, когда процесс был убит, но его PID был повторно использован другим процессом. GetProcessTimes нужен дескриптор процесса, а не PID. Поэтому ОС должна знать, что дескриптор относится к процессу, который выполнялся в какой-то момент, но не больше, и дать вам время выхода.

Опираясь на ExitCode чувствовал себя грязным: /

Это решение, которое я использовал в прошлом. Хотя пример здесь на VB.net - я использовал эту технику с c и C++. Он обходит все проблемы с идентификаторами процессов, дескрипторами процессов и кодами возврата. Windows очень верна в освобождении мьютекса независимо от того, как завершается Process2. Надеюсь это кому-нибудь пригодится...

**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

Вы можете узнать, запущен ли процесс (с учетом его имени или PID), выполнив итерации по запущенным процессам, просто сделав снимок запущенных процессов с помощью CreateToolhelp32Snapshot и используя вызовы Process32First и Process32Next для этого снимка.

Затем вы можете использовать поле th32ProcessID или поле szExeFile результирующей структуры PROCESSENTRY32 в зависимости от того, хотите ли вы выполнить поиск по PID или имени исполняемого файла. Простую реализацию можно найти здесь.

      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);
}
Другие вопросы по тегам