返回博客

如何找出 macOS 某个窗口属于哪个进程

了解如何用活动监视器、Quartz 窗口 API、终端命令和 ProcXray 判断 macOS 上某个窗口到底属于哪个进程。

当你在 macOS 上看到一个悬浮弹窗、卡住的登录框,或者来路不明的窗口时,最常见的问题就是:这个窗口到底是哪个进程创建的? Finder 和 Dock 都不会直接告诉你答案,而活动监视器也只能帮到一部分。

快速结论

在 macOS 上,想准确找出某个窗口属于哪个进程,最可靠的内置方法是调用 Quartz Window Services 的 CGWindowListCopyWindowInfo,读取窗口的 kCGWindowOwnerPID,再用 ps 检查该 PID。活动监视器能帮助缩小范围,但 Quartz 才能给出更精确的窗口到进程映射。

为什么这个问题在 macOS 上并不直观

macOS 没有提供一个显眼的“右键这个窗口并显示 PID”的功能。一个应用可能拥有多个窗口、多个辅助进程和后台代理,仅凭 Dock 图标或窗口标题来猜,往往并不可靠。

Apple 官方文档也解释了这一点。在窗口信息字典中,kCGWindowOwnerPID必有字段,而 kCGWindowOwnerNamekCGWindowName可选字段。也就是说,所属 PID 通常是稳定可取的,但应用名和窗口标题有时会缺失。

方法一:如果你已经知道是哪个应用,先用活动监视器

如果你已经大致知道窗口来自哪个应用,活动监视器 是最快的内置图形工具。

如何缩小范围

  1. /Applications/Utilities/ 打开 活动监视器
  2. 选择 显示 > 有窗口的进程
  3. 如果你还需要父子关系,选择 显示 > 所有进程,分层显示
  4. 搜索应用名,然后记录对应的 PID 或打开信息面板继续检查。

这适合处理类似“这个弹窗到底是 Safari、Slack 还是某个辅助程序弹出来的?”这类问题。

活动监视器的局限

活动监视器显示的是可以创建窗口的进程,而不是“这个具体窗口对应哪个 PID”的直接映射。以下场景里,它就不够用了:

这时就该用 Quartz。

方法二:用 Quartz Window Services 直接把窗口映射到 PID

Apple 的 Core Graphics API CGWindowListCopyWindowInfo 会返回屏幕窗口的元数据,其中就包含所属进程的 PID。你可以直接用一段简短的 Swift 命令调用它,无需先写完整 App。

列出可见窗口及其所属进程

swift -e '
import Foundation
import CoreGraphics

let query = CommandLine.arguments.dropFirst().joined(separator: " ").lowercased()
let windows = CGWindowListCopyWindowInfo(
  [.optionOnScreenOnly, .excludeDesktopElements],
  kCGNullWindowID
) as? [[String: Any]] ?? []

for window in windows {
  let owner = (window[kCGWindowOwnerName as String] as? String) ?? ""
  let pid = (window[kCGWindowOwnerPID as String] as? Int) ?? 0
  let title = (window[kCGWindowName as String] as? String) ?? ""

  guard !owner.isEmpty else { continue }
  guard query.isEmpty ||
    owner.lowercased().contains(query) ||
    title.lowercased().contains(query) else { continue }

  print("PID: \(pid)\tApp: \(owner)\tWindow: \(title)")
}
' "Safari"

"Safari" 替换成你正在排查的应用名或窗口标题片段即可。如果省略搜索词,命令会打印当前能枚举到的所有可见窗口。

输出里最关键的字段

如果你的真实需求是“这个具体窗口到底归谁管”,这就是最靠谱的内置方案。

拿到 PID 后继续确认进程详情

有了 PID 之后,再用 ps 查看可执行文件和命令行参数:

ps -p <PID> -o pid,ppid,comm,args

例如:

ps -p 1234 -o pid,ppid,comm,args

这样你就能从“一个可疑窗口”继续追到“具体是哪个二进制、由谁启动、带了哪些参数”。

使用时要注意的几点

方法三:如果这是高频排查场景,用 ProcXray 更省时间

如果你经常要做这类定位,纯命令行会越来越麻烦。ProcXray 更适合把“来路不明的窗口”一路追到完整的进程上下文。

ProcXray 为什么更适合持续排查

如果窗口来自辅助进程、更新器或短命后台工具,那么“PID 之外的上下文”通常比 PID 本身更重要。

你也可以继续阅读这些相关文章:

活动监视器 vs Quartz vs ProcXray

方法最适合优点局限
活动监视器快速人工确认系统自带、上手快、能看到 PID不能把任意窗口直接映射到所属进程
Quartz 窗口查询精确窗口到 PID 定位官方 API、可脚本化、结果精确原始输出仍需手动继续分析
ProcXray正式排查与高频调试进程谱系、环境变量、签名、短命进程一站式可见需要额外安装

结论很简单: 偶尔查一次,用 Quartz 查询就够;如果你经常排查窗口、helper 和短命进程,ProcXray 会更高效。

常见问题(FAQ)

活动监视器能直接显示某个窗口属于哪个进程吗?

不能直接显示。活动监视器可以通过 有窗口的进程 过滤出候选对象,也能帮助你查看 PID,但它没有“点击某个窗口就显示确切所属进程”的能力。更精确的内置方法是 Quartz Window Services。

Apple 官方推荐用哪个 API 找窗口所属进程?

Apple 文档中,CGWindowListCopyWindowInfo 就是用于读取窗口元数据的 Core Graphics 函数。返回结果里包含 kCGWindowOwnerPID,而且它是必有字段,所以这是窗口到进程映射时最权威的字段。

为什么有些 macOS 窗口标题是空白的?

因为 kCGWindowName 是可选字段,不保证一定存在。很多工具窗口、系统浮层和受保护界面只暴露所属 PID,不暴露可读标题,这属于正常情况。

找到 PID 之后下一步该做什么?

先执行 ps -p <PID> -o pid,ppid,comm,args,确认可执行文件、父进程和命令行参数。如果看起来仍然可疑,再继续检查它的打开文件、网络连接和代码签名。

参考资料

免费下载 ProcXray → — 更快查清一个可疑窗口背后的真实进程。