David
David

Reputation: 16038

JNA GetExitCodeProcess() working strangely

I have a very strange problem with JNA. I am checking if a process exists by using GetExitCodeProcess().

So for example, I know notepad is PID 2084. When I use my method to check if PID 2084 exists, it returns true for PIDs between 2084 and 2087 (even though I am completely sure that PIDs 2085-2087 don't exist). It returns false for other PIDs, like 2083 and 2088.

It's almost as if there is some kind of impossible rounding error, and OpenProcess() is opening a handle on a PID that doesn't exist!

This is happening with all processes. If I enumerate all the processes and call isRunning(PID), it returns true when PID + 1,2 or 3 exist. It returns false otherwise, so at least it's working partially.

The pattern is always the same, it returns true between PID and PID + 3.

Example output:

[Notepad PID = 2084, cmd.exe PID = 2100]

isRunning(2083)=False
isRunning(2084)=true
isRunning(2085)=true
isRunning(2086)=true
isRunning(2087)=true
isRunning(2088)=false
.... false .....
isRunning(2100)=true

etc..

Interface code:

protected interface Kernel32 extends StdCallLibrary {
        Kernel32 INSTANCE = (Kernel32)Native.loadLibrary("kernel32", Kernel32.class);
        public Pointer OpenProcess(int dwDesiredAccess, boolean bInheritHandle, int dwProcessId);
        int GetLastError();
        boolean GetExitCodeProcess(Pointer hProcess, IntByReference lpExitCode);
    };

Function code:

public static boolean isRunning(int pid)
{
    final int PROCESS_QUERY_INFORMATION = 0x0400;
    final int STILL_ALIVE = 259;
    final int INVALID_PARAM = 87;

    Pointer hProcess = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, false, pid);
    int error = kernel32.GetLastError();

    if (error == INVALID_PARAM)
        return false; //Invalid parameter.

    IntByReference exitCode = new IntByReference();
    kernel32.GetExitCodeProcess(hProcess, exitCode);


    if (exitCode.getValue() != STILL_ALIVE)
        return false;
    else 
        return true;


}



public static void main(String[] args) {
    System.out.println(isRunning(2083)); //Proceses with PID 2083, 2085 to 2088 do not exist.
    System.out.println(isRunning(2084)); //2084 is notepad
    System.out.println(isRunning(2085));
    System.out.println(isRunning(2086));
    System.out.println(isRunning(2087));
    System.out.println(isRunning(2088));
}

Upvotes: 0

Views: 882

Answers (2)

BrendanMcK
BrendanMcK

Reputation: 14498

Answering the question you didn't actually ask, but is the elephant in the room here:

I am checking if a process exists by using GetExitCodeProcess().

Bad strategy - process IDs get reused/recycled, so it's entirely possible that once notepad with PID 2084 dies, the PID will be recycled into some other random process, and GetExitCodeProcess could give you the false impression that notepad was still alive (when it's actually some other process that just happens to have the same PID that's now alive). So everything might appear to work fine when you test your code on your machine - but then occasionally fail randomly and mysteriously in the real world.

You might be able to make it work [better] if you save more than just the PID - eg save also the exe name (GetModuleFileNameEx), but even then you'll run into trouble if a new instance of the same app gets created. Save the main HWND too for good measure; HWNDs are recycled also, but at a much much much slower rate than PIDs.

Upvotes: 0

David Heffernan
David Heffernan

Reputation: 612983

That's a Windows implementation detail. The 2 least significant bits of the PID are ignored. So in your example, 2084-2087 all refer to the same process.

Raymond Chen wrote about this already: Why does OpenProcess succeed even when I add three to the process ID?

You would do well to heed the following caveat:

Again, I wish to emphasize that the behavior you see in Windows NT-based kernels is just an implementation artifact which can change at any time.

Upvotes: 2

Related Questions