Узнайте, какое приложение (окно) находится в фокусе на Java
Я хотел бы знать, как я могу закодировать программу Java, которая знает, какое приложение Windows находится в фокусе. Я могу открыть много окон, но хочу знать тот, который используется (например, Google Chrome сейчас, когда я набираю это).
Мне не нужно ничего менять в окне или приложении, просто нужно знать его имя.
Ответы
Ответ 1
Я боюсь, что для этого нет java api. JVM ничего не знает о тех окнах, которыми он не управляется. Вероятно, вам придется использовать JNI и вызвать эту функцию
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
Ссылка MSDN
PS. Это функция GetWindowText
, которую вы можете использовать, если вам нужно захватить заголовок окна.
В этом сообщении есть примеры JNI, которые могут вам помочь.
Ответ 2
Поскольку другие уже имеют указатель, нет никакого переносного способа получить это на всех платформах. Но ухудшить ситуацию: в 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 мы имеем три проблемы:
- Из-за прозрачности сети в одном и том же X11 могут быть смешаны несколько окон с совершенно разных машин. Поэтому ни имя, ни PID процесса, принадлежащие окну, могут иметь смысл на машине, которую вы запрашиваете.
- Большинство менеджеров окон имеют несколько рабочих столов. На каждом рабочем столе на переднем плане может быть другое приложение
- Менеджеры оконной панели (например, 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());
}
Mac OS X
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);
}
Заключение
Когда я играл с этим кодом, он работал в самых простых случаях. Но если вы хотите, чтобы этот код работал надежно, вам придется много пользы. Решите сами, если это того стоит.
Чтобы завершить код, вот раздел импорта, который я использовал:
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;
Конечно, вам придется перестроить части кода. Я использовал один большой класс с интерфейсами в начале a, а затем остальные в одном большом главном методе.
Ответ 3
Как сказал Hovercraft Full Of Eels, JNA - ваш лучший выбор здесь. В отличие от JNI, вам не придется компилировать для него код C.
Чтобы получить имя процесса:
Полный пример доступен в Получение активной информации о окне в Java
C код можно найти здесь.