java-native-access / jna

Java Native Access
Other
8.48k stars 1.67k forks source link

Processes run as admin don't show up in WindowUtils.getAllWindows(boolean) #1269

Open rococode opened 3 years ago

rococode commented 3 years ago

When an executable is run with "Run this program as an administrator" toggled on in Compatibility settings in Windows 10, it appears to not show up in calls to WindowUtils.getAllWindows(boolean).

I guess this may be intentional as a restriction of the OS, and might be bypassed by running the Java process as admin, but perhaps it's worth mentioning in the docs somewhere (if it's not already)? Just spent a fair chunk of time trying to figure out why a process was missing from the list of DesktopWindows that is returned. I admit I haven't spent much time looking into it, was just glad to realize that turning off the "Run as admin" toggle made the process show up again. I figure even if it doesn't warrant a minor update to the docs it can't hurt to document it as an issue here for posterity!

(this is on latest JNA)

matthiasblaesing commented 3 years ago

I tested with my JNA build machine and can not verify this finding. Running as the main user of the system, I get all windows, regardless whether they are started "As Administrator" or not. I see test errors for x86 on appveyor, that might be the same problem, but this is all not reproducable and currently speculation what is the reason. Could you investigate, what is required to reproduce?

matthiasblaesing commented 3 years ago

I tested some more and read some more and this leads me to the question: What is the architecture your JVM is running on? Could it be, that it is a 32bit process? I found some documentation, that reading data from a 64bit process, when the calling process is 32 bit might fail, because the pointer is truncated when passed.

rococode commented 3 years ago

I should first mention that this code used to work for me just fine, even when running as admin. I don't know what happened but at some point it just broke (iirc after a Windows Update), and after it broke I found out that running without admin made it work again.

So as I was writing my response here, I added in a println to my code that printed out dw.getTitle() for all processes (I was only printing filepaths before), and I noticed that the window title I was looking for was actually in the output, so the process is enumerated correctly. The actual problem is that the filepath from DesktopWindow.getfilePath() is missing for that process.

I've included the code I'm running at the bottom of this comment. It simply prints getFilePath() and dw.getTitle() for each DesktopWindow in the list returned from WindowUtils.getAllWindows(true).

Here's the relevant part of the output, when I've run my music player (clementine.exe) as admin:

Found: C:\Windows\explorer.exe []
Found: C:\Program Files\CSR\CSR Harmony Wireless Software Stack\vksts.exe [vksts]
>>> Found:  [Song Name]
Found: C:\Program Files\Mozilla Firefox\firefox.exe [Processes run as admin don't show up in WindowUtils.getAllWindows(boolean) · Issue #1269 · java-native-access/jna - Mozilla Firefox]

(>>> added for clarity) The [Song Name] line (the window title of my music player) is what I'm trying to find. As you can see, the filepath for it is empty.

When I exit clementine and re-run without admin, I see this:

Found: C:\Program Files\Mozilla Firefox\firefox.exe [Processes run as admin don't show up in WindowUtils.getAllWindows(boolean) · Issue #1269 · java-native-access/jna - Mozilla Firefox]
Found: C:\Program Files\Mozilla Firefox\firefox.exe [Homepage - Mozilla Firefox]
Found: C:\Program Files (x86)\Clementine\clementine.exe [LiSA - Yuukei Yesterday]

So somehow running as admin is making it so that DesktopWindow.getFilePath() is returning an empty string. But I've also just tried with a different process (sublime text - 64-bit), and running that as admin doesn't cause any issues, the filepath is still there.

OK sorry for the stream-of-consciousness writing but I just tried running a different 32-bit process (Skype) as admin and the filepath is missing for it too now!

The output when I run Skype as admin is: Found: [Skype] Running without admin: Found: C:\Program Files (x86)\Microsoft\Skype for Desktop\Skype.exe [Skype]

Just checked the only other convenient 32-bit process I have on hand (Discord) and it does the same:

Normal: Found: C:\Users\<my user>\AppData\Local\DiscordPTB\app-0.0.55\DiscordPTB.exe [#general - Discord] Run as admin: Found: [Discord]

So, conclusion from these new observations: Running a 32-bit process as admin may cause DesktopWindow.getFilePath() to return empty string. getTitle() still works.

Could you please check if you observe the same thing running 32-bit processes as admin? Right click->Run as administrator is enough to cause the problem on my machine.

Also doesn't explain why it suddenly stopped working, but I'll assume for now that the latest windows update changed something.

Here's the code I'm running (this reproduces the problem on my machine consistently):

    public static String test(String processName) {
        String res = null;
        List<DesktopWindow> windows = WindowUtils.getAllWindows(true);
        for (DesktopWindow dw : windows) {
            System.out.println("Found: " + dw.getFilePath() + " [" + dw.getTitle() + "]");
            if (dw.getFilePath().endsWith(processName)) {
                // below code is for returning the window title when correct process is found, mostly irrelevant for this issue
                String result = dw.getTitle();
                if (!result.isEmpty()) {
                    if (!result.contains("MSCTFIME") && !result.contains("Default IME")) {
                        if (res == null || res.length() < result.length()) {
                            if (res != null) {
                                System.out.println("Changing window name from " + res);
                            }
                            res = result;
                        }
                    }
                }
            }
        }
        return res;
    }

    public static void main(String[] args) {
        System.out.println(System.getProperty("sun.arch.data.model")); // confirm 64-bit jvm
        System.out.println(test("clementine.exe"));
    }

The JVM is 64-bit.

Here's my windows version info: Win 10 Pro Version 1909 OS Build 18363.1198

matthiasblaesing commented 3 years ago

Ah - I suspect your user has less privileges than my test user and you might hit his:

            final HANDLE process = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION | WinNT.PROCESS_VM_READ,
                                                                 false, pid.getValue());
            if (process == null) {
                if(Kernel32.INSTANCE.GetLastError() != WinNT.ERROR_ACCESS_DENIED) {
                    throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
                } else {
                    // Ignore windows, that can't be accessed
                    return "";
                }
            }

With a debugger and a break point on the return you should be able to check the problem.

matthiasblaesing commented 3 years ago

Please check latest master - it might help your case.