如何在macOS Sonoma之前版本中从AudioObjectID获取PID
在macOS Sonoma之前版本获取音频进程PID的方案
在macOS 14.0之前,系统并未提供kAudioProcessPropertyPID这类直接获取音频进程PID的公开API,你可以通过以下两种兼容方案实现相同功能:
方法一:通过Bundle Identifier匹配PID(公开API,稳定可靠)
先获取音频进程的Bundle Identifier,再遍历系统运行中的进程匹配对应PID,对GUI应用和大部分后台进程都适用:
#import <Foundation/Foundation.h> #import <AudioToolbox/AudioToolbox.h> #import <AppKit/AppKit.h> #import <sys/sysctl.h> pid_t getAudioProcessPIDPreSonoma(AudioObjectID process) { CFStringRef bundleID = NULL; UInt32 dataSize = sizeof(CFStringRef); // 读取音频进程的Bundle Identifier AudioObjectPropertyAddress prop = { kAudioProcessPropertyBundleIdentifier, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain }; OSStatus error = AudioObjectGetPropertyData(process, &prop, 0, NULL, &dataSize, &bundleID); if (error != noErr || !bundleID) { return -1; } NSString *bundleIDStr = (__bridge NSString *)bundleID; CFRelease(bundleID); // 优先匹配GUI应用 NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; for (NSRunningApplication *app in [workspace runningApplications]) { if ([app.bundleIdentifier isEqualToString:bundleIDStr]) { return app.processIdentifier; } } // 匹配无UI的后台进程,通过进程名关联Bundle Identifier NSString *processName = [bundleIDStr componentsSeparatedByString:@"."].lastObject; int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; size_t size; sysctl(mib, 4, NULL, &size, NULL, 0); struct kinfo_proc *procs = malloc(size); sysctl(mib, 4, procs, &size, NULL, 0); int count = size / sizeof(struct kinfo_proc); pid_t pid = -1; for (int i = 0; i < count; i++) { struct kinfo_proc *proc = &procs[i]; char name[256]; strncpy(name, proc->kp_proc.p_comm, sizeof(name)); name[sizeof(name)-1] = '\0'; if ([processName isEqualToString:[NSString stringWithUTF8String:name]]) { pid = proc->kp_proc.p_pid; break; } } free(procs); return pid; }
方法二:使用私有属性(仅限非上架应用)
macOS 10.15到13.x版本中,AudioToolbox存在未公开的属性可以直接读取PID,但该方式属于私有API,可能随系统更新失效,且无法通过App Store审核:
pid_t getAudioProcessPIDPreSonomaPrivate(AudioObjectID process) { pid_t pid = -1; UInt32 dataSize = sizeof(pid); // 私有属性标识,在旧版系统中对应PID属性 AudioObjectPropertyAddress prop = { 0x100, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain }; OSStatus error = AudioObjectGetPropertyData(process, &prop, 0, NULL, &dataSize, &pid); return error == noErr ? pid : -1; }
整合到原有代码
将前置版本的实现整合到你的函数中,完整兼容代码如下:
#import <Foundation/Foundation.h> #import <AudioToolbox/AudioToolbox.h> #import <AppKit/AppKit.h> #import <sys/sysctl.h> int getAudioProcessPID(AudioObjectID process) { pid_t pid; if (@available(macOS 14.0, *)) { constexpr AudioObjectPropertyAddress prop { kAudioProcessPropertyPID, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain }; UInt32 dataSize = sizeof(pid); OSStatus error = AudioObjectGetPropertyData(process, &prop, 0, nullptr, &dataSize, &pid); if (error != noErr) { return -1; } } else { pid = getAudioProcessPIDPreSonoma(process); } return pid; } // 前置版本实现函数 pid_t getAudioProcessPIDPreSonoma(AudioObjectID process) { CFStringRef bundleID = NULL; UInt32 dataSize = sizeof(CFStringRef); AudioObjectPropertyAddress prop = { kAudioProcessPropertyBundleIdentifier, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain }; OSStatus error = AudioObjectGetPropertyData(process, &prop, 0, NULL, &dataSize, &bundleID); if (error != noErr || !bundleID) { return -1; } NSString *bundleIDStr = (__bridge NSString *)bundleID; CFRelease(bundleID); NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; for (NSRunningApplication *app in [workspace runningApplications]) { if ([app.bundleIdentifier isEqualToString:bundleIDStr]) { return app.processIdentifier; } } NSString *processName = [bundleIDStr componentsSeparatedByString:@"."].lastObject; int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; size_t size; sysctl(mib, 4, NULL, &size, NULL, 0); struct kinfo_proc *procs = malloc(size); sysctl(mib, 4, procs, &size, NULL, 0); int count = size / sizeof(struct kinfo_proc); pid_t pid = -1; for (int i = 0; i < count; i++) { struct kinfo_proc *proc = &procs[i]; char name[256]; strncpy(name, proc->kp_proc.p_comm, sizeof(name)); name[sizeof(name)-1] = '\0'; if ([processName isEqualToString:[NSString stringWithUTF8String:name]]) { pid = proc->kp_proc.p_pid; break; } } free(procs); return pid; }
内容的提问来源于stack exchange,提问作者RuLoViC
相关产品推荐
相关产品推荐

