如何在macos上用java(使用jna)获得前台窗口/进程?

bq8i3lrv  于 2021-07-12  发布在  Java
关注(0)|答案(2)|浏览(844)

目前,我正在努力获得前景(顶部)窗口/进程在微软视窗。我需要在macos中使用jna做一些类似的事情。
macos中的等效代码是什么?

byte[] windowText = new byte[512];
  PointerType hwnd = User32.INSTANCE.GetForegroundWindow();  
  User32.INSTANCE.GetWindowTextA(hwnd, windowText, 512);
  System.out.println(Native.toString(windowText));
7dl7o3gd

7dl7o3gd1#

这里实际上有两个问题,前台窗口和前台进程。我会尽量回答这两个问题。
对于前台进程,使用jna的一个简单方法是Map应用程序服务api。请注意,这些函数是在10.9中引入的,现在已被弃用,但从10.15开始仍然可以使用。更新的版本在 AppKitLibrary ,见下文。
创建这个类,Map您需要的两个函数:

public interface ApplicationServices extends Library {
    ApplicationServices INSTANCE = Native.load("ApplicationServices", ApplicationServices.class);

    int GetFrontProcess(LongByReference processSerialNumber);
    int GetProcessPID(LongByReference processSerialNumber, IntByReference pid);
}

“前景”过程可以通过 GetFrontProcess() . 返回一个叫做 ProcessSerialNumber ,在整个应用程序服务api中使用的唯一64位值。要将其转换为用户空间使用,您可能需要进程id,并且 GetProcessPID() 帮你翻译一下。

LongByReference psn = new LongByReference();
IntByReference pid = new IntByReference();
ApplicationServices.INSTANCE.GetFrontProcess(psn);
ApplicationServices.INSTANCE.GetProcessPID(psn, pid);
System.out.println("Front process pid: " + pid.getValue());

虽然上面的方法有效,但不推荐使用。新应用程序应使用 AppKitLibrary :

public interface AppKitLibrary extends Library {
    AppKitLibrary INSTANCE = Native.load("AppKitLibrary", AppKitLibrary.class);
}

关于使用此库的最顶层应用程序,还有其他多个stackoverflow问题,例如这个问题。Map所有需要的导入和对象比我在这里的答案中花的时间多得多,但您可能会发现它很有用。可能更容易弄清楚如何使用洛可可框架(它在引擎盖下使用jna,但已经通过jnavigatorMap了所有appkit)来访问这个api。一些javadocs在这里。
也有使用 AppleScript 可以通过命令行从java执行 Runtime.exec() 以及捕捉输出。
关于屏幕上的前景窗口,它有点复杂。在我回答您之前关于在macos上迭代所有窗口的问题时,我回答了如何获取使用macos的所有窗口的列表 CoreGraphics 通过jna,包括 CFDictionary 包含更多信息。
其中一个字典键是 kCGWindowLayer 它将返回一个 CFNumber 表示窗口层编号。文件上说这是32位的,所以 intValue() 是合适的。数字是“绘图顺序”,因此较高的数字将覆盖较低的数字。因此,您可以遍历所有检索到的窗口并找到最大数目。这将是“前景”层。
有一些警告:
实际上只有20层可用。许多事物共享一层。
第1000层是屏幕保护程序。可以忽略1000层或更高的层。
第24层是dock,通常在上面,第25层(dock上的图标)在更高的层次上。
层0似乎是桌面的其余部分。
哪个窗口“在最上面”取决于你在屏幕上看的位置。在dock上,dock将位于前台(或应用程序图标)。在屏幕的其余部分,您需要检查正在评估的像素与从coregraphics窗口获得的屏幕矩形(使用 kCGWindowBounds 返回 CGRect (一种有4个双倍x,y,宽,高的结构)。
你需要过滤到屏幕上的窗口。如果您已经获取了列表,则可以使用 kCGWindowIsOnscreen 键确定窗口是否可见。它返回一个 CFBoolean . 因为该键是可选的,所以您需要测试 null . 但是,如果您是从零开始,那么最好使用 kCGWindowListOptionOnScreenOnly 初始调用时的窗口选项常量 CGWindowListCopyWindowInfo() .
除了迭代所有窗口之外 CGWindowListCopyWindowInfo() 函数需要 CGWindowID 参数 relativeToWindow 您可以添加(按位或) kCGWindowListOptionOnScreenAboveWindow 到选项。
最后,您可能会发现限制与当前会话相关联的窗口可能很有用,您应该Map CGWindowListCreate() 使用与 CopyInfo() 变体。它返回一个窗口编号数组,您可以将字典搜索限制为该数组,或者将该数组作为参数传递给它 CGWindowListCreateDescriptionFromArray() .
正如我在前面的回答中提到的,您“拥有”了使用 Create 或者 Copy 函数,并负责在完成时释放它们,以避免内存泄漏。

4zcjmb1e

4zcjmb1e2#

AppleScriptEngine appleEngine = new apple.applescript.AppleScriptEngine();
    ArrayList<String> processNames = null;
    try {
        String processName = null;
        processNames = (ArrayList<String>) appleEngine
                .eval("tell application \"System Events\" to get name of application processes whose frontmost is true and visible is true");
        if (processNames.size() > 0) {
            processName = processNames.get(0);// the front most process name
        }
        return processName;
    } catch (ScriptException e) {
        log.debug("no app running");
    }

相关问题