2009-10-19 17 views
45

Esto se refiere a los procesos de Windows XP.C++, ¿Cómo determinar si se está ejecutando un proceso de Windows?

Tengo un proceso en ejecución, llamémoslo Process1. Process1 crea un nuevo proceso, Process2, y guarda su id.

Ahora, en algún momento Process1 quiere que Process2 haga algo, por lo que primero debe asegurarse de que Process2 esté todavía vivo y de que el usuario no lo haya eliminado.

¿Cómo puedo verificar que este proceso todavía se esté ejecutando? Desde que lo creé, tengo la ID de proceso, creo que hay alguna función de biblioteca en la línea de IsProcessIDValid (id) pero no puedo encontrarla en MSDN

+0

DCOM ya hace todas estas cosas de forma gratuita, ¿por qué estás reinventando la rueda? –

+1

Este plan parece haberse roto debido a una condición de carrera inherente. El usuario podría matar a Process2 después de que haya comprobado para ver si Process2 todavía está vivo y antes de que le pida que haga el trabajo que necesita (o antes de que complete el trabajo que necesita). Es mejor que emita el comando para que Process2 haga el trabajo y luego espere la confirmación de que se completó.Mientras espera, puede observar si Process2 se va. –

Respuesta

3

Nunca se puede verificar y ver si un proceso se está ejecutando, solo puede verificar si un proceso fue ejecutándose en algún punto en el pasado reciente. Un proceso es una entidad que no está controlada por su aplicación y puede salir en cualquier momento. No hay forma de garantizar que un proceso no saldrá entre el cheque para ver si se está ejecutando y la acción correspondiente.

El mejor enfoque es hacer la acción requerida y detectar la excepción que se lanzaría si el proceso no se ejecutara.

+0

@Michael, la intención aquí es clara. El OP dijo específicamente que querían verificar si Process2 estaba vivo para poder hacer algo con él. – JaredPar

+1

Otro problema es que los PID se pueden reutilizar rápidamente. Entonces, si el Proceso 2 muere y el PID se recicla, puede pensar que su proceso aún se está ejecutando cuando no lo está. –

34

El asa del proceso se señalará si se cierra.

Así que el siguiente trabajo (manejo de errores removido por razones de brevedad):

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

Tenga en cuenta que los ID de proceso pueden ser reciclados - es mejor para almacenar en caché el mango que se devuelve desde la llamada CreateProcess.

También puede utilizar la API de subprocesos (SetThreadpoolWait en Vista +, RegisterWaitForSingleObject en plataformas mayores) para recibir una devolución de llamada cuando el proceso finalice.

EDITAR: Me perdí la parte "quiero hacer algo con el proceso" de la pregunta original. Puede utilizar esta técnica si está bien tener datos potencialmente obsoletos para una ventana pequeña o si desea suspender una operación sin siquiera intentarlo. Aún tendrá que manejar el caso donde la acción falla porque el proceso ha finalizado.

+3

Esto debería titularse WasProcessRunning, es un nombre inapropiado llamarlo IsProcessRunning – JaredPar

+0

Código muy descuidado. ¿Y dónde compruebas si el HANDLE en la variable 'process' no es NULL? – Elmue

58

Puede usar GetExitCodeProcess. Se volverá STILL_ACTIVE() si el proceso todavía está en marcha (o si ocurrió para salir con ese código de salida :().

+1

+1 para usar 'GetExitCodeProcess', dado que el proceso OP1 puede guardar el identificador de Process2 después de llamar a' CreateProcess' (un punto omitido por todos los demás que respondieron aquí) y ese identificador puede ingresarse en 'GetExitCodeProcess' – vladr

+0

wow esto funciona mucho mejor que usar OpenProcess (PROCESS_QUERY_INFORMATION gracias! – rogerdpack

+0

La mejor respuesta – dns

1

llamada EnumProcesses() y comprobar si el PID está en la lista.

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

+5

Si el proceso 2 está muerto, el PID puede reciclarse. –

+1

http://stackoverflow.com/questions/2384022/winsdk-determining-whether-an-arbitrary -pid-identifies-a-running-process-on-win tiene una respuesta w Con un ejemplo de bucle, si es útil – rogerdpack

1

JaredPar tiene razón en que no se puede saber si el proceso está en marcha. Sólo se puede saber si el proceso se estaba ejecutando en el momento que marcó. podría haber muerto en la media hora.

usted también deben ser conscientes de que los PID pueden ser reciclados muy rápido. Entonces, solo porque hay un proceso con su PID, eso no significa que sea su proceso.

Haga que los procesos compartan un GUID. (El Proceso 1 podría generar el GUID y pasarlo al Proceso 2 en la línea de comando). El Proceso 2 debería crear un mutex con nombre con ese GUID.Cuando el Proceso 1 desea verificar, puede hacer un WaitForSingleObject en el mutex con un tiempo de espera de 0. Si el proceso 2 se ha ido, el código de retorno le dirá que el mutex fue abandonado, de lo contrario obtendrá un tiempo de espera.

6

Otra forma de controlar un proceso hijo es crear un subproceso de trabajo que:

  1. CreateProcess llamada()
  2. llamada WaitForSingleObject() // el subproceso de trabajo ahora va a esperar hasta que el niño- el proceso termina la ejecución. también es posible tomar el código de retorno (desde la función main()).
6

Encontré esto hoy, es de 2003. Encuentra un proceso por nombre, ni siquiera necesita el 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; 

} 
0

Usted puede encontrar si un proceso (dado su nombre o PID) está funcionando o no por iteración sobre los procesos en ejecución simplemente tomando una instantánea de procesos que se ejecutan a través de CreateToolhelp32Snapshot, y mediante el uso Process32First y Process32Next llamadas en esa instantánea.

Luego puede usar el campo th32ProcessID o el campo szExeFile de la estructura PROCESSENTRY32 resultante, dependiendo de si desea buscar por PID o el nombre del ejecutable. Se puede encontrar una implementación simple here.

+0

Solo las respuestas de enlace se consideran no tan útiles, sería mejor si pudiera expandir su respuesta. Tal vez con una breve explicación de lo que pudimos encontrar en la publicación. – madth3

16
#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; 
} 
+1

puede considerar agregar un salto cuando existe se establece en verdadero para acelerar un poco –

+1

Un ciclo de hacer ... mientras() sería mejor aquí, ya que la entrada encontrada por Process32First podría ser la correcta. –

1

Mientras escribía una herramienta de supervisión, tomé un enfoque ligeramente diferente.

Me sentí un poco derrochador al girar un hilo adicional solo para usar WaitForSingleObject o incluso el RegisterWaitForSingleObject (que hace eso por usted). Como en mi caso no necesito saber el instante exacto en que se cerró el proceso, solo que efectivamente se ha cerrado.

estoy usando los GetProcessTimes() en su lugar:

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

GetProcessTimes() devolverá una estructura FILETIME para exitTime del proceso sólo si el proceso realmente ha salido. Entonces, solo es cuestión de verificar si la estructura ExitTime está poblada y si la hora no es 0;

Esta solución DEBERÍA dar cuenta del caso donde un proceso ha sido cancelado pero su PID fue reutilizado por otro proceso. GetProcessTimes necesita un control del proceso, no del PID. Entonces, el sistema operativo debe saber que el identificador es para un proceso que se estaba ejecutando en algún momento, pero no más, y darle el tiempo de salida.

Basándose en la ExitCode sentía sucia:/

+0

Según MSDN, "Si el proceso no ha finalizado, el contenido de esta estructura no está definido", donde "esta estructura" hace referencia al 'FILETIME'. –

0

Esta es una solución que he utilizado en el pasado. Aunque el ejemplo aquí está en VB.net, he usado esta técnica con c y C++. Pasa por alto todos los problemas con los ID de proceso & Controles de proceso y códigos de retorno. Windows es muy fiel al liberar el mutex sin importar cómo termine Process2. Espero que sea útil para alguien ...

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