Поскольку другие уже указали, нет портативного способа получить это на всех платформах. Но что еще хуже: на MS Windows нет даже единого пути. Я предоставлю код, который решит проблему для разных платформ и укажет на ограничения. Используйте на свой страх и риск, код может дать неправильные результаты или не работать вообще из соображений безопасности. Если он работает на вашей машине, это не означает, что он будет одинаково хорошо работать на других машинах.
Код использует JNA. Во время моих экспериментов у меня были проблемы с различными версиями JNA и библиотекой платформы JNA. Лучше всего скомпилировать его самостоятельно, чтобы у вас была согласованная среда.
Windows
Ответ, предоставленный 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 процесса, относящегося к окну, не имеют смысла на запрашиваемой машине.
- Большинство оконных менеджеров имеют несколько рабочих столов. На каждом рабочем столе может быть различное приложение на переднем плане
- Менеджеры окон Tiling (как 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, а затем остальные в одном большом основном методе.