0

(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.

iamoumuamua
  • 351
  • 2
  • 11
  • 1
    Why the system call? If you want to find a process there are platform APIs for that, E.g. https://stackoverflow.com/questions/865152/how-can-i-get-a-process-handle-by-its-name-in-c – Alex K. Mar 11 '19 at 15:23
  • 2
    Using `system` us almost always a poor man's solution. Using [this](https://learn.microsoft.com/en-us/windows/desktop/psapi/enumerating-all-processes) is certainly a better approach. – Jabberwocky Mar 11 '19 at 15:31
  • @iamoumuamua simple is not always clean, but what's wrong with the solution in my first comment? It's really simple and it's clean. – Jabberwocky Mar 11 '19 at 15:51
  • Oh apparently they use something called PrintProcessNameAndID() to get a name from a pid, but I cannot find any documentation on that function. At least knowing what header to include would be nice. In fact I just entered it into VS2017 and it is not recognized by intellisense. Oh wait that function calls EnumProcessModules() and GetModuleBaseName(). Looks interesting. Still seems complicated though compared to the system call. – iamoumuamua Mar 11 '19 at 16:01
  • @iamoumuamua `PrintProcessNameAndID` is part of the sample. Of course you need to make small adaptations, the sample just prints the list of processes. Of course you also need some knowledge of C. Without any knowledge of C it's of course difficult. The sample works as is, you can copy/paste/compile it (at least with Visual Studio). And believe me, `system` is the worst of all solutions. – Jabberwocky Mar 11 '19 at 16:04
  • EnumProcessModules() and GetModuleBaseName() both require process handles themselves. That seems a bit uh convoluted since a process handle is exactly what i want to get. That method will work but it is very messy. – iamoumuamua Mar 11 '19 at 16:15
  • @iamoumuamua what do you think the commands you launch with `system` do at the end of the day? Anyway there is no function that gives you the process id from a process name. So you need to do it another way – Jabberwocky Mar 11 '19 at 16:18

0 Answers0