通过OSX Accessibility API获取窗口号

时间:2011-05-30 16:52:38

标签: objective-c cocoa accessibility macos-carbon accessibility-api

我正在开发一个可以在屏幕上移动第三方应用程序窗口的应用程序。

要概述所有当前打开的窗口,我使用

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返回的辅助功能对象清楚地匹配?

2 个答案:

答案 0 :(得分:23)

我们最终聘请了一位专门的辅助功能开发人员来完成这项任务。

事实证明,如果不使用未记录的API,就无法做到这一点(在我们的案例中不行)。

幸运的是,有一个实际的解决方法:

遍历应用程序的所有打开窗口。获得他们的位置,大小和头衔:

AXUIElementCopyAttributeValue(target, kAXPositionAttribute, CFTypeRef*)&posValue);
AXUIElementCopyAttributeValue(target, kAXSizeAttribute, (CFTypeRef*)&sizeValue);
AXUIElementCopyAttributeValue(target, kAXTitleAttribute, (CFTypeRef*)&titleValue);

接下来,将位置和尺寸转换为实际的CGPointCGSize值:

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%的情况下有效。