【问题标题】:How to get list of all window handles in Java (Using JNA) on MacOS?如何在 MacOS 上获取 Java(使用 JNA)中所有窗口句柄的列表?
【发布时间】:2020-08-15 10:37:44
【问题描述】:

我一直在使用 JNA 库来获取 Windows 中所有可见的窗口句柄。我需要在 macOS 中使用 JNA 做类似的事情。

这是获取 Windows 中所有窗口句柄的代码:

 public static List<HWND> findAll() {
    final List<HWND> windows = new LinkedList<>();
    User32.INSTANCE.EnumWindows(new User32.WNDENUMPROC() {
        @Override
        public boolean callback(HWND hWnd, Pointer arg) {
            if (User32.INSTANCE.IsWindowVisible(hWnd)) {
                windows.add(hWnd);
            }
            return true;
        } 
    }, null);
    return windows;
}

macOS 中的等效代码是什么?

【问题讨论】:

    标签: java macos jna


    【解决方案1】:

    您需要映射Core Graphics Framework 的部分内容。您可以使用CGWindowListCopyWindowInfo() 函数列出窗口。

    要加载框架,您需要映射一个扩展 JNA 的 Library 类的 CoreGraphics 接口,并映射您需要的函数:

    public interface CoreGraphics extends Library {
        CoreGraphics INSTANCE = Native.load("CoreGraphics", CoreGraphics.class);
    
        CFArrayRef CGWindowListCopyWindowInfo(int option, int relativeToWindow);
    }
    

    CFArrayRef 类型已映射到 JNA 中的 CoreFoundation 类中。选择合适的Window List Option(可能是kCGWindowListOptionAll = 0)。如果您已经有窗口编号,则可以使用相对重新引用,否则您将使用 kCGNullWindowID (0) 作为第二个参数。从您的代码中调用它应该很简单:

    CFArrayRef windowInfo = CoreGraphics.INSTANCE.CGWindowListCopyWindowInfo(0, 0);
    

    这将为您提供代表窗口的CFDictionaryRef 对象数组。您可以迭代数组,然后使用CFDictionaryRef 类中的其他方法来探索这些字典对象:您将为键创建一个CFString。所需键的列表记录在 here 中,可选键为 here。常量字符串match the variable name

    这应该为每个窗口编号(“句柄”等效项)提供CFNumberRef

    // Set up keys for dictionary lookup
    CFStringRef kCGWindowNumber = CFStringRef.createCFString("kCGWindowNumber");
    CFStringRef kCGWindowOwnerPID = CFStringRef.createCFString("kCGWindowOwnerPID");
    // Note: the Quartz name is rarely used
    CFStringRef kCGWindowName = CFStringRef.createCFString("kCGWindowName");
    CFStringRef kCGWindowOwnerName = CFStringRef.createCFString("kCGWindowOwnerName");
    
    // Iterate the array
    int numWindows = windowInfo.getCount();
    for (int i = 0; i < numWindows; i++) {
        // For each array element, get the dictionary
        Pointer result = windowInfo.getValueAtIndex(i);
        CFDictionaryRef windowRef = new CFDictionaryRef(result);
    
        // Now get information from the dictionary.
    
        // Get a pointer to the result, in this case a CFNumber
        result = windowRef.getValue(kCGWindowNumber);
        // "Cast" the pointer to the appropriate type
        CFNumberRef windowNumber = new CFNumberRef(result);
        // CoreFoundation.INSTANCE.CFNumberGetType(windowNumber)
        // --> 4 = kCFNumberSInt64Type, signed 64 bit so use getLong()
        
        // Get a pointer to the result, in this case a CFNumber
        result = windowRef.getValue(kCGWindowOwnerPID);
        // "Cast" the pointer to the appropriate type
        CFNumberRef windowOwnerPID = new CFNumberRef(result);
        // CoreFoundation.INSTANCE.CFNumberGetType(windowOwnerPID)
        // --> 4 = kCFNumberSInt64Type, signed 64 bit so use getLong()
    
        // Get a pointer to the result, in this case a CFString
        result = windowRef.getValue(kCGWindowName);
        // "Cast" the pointer to the appropriate type
        // Optional key, check for null
        String windowName = result == null ? "" : new CFStringRef(result).stringValue();
    
        // Get a pointer to the result, in this case a CFString
        result = windowRef.getValue(kCGWindowOwnerName);
        // "Cast" the pointer to the appropriate type
        // Optional key, check for null
        String windowOwnerName = result == null ? "" : new CFStringRef(result).stringValue();
    
        // ... look up other keys if needed ...
        // use ProcessHandle with the PID to get start time
    
        // Output or add to List, etc.
        System.out.println(windowNumber.longValue() 
            + " (" + windowOwnerName + ", pid=" 
            + windowOwnerPID.longValue()
            + "): " + windowName);
    }
    
    // CF references from "Copy" or "Create" must be released
    // release the created key references
    kCGWindowNumber.release();
    kCGWindowOwnerPID.release();
    kCGWindowName.release();
    kCGWindowOwnerName.release();
    // release the array
    windowInfo.release();
    

    【讨论】:

    • CoreGraphics 怎么样? CoreGraphics 也在 JNA 中?
    • 您将创建自己的类来扩展Library。我在答案中包含了语法。
    • 如何获取窗口名称(标题)、进程名称、进程 ID 和进程开始时间?
    • 窗口名称在信息字典中,使用键kCGWindowName。拥有进程名称位于键 kCGWindowOwnerName。拥有进程 ID 位于 kCGWindowOwnerPID。要获取开始时间,您可以使用 PID 在 Java 9+ 中获取 ProcessHandle
    • 感谢您的帮助和时间..希望这将适用于 mac @Daniel Widdis
    猜你喜欢
    • 2012-02-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-26
    相关资源
    最近更新 更多