我正在开发一个可以在屏幕上移动第三方应用程序窗口的应用程序。
要概述所有当前打开的窗口,我使用
CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
返回定义每个打开窗口的字典数组。 这是一个返回的示例字典:
{
kCGWindowAlpha = 1;
kCGWindowBounds = {
Height = 442;
Width = 475;
X = 3123;
Y = "-118";
};
kCGWindowIsOnscreen = 1;
kCGWindowLayer = 0;
kCGWindowMemoryUsage = 907184;
kCGWindowName = Untitled;
kCGWindowNumber = 7328;
kCGWindowOwnerName = TextEdit;
kCGWindowOwnerPID = 20706;
kCGWindowSharingState = 1;
kCGWindowStoreType = 2;
kCGWindowWorkspace = 3;
},
字典中充满了其他地方使用的好信息,但缺少可用于修改窗口位置的辅助功能对象。窗口编号清楚地标识了Windows。
我现在正在使用PID(kCGWindowOwnerPID)为窗口的应用程序创建一个辅助功能对象:
AXUIElementRef app = AXUIElementCreateApplication(pid);
然后使用AXUIElementCopyAttributeValues检索应用程序已打开的所有窗口的列表:
NSArray *result;
AXUIElementCopyAttributeValues(
(AXUIElementRef) app,
kAXWindowsAttribute,
0,
99999,
(CFArrayRef *) &result
);
这可以工作并返回一个AXUIElements数组。 这是我被困的地方。似乎没有API调用来检索辅助功能对象的窗口号。有没有办法
a)找到辅助功能对象的窗口编号(最终迭代数组并找到正确的窗口)
或
b)否则,将CGWindowListCopyWindowInfo返回的数组中描述的窗口与AXUIElementCopyAttributeValues返回的辅助功能对象清楚地匹配?
答案 0 :(得分:23)
我们最终聘请了一位专门的辅助功能开发人员来完成这项任务。
事实证明,如果不使用未记录的API,就无法做到这一点(在我们的案例中不行)。
幸运的是,有一个实际的解决方法:
遍历应用程序的所有打开窗口。获得他们的位置,大小和头衔:
AXUIElementCopyAttributeValue(target, kAXPositionAttribute, CFTypeRef*)&posValue);
AXUIElementCopyAttributeValue(target, kAXSizeAttribute, (CFTypeRef*)&sizeValue);
AXUIElementCopyAttributeValue(target, kAXTitleAttribute, (CFTypeRef*)&titleValue);
接下来,将位置和尺寸转换为实际的CGPoint
和CGSize
值:
AXValueGetValue(posValue, kAXValueCGPointType, &point);
AXValueGetValue(sizeValue, kAXValueCGSizeType, &size);
将大小,位置和标题与CGWindowListCopyWindowInfo()
中对象返回的值进行比较。
如果它们匹配,你可以放心地假设它是你正在寻找的窗口,并使用已经打开的AXUIElement(在我们的例子中是target
)来工作。
循环浏览所有打开的窗口的开销在OSX上可以忽略不计。对于同时打开多少个窗口的上限非常低。
此外,虽然这不是100%准确(2个窗口可能有相同的位置,大小和标题),但到目前为止,我们还没有遇到实际使用情况。
答案 1 :(得分:9)
有一个私有函数用于获取窗口的给定AX对象的CG窗口编号:_AXUIElementGetWindow
。
SO讨论中的更多细节Uniquely identify active window on OS X
看起来没有公共API以100%的概率完成任务。按标题和框架识别窗口(如上面的答案中所述)将在99.9%的情况下有效。