26

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

为了概览所有当前打开的窗口,我使用

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;
},

该字典充满了其他地方使用的良好信息,但缺少可用于修改窗口位置的可访问性对象。窗口由窗口编号清楚地标识。

我现在使用 PID (kCGWindowOwnerPID) 为窗口的应用程序创建一个可访问性对象:

AXUIElementRef app = AXUIElementCreateApplication(pid);

然后使用 AXUIElementCopyAttributeValues 检索应用程序已打开的所有窗口的列表:

NSArray *result;

AXUIElementCopyAttributeValues(
                               (AXUIElementRef) app, 
                               kAXWindowsAttribute,
                               0,
                               99999,
                               (CFArrayRef *) &result
                               );

这有效并返回一个 AXUIElements 数组。这就是我卡住的地方。似乎没有 API 调用来检索可访问性对象的窗口编号。有没有办法

a)找到可访问性对象的窗口编号(最终遍历数组并找到正确的窗口)

或者

b) 否则,将 CGWindowListCopyWindowInfo 返回的数组中描述的窗口与 AXUIElementCopyAttributeValues 返回的辅助功能对象明确匹配?

4

2 回答 2

28

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

事实证明,如果不使用未记录的 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 个窗口具有相同的位置、大小和标题),但到目前为止,我们在实际使用中还没有遇到任何发生这种情况的情况。

于 2011-06-15T22:19:10.397 回答
12

有一个私有函数用于为 window: 的给定 AX 对象获取 CG 窗口编号_AXUIElementGetWindow。SO 讨论中的更多详细信息唯一标识 OS X 上的活动窗口 看起来没有公共 API 可以 100% 的概率完成任务。按标题和框架识别窗口(如上面的答案所述)将适用于 99.9% 的情况。

于 2013-08-19T17:15:12.070 回答