Adriaan
Adriaan

Reputation: 806

Why does OpenProcess() return a non 0 value when the process ID is no longer running

I'm starting a new instance of another application using CreateProcess from Example and I end up saving the PID so that I can later check if that process is still running.

I'm using to following method to check if it's running or not:

procedure TfrmRSM.Button1Click(Sender: TObject);
begin
  var
  ahandle := OpenProcess(PROCESS_ALL_ACCESS, true, aPID);

  if ahandle = 0 then
    ShowMessage('is not running')
  else
    ShowMessage('is running');

  CloseHandle(ahandle);
end;

The code above should return 0 when the process is no longer running but it still returns a number greater than 0

I am closing the handle after using CreateProcess

Whats to propper way to check if a PID is running if the method I'm using is incorrect? I'm only able to find methods that use the application name.

Upvotes: 1

Views: 1430

Answers (3)

Remy Lebeau
Remy Lebeau

Reputation: 596407

I'm starting a new instance of another application using CreateProcess from Example and I end up saving the PID so that I can later check if that process is still running.

The correct way to handle this is to keep open the HANDLE that CreateProcess() gives you, and then you can query it via WaitForSingleObject() or GetExitCodeProcess() to see if the process has terminated or not, and then close the HANDLE when you no longer need it.

In comments, you mention that your launching app may terminate and be restarted separate from the target process. In that case, you could close the HANDLE if you still have it open, saving its PID and creation date/time somewhere you can get it back from, and then when your app restarts it can enumerate running processes (alternatively) to see if the target EXE is still running and has a matching PID and date/time, and if so then open a new HANDLE to that PID. Just be careful, because this does introduce a small race condition where the target process might terminate after you detect its presence and its PID could get recycled before you have a chance to open it. So you might need to re-validate the HANDLE's info again after opening it.

Otherwise, during your app's shutdown (or even before), you can off-load the open HANDLE from CreateProcess() to a separate helper process that stays running in the background monitoring the HANDLE, and then your main app can get the HANDLE back from that helper after restarting. Or, perform the actual CreateProcess() call in the helper to begin with, so the HANDLE monitoring stays within a single process at all times, and let your main app query the helper for status when needed.

I'm using to following method to check if it's running or not:

That will not work, as you don't know whether the PID is still valid, or even still refers to the same process you are interested in. Once that process has terminated, its PID can be recycled at any time for use with a new process.

The code above should return 0 when the process is no longer running but it still returns a number greater than 0

The only way OpenProcess() can return non-zero is if the specified PID is actually running. But that does not guarantee it is the same process you are interested in. At the very least, after OpenProcess() returns a non-zero HANDLE, you can query that HANDLE for its info (EXE file path, creation date/time, etc) to see if it is the same process you are expecting. If the info does not match, the PID was recycled.

Upvotes: 5

AmigoJack
AmigoJack

Reputation: 6099

To wait for a process to end, simply use WaitForSingleObject() on the process handle: when the process ends, the handle is signaled and the function returns WAIT_OBJECT_0. This ensures that you "look" at the handle the whole time it lives, instead of only inspecting it from time to time and otherwise leave it unattended.

Most likely you put this into a separate thread, and upon its ending you have your event to react to when the watched process ended. If you have multiple handles to look for, use WaitForMultipleObjects() - be aware that it can't handle more than 64 (MAXIMUM_WAIT_OBJECTS) handles at once.


Edit: when not being able to track a process handle entirely you can at least use GetProcessTimes() to check if the process you're looking at still started at the same time you looked at it last time - that should make it pretty distinctive: when the process handle is recycled by a new process then at least its starting time should differ.

Upvotes: 1

fpiette
fpiette

Reputation: 12292

Why does OpenProcess() return a non 0 value when the process ID is no longer running?

This is probably an undefined behavior or the PID has been recycled by Windows.

I want to check if a process I created is still running later even after I quit my application and opened it again.

You said you cannot change the process. One work around is to use an intermediate and very simple process to launch the target process. The intermediate process can use any IPC (for example shared memory of shared file) to talk to the main process to inform it about running target process. This intermediate process will run the target process and update a flag that the main process can query (In my example in shared memory).

The intermediate process can also simply keep a file open for exclusive access while the target process is running. The main process can try to open the file and if it succeed, then the target process is done (Intermediate process use WaitForSingleObjector WaitForMultipleObject to wait for target process termination).

Upvotes: 1

Related Questions