HippoMan
HippoMan

Reputation: 155

Close all windows of another app using Accessibility API

I already know how to use the Mac OSX Accessibility API within Objective-C to reposition windows of another running application, without the use of any kind of scripting bridge.

Now, I want to use this same Accessibility API (again, without any scripting bridge) to close all the open windows of another running application.

The code that I want to write in Objective-C should do the same thing as this AppleScript code:

tell application "TheApplication"
close every window
end tell

I would guess that this is possible, because it's permitted within AppleScript.

Upvotes: 3

Views: 2682

Answers (2)

HippoMan
HippoMan

Reputation: 155

Here's my solution ...

+(void)closeWindowsOfApp:(NSString*)appName {

    boolean_t result = false;

    if (appName == nil) {
        return;
    }

    ProcessSerialNumber psn;
    psn.highLongOfPSN = 0;
    psn.lowLongOfPSN  = kNoProcess;

    while (GetNextProcess(&psn) == noErr) {

        pid_t pid = 0;

        if (GetProcessPID(&psn, &pid) != noErr) {
            continue;
        }

        AXUIElementRef elementRef = AXUIElementCreateApplication(pid);
        NSString* title = nil;
        AXUIElementCopyAttributeValue(elementRef, kAXTitleAttribute, (CFTypeRef *)&title);
        if (title == nil) {
            continue;
        }
        if ([title compare:appName] != NSOrderedSame) {
            CFRelease(title);
            continue;      
        }
        CFRelease(title);

        CFArrayRef windowArray = nil;
        AXUIElementCopyAttributeValue(elementRef, kAXWindowsAttribute, (CFTypeRef*)&windowArray);
        if (windowArray == nil) {
            CFRelease(elementRef);
            continue;
        }
        CFRelease(elementRef);

        CFIndex nItems = CFArrayGetCount(windowArray);
        if (nItems < 1) {
            CFRelease(windowArray);
            continue;
        }

        for (int i = 0; i < nItems; i++) {
            AXUIElementRef itemRef = (AXUIElementRef) CFArrayGetValueAtIndex(windowArray, i);
            AXUIElementRef buttonRef = nil;
            AXUIElementCopyAttributeValue(itemRef, kAXCloseButtonAttribute, (CFTypeRef*)&buttonRef);
            AXUIElementPerformAction(buttonRef, kAXPressAction);
            CFRelease(buttonRef);
        }

        CFRelease(windowArray);
        break;
    }
}

Upvotes: 7

jscs
jscs

Reputation: 64012

There's a Cocoa class, NSApplescript, that lets you compile and run Applescript from within your ObjC code. You haven't really said why you don't want to use AS. Since you've already got the script that does what you want, you can make your program work right now and just use it:

NSApplescript * as = [[NSApplescript alloc] initWithSource:@"tell application \"TheApplication\"\nclose every window\nend tell"];
NSDictionary * errInfo;
NSAppleEventDescriptor * res = [as executeAndReturnError:&err];
if( !res ){
    // An error occurred. Inspect errInfo and perform necessary actions
}

[as release];

Worry about ideological purity or performance later.

Upvotes: 2

Related Questions