英文:
How to get list of all window handles in Java (Using JNA) on MacOS?
问题
Sure, here's the equivalent code in macOS using JNA:
public static List<NSWindow> findAll() {
final List<NSWindow> windows = new LinkedList<>();
CoreFoundation.INSTANCE.CFArrayCallBacks arrayCallBacks = new CoreFoundation.INSTANCE.CFArrayCallBacks();
PointerByReference windowList = CoreFoundation.INSTANCE.CFArrayCreateMutable(Pointer.NULL, 0, arrayCallBacks);
Quartz.INSTANCE.CGWindowListApplierFunction listApplier = new Quartz.INSTANCE.CGWindowListApplierFunction() {
@Override
public int applyWindow(Pointer window, Pointer context) {
windows.add(new NSWindow(window));
return 0;
}
};
Quartz.INSTANCE.CGWindowListCopyWindowInfo(Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID, listApplier, null);
CoreFoundation.INSTANCE.CFRelease(windowList);
return windows;
}
Make sure to import the appropriate JNA classes and adjust the code as needed for your project.
英文:
I have been using the JNA library to get all visible window handles in Windows. I need to do something similar in macOS using JNA.
Here is code to get all window handles in 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;
}
What is the equivalent code in macOS?
答案1
得分: 0
你需要映射 Core Graphics 框架 的部分内容。可以使用 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
。所需键的列表在此处有文档记录,可选键在此处。常量字符串与变量名匹配。
这样,你应该可以得到每个窗口号的 CFNumberRef
(相当于“句柄”):
// 为字典查找设置键
CFStringRef kCGWindowNumber = CFStringRef.createCFString("kCGWindowNumber");
CFStringRef kCGWindowOwnerPID = CFStringRef.createCFString("kCGWindowOwnerPID");
// 注意:很少使用 Quartz 名称
CFStringRef kCGWindowName = CFStringRef.createCFString("kCGWindowName");
CFStringRef kCGWindowOwnerName = CFStringRef.createCFString("kCGWindowOwnerName");
// 迭代数组
int numWindows = windowInfo.getCount();
for (int i = 0; i < numWindows; i++) {
// 对于每个数组元素,获取字典
Pointer result = windowInfo.getValueAtIndex(i);
CFDictionaryRef windowRef = new CFDictionaryRef(result);
// 现在从字典中获取信息。
// 获取结果的指针,这里是 CFNumber
result = windowRef.getValue(kCGWindowNumber);
// 将指针“转换”为适当的类型
CFNumberRef windowNumber = new CFNumberRef(result);
// CoreFoundation.INSTANCE.CFNumberGetType(windowNumber)
// --> 4 = kCFNumberSInt64Type,有符号 64 位,因此使用 getLong()
// 获取结果的指针,这里是 CFNumber
result = windowRef.getValue(kCGWindowOwnerPID);
// 将指针“转换”为适当的类型
CFNumberRef windowOwnerPID = new CFNumberRef(result);
// CoreFoundation.INSTANCE.CFNumberGetType(windowOwnerPID)
// --> 4 = kCFNumberSInt64Type,有符号 64 位,因此使用 getLong()
// 获取结果的指针,这里是 CFString
result = windowRef.getValue(kCGWindowName);
// 将指针“转换”为适当的类型
// 可选键,检查是否为 null
String windowName = result == null ? "" : new CFStringRef(result).stringValue();
// 获取结果的指针,这里是 CFString
result = windowRef.getValue(kCGWindowOwnerName);
// 将指针“转换”为适当的类型
// 可选键,检查是否为 null
String windowOwnerName = result == null ? "" : new CFStringRef(result).stringValue();
// ... 如果需要,查找其他键 ...
// 使用进程句柄和 PID 获取启动时间
// 输出或添加到列表等
System.out.println(windowNumber.longValue()
+ " (" + windowOwnerName + ", pid="
+ windowOwnerPID.longValue()
+ "): " + windowName);
}
// 必须释放“Copy”或“Create”生成的 CF 引用
// 释放创建的键引用
kCGWindowNumber.release();
kCGWindowOwnerPID.release();
kCGWindowName.release();
kCGWindowOwnerName.release();
// 释放数组
windowInfo.release();
英文:
You'll need to map portions of the Core Graphics Framework. You can list windows using the CGWindowListCopyWindowInfo() function.
To load the framework you'll need to map a CoreGraphics
interface extending JNA's Library
class, and map the function you need:
public interface CoreGraphics extends Library {
CoreGraphics INSTANCE = Native.load("CoreGraphics", CoreGraphics.class);
CFArrayRef CGWindowListCopyWindowInfo(int option, int relativeToWindow);
}
The CFArrayRef
type is already mapped in JNA in the CoreFoundation
class. Pick the appropriate Window List Option (probably kCGWindowListOptionAll
= 0). If you already had a window number you could use relative reerences, otherwise you'll use kCGNullWindowID
(0) for the second parameter. Calling it from your code should be simple:
CFArrayRef windowInfo = CoreGraphics.INSTANCE.CGWindowListCopyWindowInfo(0, 0);
That will give you an array of CFDictionaryRef
objects representing the windows. You can iterate the array and then use further methods in the CFDictionaryRef
class to explore these dictionary objects: you'll create a CFString
for the keys. A list of required keys is documented here and optional keys are here. The constant strings match the variable name.
This should get you a CFNumberRef
for each window number (the "handle" equivalent):
// 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();
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论