windows 找出 Java 中关注的应用程序(窗口)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5206633/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Find out what application (window) is in focus in Java
提问by Daniel Loureiro
I'd like to know how I can code a Java program that knows which Windows application is in focus. I can have many windows open but I want to know the one that's being used (like Google Chrome right now as I'm typing this).
我想知道如何编写一个知道哪个 Windows 应用程序是焦点的 Java 程序。我可以打开许多窗口,但我想知道正在使用的窗口(例如我正在输入的 Google Chrome)。
I don't need to change anything in the window or application, just need to know its name.
我不需要更改窗口或应用程序中的任何内容,只需要知道它的名称。
采纳答案by Bala R
I'm afraid there's no java api for that. JVM does not know anything about the windows it does not manage. You'll probably have to use JNI and call this function
恐怕没有java api。JVM 对它不管理的窗口一无所知。您可能必须使用 JNI 并调用此函数
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
PS. THere is a GetWindowText
function that you might want to use if you need to grab the title of the window.
附注。GetWindowText
如果您需要获取窗口的标题,您可能需要使用一个函数。
This posthas JNI examples that might be helpful for you.
这篇文章包含可能对您有所帮助的 JNI 示例。
回答by stefan.schwetschke
As the others have already pointer out, there is no portable way to get this on all platforms. But to make things worse: There not even a consistent way on MS Windows. I will provide some code that will solve the problem for different platforms and will point out the limitations. Use at your own risk, the code may provide wrong results or not run at all because of security reasons. If it runs on your machine, it will not mean that it will run equally well on other machines.
正如其他人已经指出的那样,没有可移植的方式在所有平台上获得它。但更糟糕的是:在 MS Windows 上甚至没有一致的方法。我将提供一些代码来解决不同平台的问题并指出局限性。使用风险自负,由于安全原因,代码可能会提供错误的结果或根本无法运行。如果它在您的机器上运行,并不意味着它在其他机器上也能运行得同样好。
The code uses JNA. During my experiments I had problems with different versions of JNA and the JNA platform library. It might be best to compile it yourself, so you have a consistent environment.
该代码使用 JNA。在我的实验中,我遇到了不同版本的 JNA 和 JNA 平台库的问题。最好自己编译它,这样你就有一个一致的环境。
Windows
视窗
The answer provided by kichik was correct at its time but will not work with Windows 8 in all cases. The problem is, that it will not handle Metro apps correctly. Unfortunately there is currently no stable API to get the name of the currently running Metro app. I have inserted some hints in the code, but it's best to wait until Microsoft will provide you with an API.
kichik 提供的答案当时是正确的,但在所有情况下都不适用于 Windows 8。问题是,它无法正确处理 Metro 应用程序。不幸的是,目前没有稳定的 API 来获取当前运行的 Metro 应用程序的名称。我在代码中插入了一些提示,但最好等到 Microsoft 为您提供 API。
On Windows you will also have problems with privileged apps and with the UAC dialog. So you will not always get a correct answer.
在 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
Linux / Unix / X11
With X11 we have three problems:
对于 X11,我们有三个问题:
- Because of network transparency, multiple windows from completely different machines might be mixed in the same X11. So neither name nor PID of the process belonging to a window might be make sense on the machine you are querying.
- Most windows managers have mutliple desktops. On each desktop there can be a different application in the foreground
- Tiling window managers (like XMonad) don't have the concept of a foreground window. They arrange all windows in a way, so each window is in the foreground at the same time.
- 由于网络透明,来自完全不同机器的多个窗口可能会混合在同一个 X11 中。因此,属于窗口的进程的名称和 PID 在您查询的机器上可能没有意义。
- 大多数 Windows 管理器都有多个桌面。在每个桌面上,前台可以有一个不同的应用程序
- 平铺窗口管理器(如XMonad)没有前景窗口的概念。它们以某种方式排列所有窗口,因此每个窗口同时位于前台。
On X11 it makes more sense to query for the window that currently has the focus.
在 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
Mac OS X does not focus on windows but on applications. So it makes sense to ask for the currently active application. Older versions of Mac OS X provide multiple desktops. Newer versions can have multiple fullscreen applications open at the same time. So you might not always get a correct answer.
Mac OS X 不专注于窗口,而是专注于应用程序。因此,询问当前活动的应用程序是有意义的。较旧版本的 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);
}
Conclusion
结论
When I played around with this code, it worked in the most basic cases. But if you want this code to run reliable, you will have to put in a lot of polish. Decide for yourself if it is worth it.
当我玩弄这段代码时,它在最基本的情况下都有效。但是,如果您希望此代码可靠运行,则必须进行大量润色。自己决定是否值得。
To make the code complete, here is the import section I used:
为了使代码完整,这是我使用的导入部分:
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;
Of course you will have to rearrange the parts of the code. I used one big class with the interfaces at the beginning a and then the rest in one big main method.
当然,您必须重新排列代码的各个部分。我在开头使用了一个带有接口的大类,然后在一个大的 main 方法中使用了其余的类。
回答by kichik
As Hovercraft Full Of Eels said, JNAis your best bet here. Unlike JNI, you won't have to compile any C code for it.
正如充满鳗鱼的气垫船所说,JNA是您最好的选择。与 JNI 不同,您不必为它编译任何 C 代码。
To get the process name:
获取进程名称:
- Call GetForegroundWindow()to get the window handle
- Call GetWindowThreadProcessId()to figure out which process owns it
- Call OpenProcess()to get a handle to the process (with
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ
) - Call GetModuleFileNameEx()to get the process name from the handle. You can also call GetModuleBaseName()for just the module name without the full path.
- 调用GetForegroundWindow()获取窗口句柄
- 调用GetWindowThreadProcessId()以确定哪个进程拥有它
- 调用OpenProcess()以获取进程的句柄(使用
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ
) - 调用GetModuleFileNameEx()从句柄中获取进程名称。您也可以只为模块名称调用GetModuleBaseName()而没有完整路径。
A full example is available in Getting active window information in Java
C code can be found here.
C 代码可以在这里找到。