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

这涉及到 WindowsXP 进程。

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

现在,在某个时刻,Process1希望 Process2执行某些操作,因此它首先需要确保 Process2仍然活着,并且用户没有杀死它。

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

148930 次浏览

您永远不能检查和查看进程是否正在运行,您只能检查进程 曾经是是否在最近的某个时刻正在运行。流程是一个不受应用程序控制的实体,可以在任何时候退出。无法保证进程不会在检查之间退出,以查看它是否正在运行以及相应的操作。

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

如果流程句柄退出,将发出信号。

因此,下面的代码可以正常工作(为简洁起见,删除了错误处理) :

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)在进程退出时接收回调。

编辑: 我错过了原始问题中“想对过程做些什么”的部分。如果某个小窗口的数据可能过期,或者您希望操作失败,甚至不尝试操作,则可以使用此技术。您仍然必须处理操作失败的情况,因为进程已经退出。

你可以使用 GetExitCodeProcess。如果进程仍在运行(或者正好退出时带有退出代码: ()) ,它将返回 STILL_ACTIVE(259)。

调用 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消失了,返回代码将告诉您互斥对象已被放弃,否则您将得到超时。

监视子进程的另一种方法是创建一个工作线程,它将:

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

我今天发现了这个,是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;


}

您可以通过简单地通过 创建工具帮助32Snapshot获取正在运行的进程的快照,并通过对该快照使用 Process32First 和 Process32Next 调用来遍历正在运行的进程来查找进程(给定其名称或 PID)是否正在运行。

然后,您可以使用结果 PROCESSENTRY32结构的 th32ProcessID 字段或 szExeFile 字段,具体取决于您想要按 PID 或可执行文件名进行搜索。可以找到一个简单的实现 给你

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

在编写监视工具时,我采用了略有不同的方法。

仅仅为了使用 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 让人觉得肮脏:/

这是我以前用过的解决方案。虽然这里的例子是在 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

正如在评论中所指出的那样,@ 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;
}
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);
}