如何获得Mac OSX上窗口标题的列表?

时间:2023-01-24 16:30:25

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

我想获取当前运行的应用程序的窗口标题列表。

On windows I have EnumWndProc and GetWindowText.

在windows上,我有EnumWndProc和GetWindowText。

On Linux I have XGetWindowProperty and XFetchName.

在Linux上,我有XGetWindowProperty和XFetchName。

What is the Native Mac equivalent?

什么是本地Mac对等物?

2 个解决方案

#1


12  

A few potentially useful references:

一些潜在有用的参考资料:

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

CGSGetWindowProperty没有正式的文档记录,但是我相信您可以将它用于NSWindowList()的一个条目(完全未经测试)如下:

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:

在AppleScript中,这真的很简单:

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):

您可以使用NSAppleScript从应用程序内部调用applescript,或者使用appscript作为object - applescript桥接。使用Leopard,您可以使用脚本桥接(更多未经测试的代码):

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].

编译器会抱怨@selector(title)是错误的类型,但它应该可以工作。手动滚动一些委托,您可以将调用转换为[[[[[systemEvents process] windows]标题]。

#2


8  

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:

CGSPrivate。浮动的h报头在CGSGetWindowProperty()中不能直接与OS X 10.8兼容(它已经存在了,但是你不能再链接它了)。把这两条线加到CGSPrivate上。h文件——我在谷歌上搜索了好几个小时之后,自己找到了它——让它工作:

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:

修改outis的代码,这里有一种遍历每个窗口标题的方法。我已经用《山狮4.2》测试过了:

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. 没有窗口标题超过127字节。
  3. Window titles are encoded with kCFStringEncodingMacRoman
  4. 窗口标题用kCFStringEncodingMacRoman编码

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

所以,如果你想把它写成C-string,可以这样写:

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.

就我个人而言,我建议这样做,因为可访问性API非常麻烦,需要额外的权限。

Hope this helps someone! Cheers!

希望这可以帮助别人!干杯!

#1


12  

A few potentially useful references:

一些潜在有用的参考资料:

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

CGSGetWindowProperty没有正式的文档记录,但是我相信您可以将它用于NSWindowList()的一个条目(完全未经测试)如下:

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:

在AppleScript中,这真的很简单:

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):

您可以使用NSAppleScript从应用程序内部调用applescript,或者使用appscript作为object - applescript桥接。使用Leopard,您可以使用脚本桥接(更多未经测试的代码):

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].

编译器会抱怨@selector(title)是错误的类型,但它应该可以工作。手动滚动一些委托,您可以将调用转换为[[[[[systemEvents process] windows]标题]。

#2


8  

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:

CGSPrivate。浮动的h报头在CGSGetWindowProperty()中不能直接与OS X 10.8兼容(它已经存在了,但是你不能再链接它了)。把这两条线加到CGSPrivate上。h文件——我在谷歌上搜索了好几个小时之后,自己找到了它——让它工作:

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:

修改outis的代码,这里有一种遍历每个窗口标题的方法。我已经用《山狮4.2》测试过了:

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. 没有窗口标题超过127字节。
  3. Window titles are encoded with kCFStringEncodingMacRoman
  4. 窗口标题用kCFStringEncodingMacRoman编码

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

所以,如果你想把它写成C-string,可以这样写:

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.

就我个人而言,我建议这样做,因为可访问性API非常麻烦,需要额外的权限。

Hope this helps someone! Cheers!

希望这可以帮助别人!干杯!