如何在Mac OSX上获取窗口标题列表?

18

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

在Windows上,我可以使用EnumWndProc和GetWindowText函数。

在Linux上,我可以使用XGetWindowProperty和XFetchName函数。

那么,在Mac系统中有什么本地等价的函数吗?

2个回答

13

以下是一些可能有用的参考资料:

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);
}

在AppleScript中,这非常简单:
tell application "System Events" to get the title of every window of every process

你可以使用NSAppleScript在应用程序内调用AppleScript,或者使用appscript作为ObjC-AppleScript桥接。在Leopard中,你可以使用Scripting Bridge(更多未经测试的代码):
SystemEventsApplication *systemEvents = [SBApplication applicationWithBundleIdentifier:@"com.apple.systemevents"];
SBElementArray *processes = [systemEvents processes];
for (SystemEventsProcess* process in processes) {
    NSArray *titles = [[process windows] arrayByApplyingSelector:@selector(title)];
}

如果您不关心可读性,甚至可以尝试在一个长的调用中使用它。

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

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

2
请注意,AppleScript正在使用可访问性接口,这些接口是公共的,并且具有C等效项(请参见http://developer.apple.com/mac/library/documentation/Accessibility/Reference/AccessibilityLowlevel/)。 CGS * API不仅没有记录,而且可能随时更改。(因此,只有在您愿意在新的操作系统版本上进行早期和频繁测试并且没有其他选择时才使用它们。) - Nicholas Riley
1
你有无障碍API的使用示例吗?我本来希望将其包含在内,但不熟悉它的使用方法。 - outis
此外,它需要启用辅助功能。 - outis
你知道吗?我刚刚测试了AppleScript解决方案,获取窗口列表还需要启用辅助功能。 - outis
3
示例无障碍API代码:http://developer.apple.com/mac/library/samplecode/UIElementInspector/index.html - outis

8

CGSPrivate.h这个头文件并不直接兼容OS X 10.8,因为CGSGetWindowProperty()已经不存在了(实际上仍然存在,但你不能再链接它了)。所以请在CGSPrivate.h文件中添加以下两行代码 - 经过我多次搜索谷歌后,我自己找到了解决办法 - 就可以让它正常工作:

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

参考outis的代码,这里有一种遍历每个窗口标题的方法。我已在Mountain Lion上使用clang 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);
}

我发现的其他一些内容如下:

  1. 没有窗口标题超过127个字节。
  2. 窗口标题使用kCFStringEncodingMacRoman编码。

因此,如果你想将其作为C字符串,可以写成以下代码:

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

个人而言,我建议这样做,因为辅助功能API非常麻烦,需要额外的权限。

希望这能帮到某些人!干杯!


谢谢您的回答,但是关于可移植性呢?CGS_xx无法找到或已被10.6弃用。我编写的是适用于10.8.2 ML的代码,肯定不能依赖于已弃用的东西。 - user1299518
好的,我上面提供的代码是在Mountain Lion上测试过的。它不是找不到,而是已经改变了。我上面所做的反向工作可以给出正确的签名,让你能够使用CGS_xx来解决这个特定的问题。我只尝试了窗口标题,没有尝试其他任何东西。如果你觉得需要使用它,你应该发布多个版本的软件包(例如,针对10.6及以下或10.7+)。 - Alex Reinking

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接