简体   繁体   English

如何从 NSView 或 NSWindow 创建 AXUIElementRef?

[英]How to create an AXUIElementRef from an NSView or NSWindow?

Concerning the macOS Accessibility API, is there anyway to create an AXUIElementRef that corresponds to either an NSView or an NSWindow?关于 macOS 辅助功能 API,无论如何创建一个对应于 NSView 或 NSWindow 的 AXUIElementRef?

There appears to have been a way of doing this back in the days of Carbon using AXUIElementCreateWithHIObjectAndIdentifier but that function isn't available anymore.在使用 AXUIElementCreateWithHIObjectAndIdentifier 的 Carbon 时代,似乎有一种方法可以做到这一点,但该功能不再可用。

The only method I'm aware of is to use the Accessibility API to recursively search the entire hierarchy of UI elements of your application looking for one that matches the NSView or NSWindow.我知道的唯一方法是使用 Accessibility API 递归搜索应用程序的整个 UI 元素层次结构,寻找与 NSView 或 NSWindow 匹配的元素。 But in addition to being an onerous solution, it's not even guaranteed to succeed as there might not be a way to positively correspond an AXUIElementRef and a Cocoa object by just using the available attributes of the AXUIElementRef.但是除了是一个繁重的解决方案之外,它甚至不能保证成功,因为可能没有一种方法可以通过使用 AXUIElementRef 的可用属性来积极对应 AXUIElementRef 和 Cocoa 对象。

I am willing to consider undocumented APIs that help accomplish this.我愿意考虑帮助实现这一目标的未记录的 API。

I found the way to do the same thing in iOS.我找到了在 iOS 中做同样事情的方法。

I know this isn't a direct answer to your question, but I'll try to explain what I have done to find it in iOS, and hopefully you'll be able to do the same in macOS.我知道这不是对您问题的直接回答,但我会尝试解释我在 iOS 中找到它所做的工作,希望您能够在 macOS 中做同样的事情。 Also, this might be useful for other readers...此外,这可能对其他读者有用......

I started by guessing that the process itself is creating the AXUIElementRef , so it has to create them when I request accessibility attributes that have AXUIElementRef values, such as kAXUIElementAttributeChildren .我首先猜测流程本身正在创建AXUIElementRef ,因此当我请求具有AXUIElementRef值的可访问性属性(例如kAXUIElementAttributeChildren时,它必须创建它们。

I then created an app, and dlsym'ed _AXUIElementCreateAppElementWithPid(int pid) , calling it with [[NSProcessInfo processInfo] processIdentifier] .然后我创建了一个应用程序,并 dlsym'ed _AXUIElementCreateAppElementWithPid(int pid) ,用[[NSProcessInfo processInfo] processIdentifier]调用它。 I received the root AXUIElementRef , which I then passed into AXError AXUIElementCopyMultipleAttributeValues(AXUIElementRef element, CFArrayRef attributes, AXCopyMultipleAttributeOptions options, CFArrayRef _Nullable *values) , requesting kAXUIElementAttributeChildren , and it worked (should be run on main thread)!我收到了根AXUIElementRef ,然后我将其传递给AXError AXUIElementCopyMultipleAttributeValues(AXUIElementRef element, CFArrayRef attributes, AXCopyMultipleAttributeOptions options, CFArrayRef _Nullable *values) ,请求kAXUIElementAttributeChildren ,它工作(应该在主线程上运行)!

I started debugging the AXUIElementCopyMultipleAttributeValues call carefully into the assembly code, which went pretty much like that (this is very pseudo-code, off course...):我开始在汇编代码AXUIElementCopyMultipleAttributeValues心地调试AXUIElementCopyMultipleAttributeValues调用,它非常像这样(这是非常伪代码,当然......):

// serialize the arguments for MIG call
if (axUIElementRef->pid == self pid) {
    // that's good that we are calling our own process, we can easily keep debugging!
    _MIGXAAXUIElementCopyMultipleAttributeValues(serialized arguments) {
         // Get the original element from the AXUIElementRef:
         UIView* view = _AXElementForAXUIElementUniqueId(id);
         [view accessibilityAttributeValue:kAXUIElementAttributeChildren] {
              [view _accessibilityUserTestingChildren] {
                   // since this is the UIApplication element, it just gets the windows:
                   NSArray*<UIWindow*> winArr = [(UIApplication*)view _accessibilityWindows];
                   // for each value in result, convert to AX value:
                   ...
                   AXConvertOutgoingValue(winArr) {
                       // For each UIView, convert to AXUIElementRef:
                       AXUIElementRef e = _AXCreateAXUIElementWithElement(winArr[i]);
                   }
              }
         }
    }
} else {
    // Do the same only if we are entitled, and outside our process
}

So, in iOS, you simply call AXUIElementRef _AXCreateAXUIElementWithElement(UIView*);因此,在 iOS 中,您只需调用AXUIElementRef _AXCreateAXUIElementWithElement(UIView*); to convert from UI to accessibility element, and UIView* _AXElementForAXUIElementUniqueId(AXUIElementRefGetUniqueID(AXUIElementRef));从 UI 转换为辅助功能元素,以及UIView* _AXElementForAXUIElementUniqueId(AXUIElementRefGetUniqueID(AXUIElementRef)); in the opposite direction.在相反的方向。

All symbols are from AXRuntime.framework .所有符号都来自AXRuntime.framework

In mac, you'll need to link against ApplicationServices.framework and try something similar.在 mac 中,您需要链接ApplicationServices.framework并尝试类似的操作。

Hope this helps...希望这可以帮助...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM