How can I get name of currently running app (same as in task manager) with Java? (Windows)

62 Views Asked by At

Here is my code which is supposed to print name of currently used app:

import com.sun.jna.Native;
import com.sun.jna.platform.win32.*;
import com.sun.jna.ptr.IntByReference;

public class AppList {

    public static void main(String[] args) {

        // PRINT TITLE OF ACTIVE WINDOW (NOT WANTED)
        User32 user32 = User32.INSTANCE;
        WinDef.HWND hwnd = user32.GetForegroundWindow();
        char[] title = new char[1024];
        user32.GetWindowText(hwnd, title, title.length);
        System.out.println("Active window: " + String.valueOf(title));
        // "Active window: Project4 - AppList.java"

        // PRINT PATH OF ACTIVE APP AND FORMAT IT (BETTER BUT STILL NOT WANTED)
        IntByReference processId = new IntByReference();
        int id = user32.GetWindowThreadProcessId(hwnd, processId);
        WinNT.HANDLE processHandle = Kernel32.INSTANCE.OpenProcess(
                WinNT.PROCESS_QUERY_INFORMATION | WinNT.PROCESS_VM_READ, false, processId.getValue());
        char[] exePath = new char[1024];
        Psapi.INSTANCE.GetModuleFileNameExW(processHandle, null, exePath, exePath.length);
        String exePathStr = Native.toString(exePath);
        System.out.println(exePathStr);
        // "C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2023.3.2\bin\idea64.exe"

        // FORMAT IT
        String activeAppName = exePathStr.substring(exePathStr.lastIndexOf("\\") + 1)
                .replace(".exe", "").toLowerCase();
        char firstChar = Character.toUpperCase(activeAppName.charAt(0));
        activeAppName = firstChar + activeAppName.substring(1);

        System.out.println(activeAppName);
        Kernel32.INSTANCE.CloseHandle(processHandle);
        // "Idea64" instead of "InteliJ IDEA"
        // "Chrome" instead of "Google Chrome"
        // "Applicationframehost" instead of "Settings"
        // Task manager doesn't work at all
    }
}

As you can see it doesn't work correctly. I would like to get names of apps that are in the task manager (InteliJ IDEA, Google Chrome)

-Using the first option gives me title of window. It works well in some cases but usually sucks.

-Using the second option gives me path of the current app and formats it. Also works well in some cases (e.g. Spotify) but in most cases it gives me some weird names or doesn't work ar all.

I've spent hours googling and asking AI but I can't find anything. These are the only options I've found.

1

There are 1 best solutions below

0
Nick Gris On

After MUCH fiddling around trying to work out how to use JNA and correctly specify and manipulate parameters with the Win32 API, I have a working sample which ended up being nothing like the link I gave - that was far too manual:

import com.sun.jna.platform.win32.*;

import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;

public class AppList {
    public static void main(String[] args) {
        // Just my luck, Eclipse doesn't have version info in it. This one does... 
        String testAppFilespec = "D:\\Programming\\Android\\Android Studio\\bin\\studio64.exe"; 
        String appDescription = "<Unable to retrieve>"; 
        
        // Find out how big the VERSIONINFO structure in the file is. 
        IntByReference hVerHandle = new IntByReference(); 
        int verSize = Version.INSTANCE.GetFileVersionInfoSize(testAppFilespec, hVerHandle); 
        
        // Create a memory buffer and try to grab the version info into it. 
        if (verSize > 0) { 
            Memory pInfoStruct = new Memory(verSize); 
            boolean gotInfo = Version.INSTANCE.GetFileVersionInfo(testAppFilespec, 
                                                                    0, verSize, 
                                                                    pInfoStruct); 
            if (gotInfo) { 
                // Grab the list of language / codepage pairs. 
                PointerByReference pLangCodePageList = new PointerByReference(null); 
                IntByReference pCPSize = new IntByReference(); 
                if (Version.INSTANCE.VerQueryValue(pInfoStruct, 
                                                    "\\VarFileInfo\\Translation", 
                                                    pLangCodePageList, pCPSize) 
                        && pCPSize.getValue() > 0) { 

                    // We'll use the description from the first entry only: 
                    
                    // pCPSize.getValue() has the number of bytes in ONE 
                    // language / codepage entry. 

                    // Don't use getPointer() here!!! Had me scratching my head 
                    // for hours... 
                    Pointer pEntry = pLangCodePageList.getValue();                  
                    byte[] langCodePageBytes 
                        = pEntry.getByteArray(0,  pCPSize.getValue() ); 
                    
                    // Format the name of the sub-block containing the 
                    // description of this app (watch byte order here): 
                    String subBlockName 
                        = "\\StringFileInfo\\" 
                        + String.format("%02x", langCodePageBytes[1] ) 
                        + String.format("%02x", langCodePageBytes[0] ) 
                        + String.format("%02x", langCodePageBytes[3] ) 
                        + String.format("%02x", langCodePageBytes[2] ) 
                        + "\\FileDescription"; 
                    
                    // Now ask for this value out of the full info struct: 
                    PointerByReference pDesc = new PointerByReference(); 
                    if (Version.INSTANCE.VerQueryValue(pInfoStruct, 
                                                        subBlockName, 
                                                        pDesc, pCPSize) 
                            && pCPSize.getValue() > 0) { 

                        // Strings are all wide from this info struct: 
                        appDescription = pDesc.getValue().getWideString(0); 
                        
                    }
                    
                }

            }        

        }

        System.out.println("App description: " + appDescription); 
        
    }
    
}

Enjoy!

(NB: You may have to include a package declaration at the top - I did for Eclipse, but I've left it out of this code.)