Claude Code 任务栏通知系统实现
·
Claude Code 任务栏通知系统
功能效果
Claude Code 会话完成(Stop 事件)时,自动触发以下通知:
- 任务栏黄色进度条 — 对应 VSCode 的任务栏图标变为黄色(
TBPF_PAUSED状态),提示本次对话已结束 - 窗口标题标记 — 若同时打开了多个 VSCode 窗口,该窗口的 Win32 标题临时变为
★ Claude: <项目目录名>,鼠标悬浮任务栏图标组时可通过缩略图标题识别是哪个窗口 - Toast 弹窗 — 右下角系统通知弹窗,内容为
Claude [<项目目录名>] - 自动清除 — 用户点击(聚焦)对应 VSCode 窗口后,黄色进度条在 500 ms 内消失,窗口标题由 VSCode 自动恢复
涉及文件
| 文件 | 位置 | 职责 |
|---|---|---|
claude-notify.ps1 | %USERPROFILE%\.claude\ | 主通知脚本:设黄色进度条、改窗口标题、发 Toast、启动 reset 进程 |
claude-notify-reset.ps1 | %USERPROFILE%\.claude\ | 重置脚本:后台轮询前台窗口,用户聚焦 VSCode 后清除黄色 |
settings.json | %USERPROFILE%\.claude\ | Claude Code 全局配置,通过 hooks.Stop 触发主通知脚本 |
hook_test.log | %USERPROFILE%\.claude\ | 运行日志,记录每次通知和清除的时间戳、窗口句柄、进程 PID |
触发机制
Claude Code 会话结束
│
▼ hooks.Stop(settings.json)
claude-notify.ps1(同步执行,<1 秒)
│
├─ 遍历父进程链,找到有窗口句柄的 Code.exe(VSCode 主进程)
│ └─ 若未找到,按窗口标题匹配当前目录的 Code.exe
│
├─ ITaskbarList3::SetProgressState(hwnd, TBPF_PAUSED=8)
│ └─ 任务栏图标变为黄色
│
├─ SetWindowText(hwnd, "★ Claude: <dir>")
│ └─ 任务栏缩略图标题变化,多窗口时可识别
│
├─ WinRT Toast 弹窗
│
└─ Start-Process → claude-notify-reset.ps1(后台异步,最长 10 分钟)
claude-notify-reset.ps1(后台持续轮询)
│
├─ EnumWindows 枚举 TargetPid 的所有顶层窗口
│
├─ WMI 递归枚举 TargetPid 的全部子/孙 Code.exe 进程 PID
│ └─ VSCode = 1 个主进程 + 十余个渲染器子进程
│
└─ 每 500 ms 检查前台窗口
├─ GetForegroundWindow() → GetWindowThreadProcessId() → fgPid
├─ GetAncestor(fg, GA_ROOT) → rootHwnd
│
└─ 命中条件:rootHwnd == HwndValue OR fgPid ∈ relatedPids
└─ 对所有枚举到的窗口调用 SetProgressState(h, TBPF_NOPROGRESS=0)
依赖的 Windows API
claude-notify.ps1
| API | 用途 |
|---|---|
ITaskbarList3::SetProgressState | 设置任务栏进度条状态(黄色 = TBPF_PAUSED=8,清除 = 0) |
ITaskbarList3::SetProgressValue | 配合 SetProgressState 固定进度为 100% |
user32!SetWindowText (Unicode) | 临时修改 Win32 窗口标题,用于多窗口识别 |
user32!GetWindowThreadProcessId | 从窗口句柄取进程 PID |
WinRT ToastNotificationManager | 发送系统 Toast 弹窗 |
claude-notify-reset.ps1
| API / 机制 | 用途 |
|---|---|
user32!GetForegroundWindow | 获取当前前台窗口句柄 |
user32!GetAncestor(hWnd, GA_ROOT=2) | 沿父窗口链找根窗口,用于匹配主窗口 |
user32!GetWindowThreadProcessId | 前台窗口 → 进程 PID |
user32!EnumWindows | 枚举指定进程的所有顶层窗口 |
ITaskbarList3::SetProgressState | 清除任务栏进度条 |
WMI Win32_Process | 递归枚举 VSCode 子进程树(渲染器进程) |
VSCode 多进程架构说明
VSCode 基于 Electron,运行时由多个 Code.exe 构成进程树:
Code.exe(浏览器主进程,PID=XXXX,有主窗口句柄)
├── Code.exe(GPU 进程)
├── Code.exe(渲染器进程,拥有编辑器/面板窗口)
│ └── Code.exe(Extension Host 渲染器)
├── Code.exe(渲染器进程,拥有终端面板窗口)
└── Code.exe(工具进程)
当用户点击 VSCode 编辑器区域时,聚焦的是渲染器子进程所有的窗口,而不是主进程窗口。
因此 reset 脚本必须通过 WMI 递归收集整棵子进程树的 PID,才能正确检测到"用户回到 VSCode"这一事件。
settings.json 关键配置
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell -NoProfile -ExecutionPolicy Bypass -STA -File \"%USERPROFILE%\\.claude\\claude-notify.ps1\""
}
]
}
]
},
"permissions": {
"allow": ["Bash(*)", "PowerShell(*)"]
}
}
hooks.Stop:Claude Code 内置钩子,每次会话结束时同步执行指定命令-STA参数:PowerShell 单线程模式,COM 对象(ITaskbarList3、WinRT Toast)需要此模式PowerShell(*):允许 Claude 在调试时免提示执行 PowerShell 工具调用
已知限制
- reset 脚本在启动时一次性枚举进程树,之后不刷新。若 VSCode 在通知期间新建渲染器进程,该进程 PID 不在集合中,点击其窗口不会触发清除(概率较低)
- 窗口标题由 VSCode 在获得焦点后自动恢复,极少数情况下(VSCode 长时间无内部事件)可能滞留
- 同一台机器运行多个不同目录的 Claude Code 会话时,各自独立触发通知,互不干扰
更多推荐


所有评论(0)