fix: 扫描并监听 Windows 开始菜单根级快捷方式#552
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a dual-watcher and dual-scanner mechanism on Windows to ensure that shortcut files (.lnk and .url) located directly in the Start Menu root directory are correctly indexed and monitored, resolving issue #551. It refactors the scanning logic by extracting a shared processShortcutEntry function, implementing a flat directory scanner for the Start Menu root, and adding corresponding unit tests. Feedback on these changes suggests improving robustness by using environment variables (process.env.ProgramData and process.env.APPDATA) instead of hardcoded paths, and wrapping the shortcut processing loop in try-catch blocks to prevent a single corrupt file from failing the entire directory scan.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| export function getWindowsRootScanPaths(): string[] { | ||
| // 系统级开始菜单根 | ||
| const programDataStartMenuRoot = path.join( | ||
| 'C:', | ||
| 'ProgramData', | ||
| 'Microsoft', | ||
| 'Windows', | ||
| 'Start Menu' | ||
| ) | ||
|
|
||
| // 用户级开始菜单根 | ||
| const userStartMenuRoot = path.join( | ||
| os.homedir(), | ||
| 'AppData', | ||
| 'Roaming', | ||
| 'Microsoft', | ||
| 'Windows', | ||
| 'Start Menu' | ||
| ) | ||
|
|
||
| return [programDataStartMenuRoot, userStartMenuRoot] | ||
| } |
There was a problem hiding this comment.
在 Windows 系统中,系统级 ProgramData 目录和用户级 AppData 目录可能会被安装在非 C 盘,或者在企业环境中被重定向(Folder Redirection)。
建议优先使用环境变量 process.env.ProgramData 和 process.env.APPDATA,并提供默认值作为降级方案。这样可以显著提升在非标准 Windows 环境下的健壮性。
export function getWindowsRootScanPaths(): string[] {
// 系统级开始菜单根
const programData = process.env.ProgramData || path.join('C:', 'ProgramData')
const programDataStartMenuRoot = path.join(
programData,
'Microsoft',
'Windows',
'Start Menu'
)
// 用户级开始菜单根
const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming')
const userStartMenuRoot = path.join(
appData,
'Microsoft',
'Windows',
'Start Menu'
)
return [programDataStartMenuRoot, userStartMenuRoot]
}| // 处理文件(共享逐文件逻辑) | ||
| await processShortcutEntry(dirPath, entry, apps, displayNameMap) |
There was a problem hiding this comment.
在递归遍历目录处理快捷方式时,如果其中某一个快捷方式文件损坏或读取失败(例如 processShortcutEntry 抛出未捕获的异常),会导致整个目录的扫描流程中断。
建议在循环内部使用 try...catch 包裹 processShortcutEntry,确保单个文件的处理失败不会影响其他正常快捷方式的扫描,提升扫描器的容错能力。
// 处理文件(共享逐文件逻辑)
try {
await processShortcutEntry(dirPath, entry, apps, displayNameMap)
} catch (error) {
console.error('[Scanner] 处理快捷方式失败 ' + entry.name + ':', error)
}| for (const entry of entries) { | ||
| // 扁平扫描:跳过子目录,仅处理本层文件 | ||
| if (entry.isDirectory()) continue | ||
|
|
||
| apps.push(app) | ||
| await processShortcutEntry(dirPath, entry, apps, displayNameMap) | ||
| } |
There was a problem hiding this comment.
同递归扫描,在扁平扫描目录处理快捷方式时,建议在循环内部使用 try...catch 包裹 processShortcutEntry,防止单个损坏的快捷方式文件导致整个扁平扫描流程中断。
for (const entry of entries) {
// 扁平扫描:跳过子目录,仅处理本层文件
if (entry.isDirectory()) continue
try {
await processShortcutEntry(dirPath, entry, apps, displayNameMap)
} catch (error) {
console.error('[Scanner] 处理快捷方式失败 ' + entry.name + ':', error)
}
}

Closes #551
新增
Start Menu的 watcher,完全复用已有逻辑,不影响其他文件夹的 watcher。新增
Start Menu的 scanner,完全复用已有逻辑,不影响其他文件夹的 scanner。