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

时间:2011-03-05 20:38:41

标签: java windows

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

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

3 个答案:

答案 0 :(得分:31)

由于其他人已经指出,所以在所有平台上都没有可移植的方法。但更糟糕的是:在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
        }

Linux / Unix / X11

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

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

    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);
        }
    

    结论

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

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

        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;
    

    当然,你必须重新安排代码的各个部分。我在一个主要的方法中使用了一个带有接口的大类,然后是一个大的主要方法。

答案 1 :(得分:12)

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

[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();

MSDN link

PS。这是一个GetWindowText函数,如果你需要获取窗口的标题,你可能想要使用它。

This post有可能对您有帮助的JNI示例。

答案 2 :(得分:6)

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

获取进程名称:

  1. 致电GetForegroundWindow()以获取窗口句柄
  2. 致电GetWindowThreadProcessId()以确定拥有该流程的流程
  3. 致电OpenProcess()以获取流程的句柄(使用PROCESS_QUERY_INFORMATION | PROCESS_VM_READ
  4. 调用GetModuleFileNameEx()从句柄中获取进程名称。您也可以仅为模块名称调用GetModuleBaseName()而不使用完整路径。
  5. Getting active window information in Java

    中提供了完整的示例

    可以找到C代码here