Mac / Cocoa - Getting a list of windows using Accessibility API
Asked Answered
Q

5

9

I want to use the Accessibility API to get a list of all windows for a given application (external).

The goal is to check if a certain window is open. First I check that the application is running (using [NSWorkspace runningApplications] and checking each one), and then I want to check the title bar text of each window that is open for that application.

PS

So I can create an element for the app using the PID:

AXUIElementRef app = AXUIElementCreateApplication(pid);

but what do I do with it? Am I even going in the right direction? Can't beleive it's so hard to find examples on this.

Quizmaster answered 21/1, 2010 at 7:51 Comment(0)
D
3

Use AXUIElementCopyAttributeValues to copy the value for kAXWindowsAttribute, which should be an array of AXUIElement objects representing the application's windows.

As you can guess from its function name, it follows the copy rule.

Danner answered 22/1, 2010 at 3:41 Comment(4)
Nice Hint, Peter. But... It didn't work for me and I finally found out that I always had kAXErrorCannotComplete when trying to get AXUIElementCopyAttributeNames (I'm getting the AXUIElementRef like on the original question, would it be the problem?!).Disappointment
@StuFFmc: I suggest asking a separate question and/or filing a bug.Danner
another example of why we don't just drop a link in an answer. what is the copy rule? click the link to not find out.Denture
It was a much more useful link back when I originally put it there, before Apple broke the link. I've updated the link to point to the new location.Danner
P
13

I don't know a way to get window ID and PID from the Accessibility API.
The NSWindow method Laurent mentioned only provides Window IDs but not the PID of the window owning application.
I would use the CGWindowList methods that are available since 10.5.
To get a list of window IDs and the PID of the owner you can try the following:

CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
for (NSMutableDictionary* entry in (NSArray*)windowList) 
{
    NSString* ownerName = [entry objectForKey:(id)kCGWindowOwnerName];
    NSInteger ownerPID = [[entry objectForKey:(id)kCGWindowOwnerPID] integerValue];
    NSLog(@"%@:%d", ownerName, ownerPID);
}
CFRelease(windowList);  

You can control if you want all windows (including offscreen, ...) with the option paramter.
Also the entry objects contain a lot more information. Documentation link

Periwig answered 21/1, 2010 at 12:1 Comment(2)
Does this work in Swift too? Seems that there is no equivalent of kCGNullWindowID in Swift...Apologete
@Apologete the constant exists in it's original form in swift - kCGNullWindowIDFormica
T
4

This works for me in Swift 5.1:

let windowList: CFArray? = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID)

for entry in windowList! as Array {

    let ownerName: String = entry.object(forKey: kCGWindowName) as? String ?? "N/A"
    let ownerPID: Int = entry.object(forKey: kCGWindowOwnerPID) as? Int ?? 0
    print("ownerName: \(ownerName), ownerPID:\(ownerPID)")
}
Trichloride answered 8/5, 2020 at 22:5 Comment(0)
D
3

Use AXUIElementCopyAttributeValues to copy the value for kAXWindowsAttribute, which should be an array of AXUIElement objects representing the application's windows.

As you can guess from its function name, it follows the copy rule.

Danner answered 22/1, 2010 at 3:41 Comment(4)
Nice Hint, Peter. But... It didn't work for me and I finally found out that I always had kAXErrorCannotComplete when trying to get AXUIElementCopyAttributeNames (I'm getting the AXUIElementRef like on the original question, would it be the problem?!).Disappointment
@StuFFmc: I suggest asking a separate question and/or filing a bug.Danner
another example of why we don't just drop a link in an answer. what is the copy rule? click the link to not find out.Denture
It was a much more useful link back when I originally put it there, before Apple broke the link. I've updated the link to point to the new location.Danner
M
0

You can use windowNumbersWithOptions:. It lists all the windows from all the applications by their number. But I can't find how to get a NSWindow from a window number...

Myocardiograph answered 21/1, 2010 at 8:55 Comment(1)
You can't, even in principle, get an NSWindow from a window number, because the premise is that you are doing this from an external application, i.e. another process, and each NSWindow * pointer is in the address space of the process that owns the window. The window number is the index the window server uses and provides so that any application can do things like request screen snapshots.Teletype
C
0

If you get kAXErrorCannotComplete it is likely because you used a newer project where XCode automatically added the App Sandbox or Hardened Runtime capabilities. Delete them from the Signing and Capabilities tab and it should start working.

Chev answered 29/3, 2022 at 1:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.