在OS X上唯一标识活动窗口

时间:2011-09-14 20:36:47

标签: objective-c macos cocoa

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

我需要维护一个包含以前窗口大小的字典。密钥需要标识当前活动的窗口。此时,按下热键后,通过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中检索活动窗口的某些唯一标识符?

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

2 个答案:

答案 0 :(得分:17)

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. 使用类似CGWindowListCreateDescriptionFromArray之类的内容对您的CGWindowID集进行编目,与Rob建议完全一样。这里的目标是找到一些桥接Accessibility API和Quartz的方案,但这可以通过利用绑定到当前活动窗口的上下文的回调来实现。老实说,我不知道这个的最佳例子是否适合未来的

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

    祝你的申请好运。

答案 1 :(得分:9)

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

此调用低于AppKit,并且不会告诉您哪个是活动窗口,但它将为您提供当前用户会话唯一的窗口ID。这不是一个很好的解决方案,但您可以将窗口边界信息与从可访问性API接收的内容进行比较,以将窗口与其真实ID相关联。