Macos 如何从AXMenu获取AXMenuItems数组?
对于我的代码,我试图从Macos 如何从AXMenu获取AXMenuItems数组?,macos,cocoa,menu,parent-child,accessibility-api,Macos,Cocoa,Menu,Parent Child,Accessibility Api,对于我的代码,我试图从AXMenu(AXUIElementRef)中获取AXMenuItems数组。菜单成功登录,下面是我的代码: NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]; AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0
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;
}
}
NSArray*anArray=[NSRunningApplication runningApplicationsWithBundleIdentifier:@“com.apple.dock”];
AXUIElementRef anAXDockApp=AXUIElementCreateApplication([[anArray objectAtIndex:0]processIdentifier]);
CFTypeRef-aChildren;
AXUIElementCopyAttributeValue(anAXDockApp、kAXChildrenAttribute和aChildren);
安全释放(anAXDockApp);
CFTypeRef-aMenu=CFArrayGetValueAtIndex(aChildren,0);
NSLog(@“阿门努:%”,阿门努);
//获取菜单项
CFTypeRef修正案;
AxUIElement CopyAttributeValue(阿门努、Kaxviible ChildrenAttribute和阿门儿童);
对于(NSInteger i=0;i
获取AXMenuItems
列表的正确方法是什么
可访问性检查器的屏幕截图:
如何从AXMenu获取AXMENUITEM数组?”问题的答案:aMenuChildren
是菜单项列表。确保您可以按角色kAXMenuItemRole
进行筛选
“为什么它不工作?”问题的答案:菜单不是菜单。当您在AXAccessibility Inspector中检查菜单时,它会显示一条警告“父元素不将元素报告为其子元素之一”。Dock应用程序有一个子项,即Dock项列表。我已经找到了一个答案,使用
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;
}
这个方法[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;
}
我将所有这些代码放在我的一个方法中:
//检查是否在船坞内(否则无法进行)
if([self-isAppOfNameInDock:[appDict-objectForKey:@“AppName”]])){
//获取码头项目
AXUIElementRef aDockItem=[self-getDockItemWithName:[appDict objectForKey:@“AppName”];
AxUIElement性能(aDockItem、kAXShowMenuAction);
[NSThread sleepForTimeInterval:0.5];
CGRect-aRect;
CFTypeRef定位;
AXUIElementCopyAttributeValue(aDockItem、kAXPositionAttribute和&aPosition);
AXValueGetValue(位置、kAXValueCGPointType和aRect.origin);
安全释放(aPosition);
CFTypeRef aSize;
AXUIElementCopyAttributeValue(aDockItem、kAXSizeAttribute和aSize);
AXValueGetValue(aSize、kAXValueCGSizeType和aRect.size);
安全释放(aSize);
安全放行(aDockItem);
CGPoint-aMenuPoint;
if([self-dockHidden]==kDockAutohideOff){
开关([自停靠位置]){
案例kDockPositionRight:
修正点=CGPointMake(aRect.origin.x-18,aRect.origin.y+(aRect.size.height/2));
打破
案例kDockPositionLeft:
修正点=CGPointMake(aRect.origin.x+aRect.size.width+18,aRect.origin.y+(aRect.size.height/2));
打破
案例kDockPositionBottom:
修正点=CGPointMake(aRect.origin.x+(aRect.size.width/2),aRect.origin.y-18);
打破
案例kDockPositionUnknown:
aMenuPoint=CGPointMake(0,0);
打破
}
}否则{
NSRect screen frame=[[NSScreen main screen]frame];
开关([自停靠位置]){
案例kDockPositionRight:
aMenuPoint=CGPointMake(screensframe.size.width-18,aRect.origin.y+(aRect.size.height/2));
打破
案例kDockPositionLeft:
aMenuPoint=CGPointMake(screensframe.origin.x+18,aRect.origin.y+(aRect.size.height/2));
打破
案例kDockPositionBottom:
amenuppoint=CGPointMake(aRect.origin.x+(aRect.size.width/2),screensframe.size.height-18);
打破
案例kDockPositionUnknown:
aMenuPoint=CGPointMake(0,0);
打破
}
}
如果((aMenuPoint.x!=0)和&(aMenuPoint.y!=0)){
AXUIElementRef_systemWideElement=AXUIElementCreateSystemWide();
AXUIElementRef-aMenu;
AxUIElement CopyElement位置(_systemWideElement、AmenUpPoint.x、AmenUpPoint.y和aMenu);
安全地带
void SafeCFRelease( CFTypeRef cf )
{
if (cf) CFRelease(cf);
}
- (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;
}
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);
}
}