使用 Cocoa 的 Accessibility API 获取应用程序的停靠图标的位置

发布于 2024-11-19 22:16:14 字数 49 浏览 1 评论 0原文

如何使用 Accessibility API 获取应用程序的 Dock 图标的位置?

How can I get the position of my application's dock icon using the Accessibility API?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

寂寞笑我太脆弱 2024-11-26 22:16:14

找到了!使用此论坛帖子作为参考,我能够塑造给定的示例代码我需要什么:

- (NSArray *)subelementsFromElement:(AXUIElementRef)element forAttribute:(NSString *)attribute
{
    NSArray *subElements = nil;
    CFIndex count = 0;
    AXError result;

    result = AXUIElementGetAttributeValueCount(element, (CFStringRef)attribute, &count);
    if (result != kAXErrorSuccess) return nil;
    result = AXUIElementCopyAttributeValues(element, (CFStringRef)attribute, 0, count, (CFArrayRef *)&subElements);
    if (result != kAXErrorSuccess) return nil;

    return [subElements autorelease];
}

- (AXUIElementRef)appDockIconByName:(NSString *)appName
{
    AXUIElementRef appElement = NULL;

    appElement = AXUIElementCreateApplication([[[NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"] lastObject] processIdentifier]);
    if (appElement != NULL)
    {
        AXUIElementRef firstChild = (__bridge AXUIElementRef)[[self subelementsFromElement:appElement forAttribute:@"AXChildren"] objectAtIndex:0];
        NSArray *children = [self subelementsFromElement:firstChild forAttribute:@"AXChildren"];
        NSEnumerator *e = [children objectEnumerator];
        AXUIElementRef axElement;
        while (axElement = (__bridge AXUIElementRef)[e nextObject])
        {
            CFTypeRef value;
            id titleValue;
            AXError result = AXUIElementCopyAttributeValue(axElement, kAXTitleAttribute, &value);
            if (result == kAXErrorSuccess)
            {
                if (AXValueGetType(value) != kAXValueIllegalType)
                    titleValue = [NSValue valueWithPointer:value];
                else
                    titleValue = (__bridge id)value; // assume toll-free bridging
                if ([titleValue isEqual:appName]) {
                    return axElement;
                }
            }
        }
    }

    return nil;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    AXUIElementRef dockIcon = [self appDockIconByName:@"MYAPPNAME"];
    if (dockIcon) {
        CFTypeRef value;
        CGPoint iconPosition;
        AXError result = AXUIElementCopyAttributeValue(dockIcon, kAXPositionAttribute, &value);
        if (result == kAXErrorSuccess)
        {
            if (AXValueGetValue(value, kAXValueCGPointType, &iconPosition)) {
                NSLog(@"position: (%f, %f)", iconPosition.x, iconPosition.y);
            }
        }
    }
}

Found it! Using this forum post as reference, I was able to shape the given sample code to what I needed:

- (NSArray *)subelementsFromElement:(AXUIElementRef)element forAttribute:(NSString *)attribute
{
    NSArray *subElements = nil;
    CFIndex count = 0;
    AXError result;

    result = AXUIElementGetAttributeValueCount(element, (CFStringRef)attribute, &count);
    if (result != kAXErrorSuccess) return nil;
    result = AXUIElementCopyAttributeValues(element, (CFStringRef)attribute, 0, count, (CFArrayRef *)&subElements);
    if (result != kAXErrorSuccess) return nil;

    return [subElements autorelease];
}

- (AXUIElementRef)appDockIconByName:(NSString *)appName
{
    AXUIElementRef appElement = NULL;

    appElement = AXUIElementCreateApplication([[[NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"] lastObject] processIdentifier]);
    if (appElement != NULL)
    {
        AXUIElementRef firstChild = (__bridge AXUIElementRef)[[self subelementsFromElement:appElement forAttribute:@"AXChildren"] objectAtIndex:0];
        NSArray *children = [self subelementsFromElement:firstChild forAttribute:@"AXChildren"];
        NSEnumerator *e = [children objectEnumerator];
        AXUIElementRef axElement;
        while (axElement = (__bridge AXUIElementRef)[e nextObject])
        {
            CFTypeRef value;
            id titleValue;
            AXError result = AXUIElementCopyAttributeValue(axElement, kAXTitleAttribute, &value);
            if (result == kAXErrorSuccess)
            {
                if (AXValueGetType(value) != kAXValueIllegalType)
                    titleValue = [NSValue valueWithPointer:value];
                else
                    titleValue = (__bridge id)value; // assume toll-free bridging
                if ([titleValue isEqual:appName]) {
                    return axElement;
                }
            }
        }
    }

    return nil;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    AXUIElementRef dockIcon = [self appDockIconByName:@"MYAPPNAME"];
    if (dockIcon) {
        CFTypeRef value;
        CGPoint iconPosition;
        AXError result = AXUIElementCopyAttributeValue(dockIcon, kAXPositionAttribute, &value);
        if (result == kAXErrorSuccess)
        {
            if (AXValueGetValue(value, kAXValueCGPointType, &iconPosition)) {
                NSLog(@"position: (%f, %f)", iconPosition.x, iconPosition.y);
            }
        }
    }
}
深海不蓝 2024-11-26 22:16:14

至于 Mac OS El Capitan,看起来您不应该使用 Accessibility API 获取图标的位置。问题是图标并不位于应用程序的辅助功能对象层次结构中,它可以在系统 Dock 应用程序的层次结构中找到。沙盒应用程序不应访问其他应用程序的辅助功能对象。

已批准答案中的代码不会在控制台中产生任何 sandboxd 守护进程的警告,看起来它没有违反任何规则。它使用函数 AXUIElementCreateApplication 创建顶级辅助功能对象。文档指出:

Creates and returns the top-level accessibility object for the
application with the specified process ID.

不幸的是,这个顶级对象不是 Dock 图标的祖先。
我尝试运行代码,它计算第一个应用程序主菜单项的位置(其标题与应用程序本身相同)。比较发生在这一行:

if ([titleValue isEqual:appName]) {

因此,我的应用程序的输出始终相同:

position: (45.000000, 0.000000)

尝试访问其他应用程序的辅助功能对象在控制台中产生了警告。我想必须找到另一种计算图标位置的方法。

As for Mac OS El Capitan, looks like you aren't supposed to get the position of the icon using Accessibility API. The matter is that the icon isn't located in accessibility objects hierarchy of the app—it can be found in the hierarchy of the system Dock application. A sandboxed app isn't supposed to access the accessibility objects of other apps.

The code in approved answer doesn't yield any warnings of the sandboxd daemon in the console, looks like it doesn't violate any rules. It creates the top-level accessibility object with the function AXUIElementCreateApplication. The documentation states, that it:

Creates and returns the top-level accessibility object for the
application with the specified process ID.

Unfortunately, this top-level object is not the ancestor of the Dock icon.
I've tried to run the code, and it calculates the position of the first app's main menu item (which has the same title as the app itself). The comparison takes place in this line:

if ([titleValue isEqual:appName]) {

So the output was always the same for my app:

position: (45.000000, 0.000000)

An attempt to access the other app's accessibility object yielded a warning in console. I guess another way to calculate the position of the icon has to be found.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文