How do I get a list of the window titles on the Mac OSX?

前端 未结 2 1272
半阙折子戏
半阙折子戏 2020-12-23 18:02

I want to get the list of window titles of the currently running applications.

On windows I have EnumWndProc and GetWindowText.

On Linux I have XGetWindowPro

相关标签:
2条回答
  • 2020-12-23 18:18

    A few potentially useful references:

    • NSWindowList()
    • NSWorkspace -launchedApplications and +runningApplications
    • CGWindowListCreate() and CGWindowListCopyWindowInfo() (requires 10.5)
    • CGSGetWindowProperty()

    CGSGetWindowProperty is not officially documented, but I believe you can use it with the an item of NSWindowList() as follows (completely untested):

    OSErr err;
    CGSValue titleValue;
    char *title;
    CGSConnection connection = _CGSDefaultConnection();
    int windowCount, *windows, i;
    
    NSCountWindows(&windowCount);
    windows = malloc(windowCount * sizeof(*windows));
    if (windows) {
        NSWindowList(windowCount, windows);
        for (i=0; i < windowCount; ++i) {
            err = CGSGetWindowProperty(connection, windows[i], 
                        CGSCreateCStringNoCopy("kCGSWindowTitle"), 
                        &titleValue);
            title = CGSCStringValue(titleValue);
        }
        free(windows);
    }
    

    In AppleScript, it's really easy:

    tell application "System Events" to get the title of every window of every process
    

    You can call applescript from within an application using NSAppleScript or use appscript as an ObjC-AppleScript bridge. With Leopard, you can use the Scripting Bridge (more untested code):

    SystemEventsApplication *systemEvents = [SBApplication applicationWithBundleIdentifier:@"com.apple.systemevents"];
    SBElementArray *processes = [systemEvents processes];
    for (SystemEventsProcess* process in processes) {
        NSArray *titles = [[process windows] arrayByApplyingSelector:@selector(title)];
    }
    

    You could even try it in one long call, if you don't care about readability.

    SystemEventsApplication *systemEvents = [SBApplication applicationWithBundleIdentifier:@"com.apple.systemevents"];
    NSArray *titles = [[[systemEvents processes] 
                         arrayByApplyingSelector:@selector(windows)] 
                   arrayByApplyingSelector:@selector(arrayByApplyingSelector:) 
                   withObject:@selector(title)];
    

    The compiler will complain that @selector(title) is the wrong type, but it should work. Hand roll some delegation and you could turn the call into [[[systemEvents processes] windows] title].

    0 讨论(0)
  • 2020-12-23 18:36

    The CGSPrivate.h header that's floating around isn't directly compatible with OS X 10.8 in that CGSGetWindowProperty() no longer exists (well, it does, but you can't link to it anymore). So add these two lines to the CGSPrivate.h file -- I went ahead and figured this out myself after many hours searching Google -- to get it to work:

    extern CGSConnection CGSDefaultConnectionForThread(void);
    extern CGError CGSCopyWindowProperty(const CGSConnection cid, NSInteger wid, CFStringRef key, CFStringRef *output);
    

    Adapting outis's code, here's a way of iterating through each window title. I have tested this with clang 4.2 on Mountain Lion:

    CFStringRef titleValue;
    CGSConnection connection = CGSDefaultConnectionForThread();
    NSInteger windowCount, *windows;
    
    NSCountWindows(&windowCount);
    windows = (NSInteger*) malloc(windowCount * sizeof(NSInteger));
    if (windows) {
        NSWindowList(windowCount, windows);
        for (int i = 0; i < windowCount; ++i)
        {
            CGSCopyWindowProperty(connection, windows[i], CFSTR("kCGSWindowTitle"), &titleValue);
    
            if(!titleValue) //Not every window has a title
                continue;
    
            //Do something with titleValue here
        }
        free(windows);
    }
    

    Some other stuff I found out includes the following:

    1. No window title exceeds 127 bytes.
    2. Window titles are encoded with kCFStringEncodingMacRoman

    So, if you want it as a C-string, write something like this:

    char *cTitle[127] = {0};
    CFStringGetCString(titleValue,cTitle,127,kCFStringEncodingMacRoman);
    

    Personally, I'd recommend doing it this way since the Accessibility API is a total pain and requires extra permissions.

    Hope this helps someone! Cheers!

    0 讨论(0)
提交回复
热议问题