简体   繁体   中英

Win32 C++ OpenProceess should return null if user has exited application but doesnt?

I have a program (runs as a background process) in which I installed a hook to capture EVENT_SYSTEM_FOREGROUND events (ie - when the user switches between windows). The callback which is registered for the hook basically logs what application (process exe filename) the user has switched from and which they have switched to .

I want to add some code to check if the application they have switched from is still active (if not we assume they have closed it and that is what has brought a new window into the foreground). I am testing for it's existence by trying to create a handle to the previous PID using OpenProcess

    //Check prev pid still exists - if not, assume the previous app has been closed
    HANDLE hPrevProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,false,g_prevPid);
    if (hPrevProc==NULL){
        prevProcStillRunning=false;
    }
    else{
        CloseHandle(hPrevProc);
    }

Assumptions with the code above:
g_prevPid is populated with a PID - I have verified this
prevProcStillRunning has been initialized to true

The problem with the code above is that for some reason, even when the user has exited an app (say notepad.exe for example). For up to 10 seconds after they have exited, this test still passes (ie - hPrevProc gets initialised). Even though I can see in the task manager that the Notepad.exe process has dissapeared (and yes I only have one instance of it opened), somehow, the OpenProcess line still can get a handle on that PID. I am guessing that somehow the PID actually still exists but it may be in a state where its terminating. I have found that if this code is called a few more times, eventually it will return null.

I would like to find out a better way I can test whether hPrevProc is still acitive. I tried to test this using the GetExitCodeProcess function but this seems to just give me the PID and I'm not even sure if that's the right approach in any case.

Any help appreciated.

Probably some process (maybe yours?) still holds a valid handle to this process. Until CloseHandle was called on all handles, system maintains internal record which allow to access its process data. This is important because as you say it must be possible to call GetExitCodeProcess on closed process, also someone might want to wait for it to stop with WaitForSingleObject.

Also be carefull with PIDs, they can be reused - so in theory you might call OpenProcess on some other newly opened process.

As for checking if given process is not a zombie, you might try enumerating top level windows with EnumWindows, and checking if any of them is associated with given PID (to get window's PID use GetWindowThreadProcessID).

The process subsists in the system after it terminates at least while there is an open handle to it.

The only foolproof method to know whether a process is still active is:

  • make sure the process cannot exit with code STILL_ACTIVE (259)
  • try to open the process ( OpenProcess )-> if you cannot is is terminated
  • read the exit process code ( GetExitCodeProcess ) -> if it is not STILL_ACTIVE the process is terminated.

You code could become:

//Check prev pid still exists - if not, assume the previous app has been closed
HANDLE hPrevProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,false,g_prevPid);
if (hPrevProc==NULL){
    prevProcStillRunning=false;
}
else{
    DWORD cr;
    if ((GetExitCodeProcess(hPrevProc, &cr) == 0) || (cr != STILL_ACTIVE)) {
        prevProcStillRunning=false;
    }
    CloseHandle(hPrevProc);
}

Anyway, closing a GUI application involves different steps:

  • the GUI elements are destroyed
  • the message loop ends
  • eventually the application could do background operations (save state to file, etc.)
  • the main procedure returns an exit code
  • the system knows that the application is terminated

The event will be sent as soon as the main window will be closed, which can happen some time before the application actually stops. A good example for that is Firefox. If you close the window and immediately try to start a new process, you will get an error because even if the UI is gone, the process is not still terminated. What is worse, is that you can find applications that simply go into background when you close the UI, and allow user to open UI again through an action on an icon in the status area of the taskbar ( Shell_NotifyIcon and its callback). This is common for services of other application working in background (network servers, firewalls, etc.). In that case, the UI is gone but the process will not terminate.

TL/DR: the time between the disparition of the UI and the termination of the process owning it is variable and depends on the system load and the background activity of the process after closing the UI. You can try to use a delay for that but I cannon guarantee anything about it...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM