在Java中找出应用程序(窗口)的焦点

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

我不需要在窗口或应用程序中改变任何东西,只需要知道它的名字。

恐怕没有Java的API。 JVM不知道有关它不管理的窗口。 你可能不得不使用JNI并调用这个函数

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

MSDN链接

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

这篇文章有JNI的例子,可能会对你有所帮助。

正如其他人已经指出的那样,在所有平台上都没有可行的方法。 但更糟糕的是,在MS Windows上甚至没有一致的方法。 我将提供一些解决不同平台问题的代码,并指出其中的局限性。 由于安全原因,使用风险自担,代码可能会提供错误的结果或根本不运行。 如果它在你的机器上运行,这并不意味着它可以在其他机器上运行。

代码使用JNA。 在我的实验中,我遇到了不同版本的JNA和JNA平台库的问题。 编译它可能是最好的,所以你有一个一致的环境。

视窗

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

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

 public interface Psapi extends StdCallLibrary { Psapi INSTANCE = (Psapi) Native.loadLibrary("Psapi", Psapi.class); WinDef.DWORD GetmoduleeBaseNameW(Pointer hProcess, Pointer hmodulee, 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 coreel32 kernel32=coreel32.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.GetmoduleeBaseNameW(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 )没有前景窗口的概念。 他们安排所有的窗户,所以每个窗口同时在前台。

在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, eg 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.coreel32; 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; 

当然,你将不得不重新安排代码的部分。 我在开始的时候用了一个大接口的类,然后用一个大的主要方法。

作为气垫船充满鳗鱼说, JNA是你最好的选择在这里。 不像JNI,你不必为它编译任何C代码。

要获取进程名称:

  1. 调用GetForegroundWindow()来获得窗口句柄
  2. 调用GetWindowThreadProcessId()来确定哪个进程拥有它
  3. 调用OpenProcess()获取进程的句柄(使用PROCESS_QUERY_INFORMATION | PROCESS_VM_READ
  4. 调用GetmoduleeFileNameEx()从句柄中获取进程名称。 你也可以调用GetmoduleeBaseName()来获取没有完整路径的模块名称。

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

C代码可以在这里找到。