【问题标题】:Uniquely identify active window on OS X唯一标识 OS X 上的活动窗口
【发布时间】:2011-09-14 20:36:46
【问题描述】:

我正在尝试修补 an application 以使用辅助功能 API 调整窗口大小。

我需要维护一个包含以前窗口大小的字典。密钥需要识别当前活动的窗口。目前,按下热键后通过NSAccessibilityFocusedWindowAttribute 检索此活动窗口。

但是,每次调用该方法,返回的标识窗口的AXUIElementRef都不一样!这当然意味着我不能将它用作字典键 - 字典不会找到相应的条目。

下面的代码重现了这个问题:

-(IBAction)testWindowIdentification:(id)sender{
    AXUIElementRef focusedApp;
    AXUIElementRef focusedWindow;

    AXUIElementCopyAttributeValue(_systemWideElement,
                                  (CFStringRef) kAXFocusedApplicationAttribute,
                                  (CFTypeRef*) &focusedApp);
    AXUIElementCopyAttributeValue((AXUIElementRef) focusedApp,
                                  (CFStringRef) NSAccessibilityFocusedWindowAttribute,
                                  (CFTypeRef*) &focusedWindow);
    CFShow(focusedWindow);
}

_systemWideElement 已在 init 方法中使用对 AXUIElementCreateSystemWide() 的调用进行初始化。

CFShow 语句在每次调用方法时清楚地显示不同的 ID(即使同一个窗口处于活动状态),这对我来说没用:

<AXUIElement 0x47e850> {pid=42463}
<AXUIElement 0x47e890> {pid=42463}
<AXUIElement 0x47e2c0> {pid=42463}
…

documentation on AXUIElement 没有显示检索 UI 元素唯一属性的方法,that of the NSAccessibility protocol 也没有。 唯一的 PID 对我来说不够,因为一个进程可以有多个窗口。

如何在 Cocoa 中检索活动窗口的一些唯一标识符?

(顺便说一下,真正的代码是检查上述调用中的返回码;没有错误,调用成功。)

【问题讨论】:

  • @JeremyBanks 最初的回答者在这里有正确的想法。一旦你确定了焦点窗口,你确实可以使用 Quartz 来获得一个CGWindowID,如果this answer 是可信的。这应该为您提供您希望的唯一窗口标识符,您可以在当前应用程序的上下文中不受惩罚地传递。如果您想要一个更连贯和完整的版本作为实际答案,请告诉我。
  • @MrGomez 当然,这样的答案会很棒。 :)
  • @JeremyBanks 会的。我今天有点超负荷,但我会在今晚晚些时候(太平洋标准时间)的某个时间尝试回答这个问题。 :)
  • @MrGomez 当然,不要着急,除非你担心其他人可能会抓住它。 :)
  • @JeremyBanks 赏金期已经结束,你想现在奖励它吗?否则它将在 22 小时内过期,并且将浪费一半的声誉。

标签: objective-c macos cocoa


【解决方案1】:

Rob Keniger 对his answer here 的策略是正确的。这个答案中唯一缺少的东西(实际上,赏金放置的原因)是一个可行的实现,它采用当前活动窗口并将其转换为适合在当前工作应用程序上下文中索引的唯一键。

Rob 的解决方案通过使用在 Quartz Window Services 上下文中给出的CGWindowID 来勾勒出这一点。当然,强烈暗示这个窗口引用是only useful for your current application

获取此窗口引用很棘手,因为 Accessibility API 和 Quartz Window Services 之间不存在强有力的保证。但是,您可以通过以下方式解决此问题:

  1. 使用extern "C" AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);,作为documented here。这保证可以工作,但如果它在您的 OSX 版本中工作,它可以作为一个底层测试来开始工作。

  2. 直接获取CGWindowID,例如使用HIWindowGetCGWindowID()。有关选择活动窗口和提取 ID 的更多详细信息,请参阅the reference manual for the Carbon Window Manager(警告:PDF 较大)。

  3. 完全按照 Rob 的建议,使用 CGWindowListCreateDescriptionFromArray 之类的内容对您的 CGWindowID 集进行编目。这里的目标是找到一些桥接 Accessibility API 和 Quartz 的方案,但这可以通过使用例如绑定到当前活动窗口的上下文的回调来实现。但是,老实说,我不知道这个可以很好地适应未来的最佳示例。

在这些选项中,如果您无法为您的窗户创建其他装饰器以唯一标识它们,我建议您使用 2. 来满足您当前的需求。它目前在遗留代码库中定义,但它会做你想做的事。

祝你的申请好运。

【讨论】:

  • 如何检测没有选择窗口或所有窗口都处于非活动状态(即控制在桌面上)?
  • 请问如何使用 "extern "C" AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);"在斯威夫特?
【解决方案2】:

我认为您可以使用 Quartz Window Services 函数,特别是 CGWindowListCreateDescriptionFromArray 来枚举特定应用程序中当前活动的窗口。

此调用比 AppKit 级别低,不会告诉您哪个是活动窗口,但它会为您提供当前用户会话唯一的窗口 ID。这不是一个很好的解决方案,但您可以将窗口边界信息与您从辅助功能 API 收到的信息进行比较,以将窗口与其真实 ID 关联起来。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    • 2019-04-10
    • 2015-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多