(Edited for brevity, clarity, and utility)
My program (B) monitors another program (A) that is not my own. I would like to detect when that program exits or crashes. My access to the source code is limited and I would prefer a solution that does not require me to make changes to the source code of the watched program, but I can make those changes if it is a better solution. Methods from best to worst: (interested in corrections/improvements)
1) CreateToolhelp32Snapshot() + Process32Next() loop iterating through a PROCESSENTRY32 struct that has both pid (::th32ProcessID) and name (::szExeFile) elements. Foreach PROCESSENTRY32 struct returned by Process32Next() check the szExeFile element for a match with the process name.
When or if a match is found then maybe call OpenProcess() with .th32ProcessID and call WaitForSingleObject(hprocess,0) with the returned process handle, but if szExeFile matches your filename then the process is still running so the rest is perhaps unnecessary, but maybe WaitForSingleObject() would be faster for checking in a loop.
2) EnumProcesses() to get an array of PIDs. Then foreach PID element call OpenProcess() to get a process handle and use that handle to call GetProcessImageFilename() and then CloseHandle() to close the process. Once you have the right PID call OpenProcess() to get a process handle and then call WaitForSingleObject() with that. Requires loop and more steps than the CreateToolhelp32Snapshot() method.
3) GetCurrentProcessId() + IPC. Requires changing code in process A which may not be possible or desirable. This method seems a bit complicated and ugly but at least there is no loop. 3 lines in A and 4 lines in B or maybe only 2 if you don't count the OpenProcess call. Not that bad I guess. In fact I think I am going to try this method for now.
The problem is that interprocess communication is a pita. Maybe worse than having to iterate through a loop. If IPC were easier/simpler or not required this might be the best method. Variations on this might use GetCurrentProcess() and DuplicateHandle() to get a process handle and then store that handle as a shared memory object.
Process A:
HANDLE hpidA = CreateFileMappingA(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, 4, "pidobj");
int * ppidA = reinterpret_cast<int *>(MapViewOfFile(hpidA, FILE_MAP_WRITE, 0, 0, 4));
*ppidA = GetCurrentProcessId();
Process B:
HANDLE hpidB = OpenFileMappingA(FILE_MAP_READ, 0, "pidobj");
int * ppidB = reinterpret_cast<int *>(MapViewOfFile(hpidB, FILE_MAP_READ, 0, 0, 4));
HANDLE hproc = OpenProcess(SYNCHRONIZE, FALSE, *ppidB);
bool running = WaitForSingleObject(hproc, 0);
4) System("tasklist /fi "imagename eq vlc.exe" /fo csv /nh > proc.txt")
and then reading the file to get the PID and then OpenProcess() + WaitForSingleObject() would probably also work but it's a slow and ugly and inelegant solution that causes disk accesses.
Note: There is some discussion in similar questions about problems with PID recycling related to using OpenProcess() itself to check if a process is running, but it appears that the PID will not be recycled until/unless you have called CloseHandle().
This is what I wanted to do before:
System("for /f \"delims=, tokens=2\" %g in (\'tasklist /fi \"imagename eq vlc.exe\" /fo csv /nh\') do set proc=%g");
int nchars = GetEnvironmentVariableA("proc", procidstr, 16);
and then something like:
for (int i = nchars; i; i--) procidstr[i] = procidstr[i+1]; //strip ""
int procid = strtol(procidstr, 0, 0);
HANDLE hproc = OpenProcess(SYNCHRONIZE, FALSE, procid);
int running = WaitForSingleObject(hproc, 0);
This did in fact set the environment variable 'proc' to the PID but GetEnvironmentVariable() only returned '\0'. I tried with setx as well with the same result. It set the variable in the console but it could not be retrieved by GetEnvironmentVariable() due to scope issues I assume. This makes me wonder if there is any way at all to set an environment variable and actually use it in a c-c++ program. I think I am going to ask a separate question about that.