web-dev-qa-db-fra.com

C++, Comment déterminer si un processus Windows est en cours d'exécution?

Cela concerne les processus Windows XP.

J'ai un processus en cours, appelons-le Process1. Process1 crée un nouveau processus, Process2, et enregistre son identifiant.

Désormais, Process1 veut que Process2 fasse quelque chose. Il doit donc d'abord s'assurer que Process2 est toujours en vie et que l'utilisateur ne l'a pas tué.

Comment puis-je vérifier que ce processus est toujours en cours d'exécution? .__ Depuis que je l'ai créé, j'ai l'ID de processus, je penserais qu'il existe une fonction de bibliothèque ressemblant à IsProcessIDValid (id), mais je ne le trouve pas sur MSDN.

52
Pedro

Vous pouvez utiliser GetExitCodeProcess . Il renverra STILL_ACTIVE (259) si le processus est toujours en cours d'exécution (ou s'il est arrivé de quitter avec ce code de sortie :().

63
Dolphin

Le descripteur de processus sera signalé s'il se ferme.

Ainsi, les éléments suivants fonctionneront (la gestion des erreurs a été supprimée par souci de concision):

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

Notez que les identifiants de processus peuvent être recyclés - il est préférable de mettre en cache le descripteur renvoyé par l'appel CreateProcess.

Vous pouvez également utiliser les API de threadpool (SetThreadpoolWait sur Vista +, RegisterWaitForSingleObject sur des plates-formes plus anciennes) pour recevoir un rappel lorsque le processus se termine.

EDIT: J'ai manqué la partie «vouloir faire quelque chose au processus» de la question initiale. Vous pouvez utiliser cette technique s'il est correct d'avoir des données potentiellement périmées pour une petite fenêtre ou si vous souhaitez échouer une opération sans même tenter de la tenter. Vous devrez toujours gérer le cas où l'action échouera parce que le processus s'est terminé.

34
Michael
#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;
}
17
user152949

Un autre moyen de surveiller un processus enfant consiste à créer un thread de travail qui:

  1. appelez CreateProcess ()
  2. call WaitForSingleObject () // le thread de travail va maintenant attendre que le processus fils termine son exécution. il est également possible de récupérer le code de retour (depuis la fonction main ()).
6
Maciek

J'ai trouvé cela aujourd'hui, il date de 2003. Il trouve un processus par son nom, vous n'avez même pas besoin du 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 [email protected]

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

}
5
Motes

Vous ne pouvez jamais vérifier si un processus est en cours d'exécution, vous pouvez uniquement vérifier si un processus était s'est exécuté à un moment donné dans le passé récent. Un processus est une entité qui n'est pas contrôlée par votre application et qui peut être fermée à tout moment. Il n'y a aucun moyen de garantir qu'un processus ne se terminera pas entre le contrôle pour voir s'il est en cours d'exécution et l'action correspondante.

La meilleure approche consiste simplement à effectuer l'action requise et à détecter l'exception qui serait levée si le processus ne fonctionnait pas. 

3
JaredPar

Lors de la rédaction d'un outil de suivi, j'ai adopté une approche légèrement différente.

C’est un peu inutile de créer un fil supplémentaire pour utiliser WaitForSingleObject ou même le RegisterWaitForSingleObject (qui le fait pour vous). Dans mon cas, je n'ai pas besoin de savoir exactement à quel moment un processus est fermé, mais simplement qu'il a bien été fermé. 

J'utilise plutôt GetProcessTimes ():

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

GetProcessTimes () renverra une structure FILETIME pour ExitTime du processus uniquement si le processus s'est réellement terminé. Il suffit donc de vérifier si la structure ExitTime est renseignée et si le temps n’est pas 0;

Cette solution DEVRAIT prendre en compte le cas où un processus a été tué mais que son PID a été réutilisé par un autre processus. GetProcessTimes a besoin d'un handle pour le processus, pas du PID. Le système d’exploitation doit donc savoir que le descripteur concerne un processus en cours d’exécution, mais plus maintenant, et vous indique le temps de sortie.

S'en remettre à l'ExitCode était sale: /

1
Andrew

Le solution fournie par @ user152949 , comme indiqué dans les commentaires, ignore le premier processus et ne se rompt pas lorsque "existe" est défini sur true. Me laisser fournir une version fixe:

#include <windows.h>
#include <tlhelp32.h>

bool IsProcessRunning(const wchar_t* const processName) {
    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);

    const auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    if (!Process32First(snapshot, &entry)) {
        CloseHandle(snapshot);
        return false;
    }

    do {
        if (!_wcsicmp(entry.szExeFile, processName)) {
            CloseHandle(snapshot);
            return true;
        }
    } while (Process32Next(snapshot, &entry));

    CloseHandle(snapshot);
    return false;
}
1
PolarBear

appelez EnumProcesses() et vérifiez si le PID est dans la liste.

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

1
Lance Rushing

JaredPar a raison de dire que vous ne pouvez pas savoir si le processus est en cours d'exécution. Vous pouvez uniquement savoir si le processus était en cours au moment de la vérification. Il est peut-être mort entre-temps.

Vous devez également savoir que les PID peuvent être recyclés assez rapidement. Donc, le fait qu’un processus existe avec votre PID ne signifie pas que c’est votre processus.

Demandez aux processus de partager un GUID. (Le processus 1 pourrait générer le GUID et le transmettre au processus 2 sur la ligne de commande.) Le processus 2 devrait créer un mutex nommé avec ce GUID. Lorsque le processus 1 veut vérifier, il peut effectuer une WaitForSingleObject sur le mutex avec un délai d'attente égal à 0. Si le processus 2 est terminé, le code de retour vous indiquera que le mutex a été abandonné, sinon vous obtiendrez un délai d'expiration.

1
Adrian McCarthy

C'est une solution que j'ai utilisée par le passé. Bien que l'exemple ici soit dans VB.net - j'ai utilisé cette technique avec c et c ++. Il contourne tous les problèmes liés aux ID de processus, aux descripteurs de processus et aux codes de retour. Windows est très fidèle à la publication du mutex, peu importe la façon dont Process2 est arrêté. J'espère que c'est utile à quelqu'un ...

**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
0
42LeapsOfFaith

Vous pouvez savoir si un processus (en fonction de son nom ou de son PID) est en cours d'exécution en itérant les processus en cours en prenant simplement un instantané des processus en cours via CreateToolhelp32Snapshot , et en utilisant les appels Process32First et Process32Next pour cet instantané.

Ensuite, vous pouvez utiliser le champ th32ProcessID ou le champ szExeFile de la structure PROCESSENTRY32 obtenue, selon que vous souhaitez effectuer une recherche par PID ou par nom d’exécutable. Une implémentation simple peut être trouvée ici .

0
user2001885