找出Java中关注的应用程序(窗口)

Dan*_*iro 23 java windows

我想知道如何编写一个知道哪个Windows应用程序是焦点的Java程序.我可以打开很多窗口,但我想知道正在使用的那个窗口(就像我正在键入的那样,就像谷歌Chrome一样).

我不需要在窗口或应用程序中更改任何内容,只需要知道它的名称.

ste*_*hke 32

由于其他人已经指出,所以在所有平台上都没有可移植的方法.但更糟糕的是:在MS Windows上甚至没有一致的方法.我将提供一些代码来解决不同平台的问题,并指出其局限性.使用风险自负,代码可能会提供错误的结果或由于安全原因而无法运行.如果它在您的计算机上运行,​​并不意味着它将在其他计算机上运行良好.

代码使用JNA.在我的实验中,我遇到了不同版本的JNA和JNA平台库的问题.最好自己编译,这样你就拥有了一致的环境.

视窗

kichik提供的答案在当时是正确的,但在所有情况下都不适用于Windows 8.问题是,它无法正确处理Metro应用程序.不幸的是,目前没有稳定的API来获取当前运行的Metro应用程序的名称.我在代码中插入了一些提示,但最好等到Microsoft为您提供API.

在Windows上,您还将遇到特权应用程序和UAC对话框的问题.所以你不会总是得到正确的答案.

public interface Psapi extends StdCallLibrary {
    Psapi INSTANCE = (Psapi) Native.loadLibrary("Psapi", Psapi.class);

    WinDef.DWORD GetModuleBaseNameW(Pointer hProcess, Pointer hModule, byte[] lpBaseName, int nSize);
}
    if (Platform.isWindows()) {
        final int PROCESS_VM_READ=0x0010;
        final int PROCESS_QUERY_INFORMATION=0x0400;
        final User32 user32 = User32.INSTANCE;
        final Kernel32 kernel32=Kernel32.INSTANCE;
        final Psapi psapi = Psapi.INSTANCE;
        WinDef.HWND windowHandle=user32.GetForegroundWindow();
        IntByReference pid= new IntByReference();
        user32.GetWindowThreadProcessId(windowHandle, pid);
        WinNT.HANDLE processHandle=kernel32.OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, true, pid.getValue());

        byte[] filename = new byte[512];
        Psapi.INSTANCE.GetModuleBaseNameW(processHandle.getPointer(), Pointer.NULL, filename, filename.length);
        String name=new String(filename);
        System.out.println(name);
        if (name.endsWith("wwahost.exe")) { // Metro App
            // There is no stable API to get the current Metro app
            // But you can guestimate the name form the current directory of the process
            // To query this, see:
            // http://stackoverflow.com/questions/16110936/read-other-process-current-directory-in-c-sharp
        }
Run Code Online (Sandbox Code Playgroud)

Linux/Unix/X11

使用X11,我们有三个问题:

  1. 由于网络透明性,来自完全不同机器的多个窗口可能会混合在同一个X11中.因此,属于窗口的进程的名称和PID在您查询的计算机上都没有意义.
  2. 大多数Windows管理员都有多个桌面.在每个桌面上,前台可以有不同的应用程序
  3. 平铺窗口管理器(如XMonad)没有前景窗口的概念.它们以某种方式排列所有窗口,因此每个窗口同时位于前景中.

在X11上,查询当前具有焦点的窗口更有意义.

public interface XLib extends StdCallLibrary {
    XLib INSTANCE = (XLib) Native.loadLibrary("XLib", Psapi.class);

    int XGetInputFocus(X11.Display display, X11.Window focus_return, Pointer revert_to_return);
}

if(Platform.isLinux()) {  // Possibly most of the Unix systems will work here too, e.g. FreeBSD
        final X11 x11 = X11.INSTANCE;
        final XLib xlib= XLib.INSTANCE;
        X11.Display display = x11.XOpenDisplay(null);
        X11.Window window=new X11.Window();
        xlib.XGetInputFocus(display, window,Pointer.NULL);
        X11.XTextProperty name=new X11.XTextProperty();
        x11.XGetWMName(display, window, name);
        System.out.println(name.toString());
    }
Run Code Online (Sandbox Code Playgroud)

Mac OS X.

Mac OS X不专注于Windows,而是专注于应用程序.因此,请求当前活动的应用程序是有意义的.较早版本的Mac OS X提供多个桌面.较新的版本可以同时打开多个全屏应用程序.所以你可能并不总能得到正确的答案.

    if(Platform.isMac()) {
        final String script="tell application \"System Events\"\n" +
                "\tname of application processes whose frontmost is tru\n" +
                "end";
        ScriptEngine appleScript=new ScriptEngineManager().getEngineByName("AppleScript");
        String result=(String)appleScript.eval(script);
        System.out.println(result);
    }
Run Code Online (Sandbox Code Playgroud)

结论

当我玩这个代码时,它在最基本的情况下工作.但是,如果您希望此代码运行可靠,则必须进行大量修改.如果值得的话,自己决定.

为了完成代码,这里是我使用的导入部分:

    import com.sun.jna.Native;
    import com.sun.jna.Platform;
    import com.sun.jna.Pointer;
    import com.sun.jna.platform.unix.X11;
    import com.sun.jna.platform.win32.Kernel32;
    import com.sun.jna.platform.win32.User32;
    import com.sun.jna.platform.win32.WinDef;
    import com.sun.jna.platform.win32.WinNT;
    import com.sun.jna.ptr.IntByReference;
    import com.sun.jna.win32.StdCallLibrary;

    import javax.script.ScriptEngine;
    import javax.script.ScriptEngineManager;
    import javax.script.ScriptException;
Run Code Online (Sandbox Code Playgroud)

当然,您必须重新排列代码的各个部分.我在一个大的主要方法中使用了一个大类,其中的接口位于开头a,然后是其余的大类.


Bal*_*a R 11

我担心没有java api.JVM对它不管理的窗口一无所知.您可能必须使用JNI并调用此函数

[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
Run Code Online (Sandbox Code Playgroud)

MSDN链接

PS.GetWindowText如果您需要获取窗口标题,则可能需要使用此功能.

这篇文章有JNI示例,可能对您有所帮助.

  • 对于我的2美分,我认为你使用JNA而不是JNI要好得多.它基于JNI,但使用起来要简单得多(我认为). (3认同)

kic*_*hik 6

正如Hovercraft Full Of Eels所说,JNA是你最好的选择.与JNI不同,您不必为其编译任何C代码.

要获取进程名称:

  1. 调用GetForegroundWindow()来获取窗口句柄
  2. 调用GetWindowThreadProcessId()来确定哪个进程拥有它
  3. 调用OpenProcess()来获取进程的句柄(with PROCESS_QUERY_INFORMATION | PROCESS_VM_READ)
  4. 调用GetModuleFileNameEx()以从句柄中获取进程名称.您也可以仅为模块名称调用GetModuleBaseName()而不使用完整路径.

在Java获取活动窗口信息中提供了完整示例

C代码可以在这里找到.