gpt4 book ai didi

macos - 如何从 AXMenu 获取 AXMenuItems 数组?

转载 作者:行者123 更新时间:2023-12-03 16:09:57 47 4
gpt4 key购买 nike

对于我的代码,我尝试从 AXMenu (AXUIElementRef) 获取 AXMenuItems 数组。菜单记录成功,这是我的代码:

NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"];
AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0] processIdentifier]);

CFTypeRef aChildren;
AXUIElementCopyAttributeValue(anAXDockApp, kAXChildrenAttribute, &aChildren);

SafeCFRelease(anAXDockApp);

CFTypeRef aMenu = CFArrayGetValueAtIndex(aChildren, 0);

NSLog(@"aMenu: %@", aMenu);

// Get menu items
CFTypeRef aMenuChildren;
AXUIElementCopyAttributeValue(aMenu, kAXVisibleChildrenAttribute, &aMenuChildren);

for (NSInteger i = 0; i < CFArrayGetCount(aMenuChildren); i++) {

AXUIElementRef aMenuItem = [self copyAXUIElementFrom:aMenu role:kAXMenuItemRole atIndex:i];

NSLog(@"aMenuItem: %@", aMenuItem); // logs (null)

CFTypeRef aTitle;
AXUIElementCopyAttributeValue(aMenuItem, kAXTitleAttribute, &aTitle);


if ([(__bridge NSString *)aTitle isEqualToString:@"New Window"] || [(__bridge NSString *)aTitle isEqualToString:@"New Finder Window"]) /* Crashes here (i can see why)*/{

AXUIElementPerformAction(aMenuItem, kAXPressAction);

[NSThread sleepForTimeInterval:1];

break;

}

}

获取 AXMenuItems 列表的正确方法是什么?

辅助功能检查器的屏幕截图:

inspector

最佳答案

我已经找到了一个答案,使用 @Willeke 使用 AXUIElementCopyElementAtPosition() 获取菜单的答案。由于有多个停靠方向和隐藏,我必须在 .h 文件中创建枚举,因为它比 0、1 或 2 更容易读取。

// .h
typedef enum {
kDockPositionBottom,
kDockPositionLeft,
kDockPositionRight,
kDockPositionUnknown
} DockPosition;

typedef enum {
kDockAutohideOn,
kDockAutohideOff
} DockAutoHideState;

然后,我在 .m 中添加了获取这些状态的方法

// .m
- (DockPosition)dockPosition
{
NSRect screenRect = [[NSScreen mainScreen] frame];

NSRect visibleRect = [[NSScreen mainScreen] visibleFrame];

// Dont need to remove menubar height
visibleRect.origin.y = 0;

if (visibleRect.origin.x > screenRect.origin.x) {
return kDockPositionLeft;
} else if (visibleRect.size.width < screenRect.size.width) {
return kDockPositionRight;
} else if (visibleRect.size.height < screenRect.size.height) {
return kDockPositionBottom;
}
return kDockPositionUnknown;
}

- (DockAutoHideState)dockHidden
{
NSString *plistPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences/com.apple.dock.plist"];
NSDictionary *dockDict = [NSDictionary dictionaryWithContentsOfFile:plistPath];

CFBooleanRef autohide = CFDictionaryGetValue((__bridge CFDictionaryRef)dockDict, @"autohide");
if (CFBooleanGetValue(autohide) == true) {
return kDockAutohideOn;
}
return kDockAutohideOff;
}

对于未知的停靠位置,我添加了以防无法从屏幕位置计算它。

然后,我使用了一种方法从菜单栏中获取停靠项:

- (AXUIElementRef)getDockItemWithName:(NSString *)name
{
NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"];
AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0] processIdentifier]);

AXUIElementRef aList = [self copyAXUIElementFrom:anAXDockApp role:kAXListRole atIndex:0];

CFTypeRef aChildren;
AXUIElementCopyAttributeValue(aList, kAXChildrenAttribute, &aChildren);
NSInteger itemIndex = -1;

for (NSInteger i = 0; i < CFArrayGetCount(aChildren); i++) {

AXUIElementRef anElement = CFArrayGetValueAtIndex(aChildren, i);

CFTypeRef aResult;

AXUIElementCopyAttributeValue(anElement, kAXTitleAttribute, &aResult);

if ([(__bridge NSString *)aResult isEqualToString:name]) {

itemIndex = i;
}
}
SafeCFRelease(aChildren);

if (itemIndex == -1) return nil;

// We have index now do something with it

AXUIElementRef aReturnItem = [self copyAXUIElementFrom:aList role:kAXDockItemRole atIndex:itemIndex];
SafeCFRelease(aList);
return aReturnItem;
}

这个SafeCFRelease()方法是一个非常简单的方法,它检查传递的值是否不为零,然后释放(之前有一些问题)。

void SafeCFRelease( CFTypeRef cf )
{
if (cf) CFRelease(cf);
}

这个方法[copyAXUIElementFrom: role: atIndex:]@Willeke回答我的另一个问题的一种方法:

- (AXUIElementRef)copyAXUIElementFrom:(AXUIElementRef)theContainer role:(CFStringRef)theRole atIndex:(NSInteger)theIndex {
AXUIElementRef aResultElement = NULL;
CFTypeRef aChildren;
AXError anAXError = AXUIElementCopyAttributeValue(theContainer, kAXChildrenAttribute, &aChildren);
if (anAXError == kAXErrorSuccess) {
NSUInteger anIndex = -1;
for (id anElement in (__bridge NSArray *)aChildren) {
if (theRole) {
CFTypeRef aRole;
anAXError = AXUIElementCopyAttributeValue((__bridge AXUIElementRef)anElement, kAXRoleAttribute, &aRole);
if (anAXError == kAXErrorSuccess) {
if (CFStringCompare(aRole, theRole, 0) == kCFCompareEqualTo)
anIndex++;
SafeCFRelease(aRole);
}
}
else
anIndex++;
if (anIndex == theIndex) {
aResultElement = (AXUIElementRef)CFRetain((__bridge CFTypeRef)(anElement));
break;
}
}
SafeCFRelease(aChildren);
}
return aResultElement;
}

将所有这些代码放入我的方法之一中:

//检查是否在dock中(否则无法执行)

                if ([self isAppOfNameInDock:[appDict objectForKey:@"AppName"]]) {

// Get dock item

AXUIElementRef aDockItem = [self getDockItemWithName:[appDict objectForKey:@"AppName"]];

AXUIElementPerformAction(aDockItem, kAXShowMenuAction);

[NSThread sleepForTimeInterval:0.5];

CGRect aRect;

CFTypeRef aPosition;
AXUIElementCopyAttributeValue(aDockItem, kAXPositionAttribute, &aPosition);
AXValueGetValue(aPosition, kAXValueCGPointType, &aRect.origin);
SafeCFRelease(aPosition);

CFTypeRef aSize;
AXUIElementCopyAttributeValue(aDockItem, kAXSizeAttribute, &aSize);
AXValueGetValue(aSize, kAXValueCGSizeType, &aRect.size);
SafeCFRelease(aSize);

SafeCFRelease(aDockItem);

CGPoint aMenuPoint;

if ([self dockHidden] == kDockAutohideOff) {
switch ([self dockPosition]) {
case kDockPositionRight:
aMenuPoint = CGPointMake(aRect.origin.x - 18, aRect.origin.y + (aRect.size.height / 2));
break;
case kDockPositionLeft:
aMenuPoint = CGPointMake(aRect.origin.x + aRect.size.width + 18, aRect.origin.y + (aRect.size.height / 2));
break;
case kDockPositionBottom:
aMenuPoint = CGPointMake(aRect.origin.x + (aRect.size.width / 2), aRect.origin.y - 18);
break;
case kDockPositionUnknown:
aMenuPoint = CGPointMake(0, 0);
break;
}
} else {

NSRect screenFrame = [[NSScreen mainScreen] frame];

switch ([self dockPosition]) {
case kDockPositionRight:
aMenuPoint = CGPointMake(screenFrame.size.width - 18, aRect.origin.y + (aRect.size.height / 2));
break;
case kDockPositionLeft:
aMenuPoint = CGPointMake(screenFrame.origin.x + 18, aRect.origin.y + (aRect.size.height / 2));
break;
case kDockPositionBottom:
aMenuPoint = CGPointMake(aRect.origin.x + (aRect.size.width / 2), screenFrame.size.height - 18);
break;
case kDockPositionUnknown:
aMenuPoint = CGPointMake(0, 0);
break;
}
}

if ((aMenuPoint.x != 0) && (aMenuPoint.y != 0)) {

AXUIElementRef _systemWideElement = AXUIElementCreateSystemWide();

AXUIElementRef aMenu;
AXUIElementCopyElementAtPosition(_systemWideElement, aMenuPoint.x, aMenuPoint.y, &aMenu);

SafeCFRelease(_systemWideElement);

// Get menu items
CFTypeRef aMenuChildren;
AXUIElementCopyAttributeValue(aMenu, kAXVisibleChildrenAttribute, &aMenuChildren);

NSRunningApplication *app = [[NSRunningApplication runningApplicationsWithBundleIdentifier:[appDict objectForKey:@"BundleID"]] objectAtIndex:0];

for (NSInteger i = 0; i < CFArrayGetCount(aMenuChildren); i++) {

AXUIElementRef aMenuItem = [self copyAXUIElementFrom:aMenu role:kAXMenuItemRole atIndex:i];

CFTypeRef aTitle;
AXUIElementCopyAttributeValue(aMenuItem, kAXTitleAttribute, &aTitle);

// Supports chrome, safari, and finder
if ([(__bridge NSString *)aTitle isEqualToString:@"New Window"] || [(__bridge NSString *)aTitle isEqualToString:@"New Finder Window"]) {

AXUIElementPerformAction(aMenuItem, kAXPressAction);

NSInteger numberOfWindows = [self numberOfWindowsOpenFromApplicationWithPID:[app processIdentifier]];
// Wait until open
while ([self numberOfWindowsOpenFromApplicationWithPID:[app processIdentifier]] <= numberOfWindows) {
}
break;
}
}
SafeCFRelease(aMenu);
SafeCFRelease(aMenuChildren);
}
}

这相当复杂,但很有效。我可能无法解释它,但我已经对这段代码进行了压力测试,它运行得很好。

关于macos - 如何从 AXMenu 获取 AXMenuItems 数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35821506/

47 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com