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

Kon*_*lph 28 macos cocoa objective-c

我正在尝试修补使用辅助功能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);
}
Run Code Online (Sandbox Code Playgroud)

_systemWideElementinit使用调用方法在方法中初始化AXUIElementCreateSystemWide().

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

<AXUIElement 0x47e850> {pid=42463}
<AXUIElement 0x47e890> {pid=42463}
<AXUIElement 0x47e2c0> {pid=42463}
…
Run Code Online (Sandbox Code Playgroud)

关于文件AXUIElement显示没有检索用于UI元素的独特的属性,而且也不方法,所述的NSAccessibility协议.独特的PID是不是足以让我,因为一个进程可以有多个窗口.

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

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

MrG*_*mez 17

Rob Keniger在这里有正确的策略和答案.这个答案中唯一缺少的(实际上是赏金放置的原因)是一个可行的实现,它接受当前活动窗口并将其转换为适合在当前工作应用程序的上下文中进行索引的唯一键.

Rob的解决方案通过CGWindowID在Quartz Window Services的上下文中使用给定的草图来概述这一点.当然,强烈暗示此窗口参考仅对您当前的应用程序有用.

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

  1. 使用extern "C" AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);,如此处所述.这不保证能够正常工作,但如果它适用于您的OSX版本,它可以作为底层测试来启动.

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

  3. CGWindowID使用像CGWindowListCreateDescriptionFromArrayRob建议的那样使用类似的目录编目.这里的目标是找到一些桥接Accessibility API和Quartz的方案,但这可以通过利用绑定到当前活动窗口的上下文的回调来实现.老实说,我不知道这个适当的未来证明的最佳例子.

在这些选项中,2.如果您无法为Windows创建其他装饰器来唯一标识它们,我建议您根据当前需要进行操作.它目前在遗留代码库中定义,但它可以满足您的需求.

祝你的应用好运.


Rob*_*ger 10

我想你可以使用Quartz Window Services函数,特别CGWindowListCreateDescriptionFromArray是枚举特定应用程序中当前活动的窗口.

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