一个甩不掉的红叉:深挖 Dropbox 文件夹图标上的”幽灵”同步错误

Written by

in

,

一次完整的取证式排查记录:从”重启大法”一路打到 Dropbox 的内部 SQLite 数据库,推翻三个看似正确的结论,最后用一条官方排障命令收尾。

症状

Windows 11 的文件资源管理器左侧导航栏里,Dropbox 文件夹图标上挂着一个红圈白叉(Dropbox 的”同步错误 / 无法同步”徽标)。但诡异的是:

  • 右下角系统托盘里的 Dropbox 图标完全正常;
  • Dropbox 自带面板底部明确写着 “✓ Your files are up to date”(文件均为最新);
  • 这个红叉已经持续很多天,期间重启过电脑很多次,纹丝不动。

一句话:Dropbox 自己说一切正常,但文件夹图标却在报错。 到底谁在撒谎?

一个关键观察(也是整场排查的起点):同一个 Dropbox 文件夹,在两个不同的资源管理器窗口里,一个有红叉、一个没有。同一对象在不同窗口显示不一致,直觉上指向”缓存 / 刷新问题”而非”实时真实状态”。后面会看到,这个直觉对了一半,又错了一半。


背景知识:文件夹图标上的”徽标”是怎么来的

Windows 资源管理器允许第三方通过 Shell Icon Overlay Handler(外壳覆盖图标处理器) 在文件/文件夹图标上叠加一个小角标。Dropbox、OneDrive、Google Drive、TortoiseGit/SVN、各种网盘都靠它来画”已同步 / 同步中 / 出错”的小图标。

但这里有一个臭名昭著的坑:Windows 最多只认 15 个覆盖图标处理器。

一个容易混淆的点:这台机器上其实有两套图标系统并存。 Dropbox 开了 Files On-Demand(基于 Windows 的 Cloud Files / 云文件 API),文件级的”绿勾(已同步)/ 白云(仅在线)”是 Cloud Files 的 hydration 状态图标画的,不受这 15 个槽位限制;而文件夹级的”同步中 / 出错”角标(至少在本机这套环境里)走的仍是上面这套老式 Shell 覆盖图标处理器(DropboxExt)。本文要追的那个红叉属于后者,所以下文只沿覆盖图标处理器这条线走。

这个限制来自底层 image list 最多只能有 15 个 overlay mask,从 Windows XP 时代硬编码至今,Raymond Chen 在《The Old New Thing》里专门解释过它的来龙去脉(devblogs.microsoft.com)。注册表位置在:

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers
HKLM\SOFTWARE\WOW6432Node\...\ShellIconOverlayIdentifiers

普遍观察到的行为是:Windows 按键名的字母序枚举、只保留前 15 个,后面的静默丢弃。(15 这个上限有 Raymond Chen 的文章背书;而”按键名排序加载”更多是业界长期观察到的实现行为,并非官方逐字承诺的契约——下文我会用实测而非这条规则来下判断。)于是各家应用为了抢进前 15,纷纷在自己的键名前面加前导空格(空格的 ASCII 比字母小,排序靠前)——这是一场公开的”军备竞赛”,ownCloud 的开发者甚至专门提了 issue 吐槽(owncloud/client #3141),也有不少博客教你手动加空格给自己常用的应用提权(garethjmsaunders.co.ukitwriting.com)。

记住这个机制,因为它马上要把我带进第一个(错误的)结论。


第一回合:当成图标缓存花屏 —— 失败

最常见的”图标错乱”原因是 Windows 的图标缓存损坏。标准疗法是重启资源管理器、重建图标缓存数据库:

# 重启 explorer
Stop-Process -Name explorer -Force; Start-Process explorer

# 彻底重建图标缓存
Stop-Process -Name explorer -Force
Remove-Item "$env:LOCALAPPDATA\IconCache.db" -Force
Remove-Item "$env:LOCALAPPDATA\Microsoft\Windows\Explorer\iconcache_*.db" -Force
Start-Process explorer

删掉了 16 个 iconcache_*.db,重启。红叉纹丝不动。

结论:它不是 Windows 图标缓存花屏。 一个能扛过图标缓存彻底重建的角标,要么是实时画上去的,要么藏在别处。


第二回合:15 个覆盖图标名额被吃满 —— 看似铁证,其实站不住

我把 ShellIconOverlayIdentifiers 下的所有处理器列出来并按加载顺序排序,再逐个解析到它们背后的 DLL:

槽位 键名 归属
1–7 OneDrive17 微软 OneDrive(FileSyncShell64.dll)
8–14 .WorkspaceExt06 百度网盘(YunShellExtV164.dll,用”前导空格 + 点”伪装,抢占最高优先级)
15 AccExtIco1 Adobe CoreSync
16–27 DropboxExt0110 ❌ Dropbox,全部排在 15 名之外

OneDrive 占 7 个 + 百度网盘占 7 个 + Adobe 占 1 个,正好吃满 15 个名额,Dropbox 的 10 个处理器一个都没排上。看起来完美解释了一切:Dropbox 画不出正确徽标,旧红叉也清不掉。

实际把注册表项按键名排序后的样子(为抢前 15,各家都在键名前堆前导空格 / 加点号):

[排进]  1.'       OneDrive1'  … 7.'       OneDrive7'        (FileSyncShell64.dll)
[排进]  8.'      .WorkspaceExt0' …14.'      .WorkspaceExt6'  (百度网盘 YunShellExtV164.dll)
[排进] 15.'   AccExtIco1'                                    (Adobe CoreSync)
[出局] 16.'   DropboxExt01' …25.'   DropboxExt10'           (Dropbox ← 全部落在 15 名之外)
[出局] 26.'  115ErrorOverlayIcon' …                         (115 网盘)

这正是网上铺天盖地的那个解释——”装太多网盘把 Dropbox 挤掉了”(itwriting.comCentreStackWikipedia: List of shell icon overlay identifiers)。

但我没有就此收手。 因为有个细节对不上:如果 Dropbox 的处理器没被加载,它连红叉都画不出来才对。于是我去查 explorer.exe 当前到底加载了哪些 DLL:

Get-Process explorer | ForEach-Object { $_.Modules } |
  Where-Object { $_.FileName -match 'DropboxExt|FileSyncShell|YunShellExt|CoreSync' } |
  Select-Object -Expand FileName -Unique

实测输出(只列命中的模块):

Dropbox  : 已加载 -> C:\Program Files (x86)\Dropbox\Client\DropboxExt64.95.0.dll
OneDrive : 已加载 -> C:\Program Files\Microsoft OneDrive\...\FileSyncShell64.dll
Baidu    : 未加载
Adobe    : 已加载 -> C:\Program Files (x86)\Common Files\Adobe\...\CoreSync_x64.dll

结果与”名额被吃满”的预期正好相反:

  • DropboxExt64.95.0.dll —— ✅ 已加载、活动中
  • 百度网盘 YunShellExtV164.dll —— ❌ 没加载(因为百度网盘进程当时没运行!)

反转: 百度网盘虽然霸占着注册表里的高优先级槽位,但它没运行,所以它的处理器根本没被加载(也就谈不上占用 overlay 槽)。”15 名额被吃满”这个流传最广的解释,在这台机器上根本不成立

教训一:注册表里”占着名额”不等于”运行时真加载了”。至少在这台机器上,排在 16 名之外的 DropboxExt 照样被加载进了 explorer 并在画角标——”名额被吃满”的解释在此不成立。(严格说,单凭”DLL 已加载”还不足以断言 Windows 的 15 计数规则,也不能证明别的 handler”让出了名额”;但第四回合会证明红叉确实是 Dropbox 实时画的,从而坐实它的覆盖处理器在正常工作。)要快速验证,直接看 explorer.exe 的已加载模块即可。

既然 DropboxExt 是活的,那红叉就是 Dropbox 自己实时画的。问题升级为:Dropbox 为什么坚持认为根目录有错?

本回合小结 —— 观察到:DropboxExt 已加载、红叉仍在;排除了:”被挤出 15 名额导致画不出图标”这个解释(在本机);还不能证明:Dropbox 的”错误”判断到底源于真实同步问题,还是陈旧状态。


第三回合:desktop.ini 红鲱鱼

文件夹图标可以被自身的 desktop.ini 静态指定。如果 Dropbox 在出错时往 desktop.ini 写了个”错误版”图标资源、修好后没改回来,那就能完美解释”持久且扛重启”。一查:

[.ShellClassInfo]
IconResource=C:\Program Files (x86)\Dropbox\Client\Dropbox.exe,-13001

确实指定了自定义图标 -13001。我用 PrivateExtractIcons(负数索引按资源 ID 提取)把它抠出来看:

# PrivateExtractIcons 传负的 nIconIndex 时,按资源 ID 的绝对值提取
[IconRes]::Save("...\Dropbox.exe", 13001, "res_13001.png", 256)

-13001 是一个干净的蓝色 Dropbox 文件夹图标,没有任何红叉。所以 desktop.ini 是清白的,文件夹的基础图标正常,红叉确实是叠加在上面的动态覆盖,不是静态图标。

教训二:desktop.iniIconResource=...,-N 里的 N资源 ID(负号表示”这是 ID,不是序号”),Windows 的图标提取语义里负值通常被解释为资源 ID(而非从 0 起的序号)——这一点在 ExtractIconEx/SHDefExtractIcon 的官方文档里有明确说明。我用 PrivateExtractIcons 传负值做了验证性提取,取到的正是资源 ID 13001 对应的图标。


第四回合:注册表命名空间节点 + 一个幽灵副本

导航栏里的 Dropbox 项不是普通的”固定文件夹”,而是一个 命名空间外壳扩展(shell namespace extension),跟 OneDrive 一样,通过 HKCU\Software\Classes\CLSID\{GUID} + ...\Desktop\NameSpace\{GUID} 注册。一查,发现注册了两个都叫 “Dropbox”、都指向 C:\Users\sudos\Dropbox 的 CLSID:

  • {9E639962-...} —— 当前生效(在 Desktop\NameSpace 里)
  • {E31EA727-...} —— 幽灵副本(不在 NameSpace 里,疑似重装/迁移残留)

为了判断红叉是”卡在这个外壳节点上”还是”Dropbox 实时喂的”,我做了一个删除—恢复对照实验(全程只改 HKCU、先 reg export 备份、可逆):

  1. 删掉幽灵副本 {E31EA727} + 把活动项 {9E639962} 连定义带挂载点整个删除;
  2. 重启 explorer → 侧边栏 Dropbox 项连同红叉一起消失;
  3. 从备份原样导回活动项;
  4. 重启 explorer → 红叉在节点一出现的瞬间又回来了。

这一步是决定性的:红叉不在注册表 / 外壳缓存里。 删除节点只是把”显示红叉的载体”拿掉了;载体一回来,Dropbox 立刻又把红叉喂上去。

教训三:想区分”显示层缓存卡死”还是”数据源实时输出”,做一次”删除载体—恢复载体”对照实验最干脆。


第五回合:翻 Dropbox 自己的数据库

既然是 Dropbox 实时输出”错误”,那它的依据在哪?我开始只读地翻 Dropbox 的本地状态(都不碰云端、不触发下载):

...\Dropbox\instance1\icon.db(81 MB) —— 名字很像”图标状态库”,但一看 schema 就排除了:键名是 tpng<路径>_256、值是 Python pickle,这其实是缩略图预览缓存,与徽标无关。

sync_history.db —— 近期 99 条事件全是正常的 file/add|edit|delete · upload|download,零错误、零冲突,最近一次同步就在当天下午。

sync_and_storage.db(127 MB) —— 只有一张 storage_files 表(Files-On-Demand 占位索引),根本没有”状态 / 错误”字段

到这里,Dropbox 所有可读的本地证据都在说”我很健康”。


第六回合:”仅在线”状态反推出真相

storage_files425,472 条记录,本地实际 406,481 个文件,其中 406,076 个是”仅在线”(online-only)占位符

“仅在线 vs 本地有内容”是靠 Windows Cloud Files 给占位符打的文件属性位数出来的(只读元数据,不触发下载):

# RECALL_ON_OPEN(0x40000) / RECALL_ON_DATA_ACCESS(0x400000) / OFFLINE(0x1000)
# 任一置位 = 该文件是"仅在线"占位符(内容在云端,本地已脱水)
attrs = entry.stat(follow_symlinks=False).st_file_attributes
online_only = bool(attrs & (0x40000 | 0x400000 | 0x1000))
# 结果:406,481 个文件里 406,076 个 online_only,仅 390 个本地有内容

关键推理:

一个文件能被脱水成”仅在线”占位符,前提是它已经成功上传到云端了。所以所有”仅在线”文件,定义上都是同步成功的。

那么”上传失败”的文件,本地必然是”有内容”状态(不会被脱水)。而本地”有内容”的文件总共只有 390 个——若元凶是某个”上传失败”的文件,它必然落在这 390 个里。我把这 390 个全列出来,逐类排查:最近编辑过的正常文档、Git 仓库、GIS 数据、.DS_Store、会被 Dropbox 默认忽略的 ~$ Office 锁文件、几个自动化脚本留下的 .tmp……

同时对全盘做了一次命名合规扫描(对照 Dropbox 官方的命名规则:不能含 < > : " | ? *、不能以空格/句点结尾、路径 < 260 等,见 Dropbox Help: Naming files and folders):

  • 非法字符:0
  • 尾部空格/句点:0
  • Windows 保留名:0
  • 大小写冲突:0
  • 以空格开头:10 个,但它们全是”仅在线”状态 = 早已同步成功
  • 超长路径(>250):769 个(多为 node_modules 深层嵌套),同样全是”仅在线” = 已同步

换句话说,那些”看起来可疑”的文件,Dropbox 其实都已经顺利同步了。 这一步把嫌疑范围从 40 万骤降到 390 个本地文件——而这 390 个里也没发现任何卡住失败的项

本回合小结 —— 观察到:可疑命名 / 超长路径的文件其实都是”仅在线”= 已同步,本地有内容的仅 390 个且无异常;排除了:存在大批因命名 / 路径而同步失败的文件;还不能证明:Dropbox 内部绝对没有任何挂起项——只能说在可读的本地证据里没发现。


结论:一个陈旧的”幽灵徽标”

把所有证据拼起来:

检查项 结果
Dropbox 面板 / 托盘 ✓ 一切正常,文件均为最新
sync_history 0 错误,当天仍在正常同步
主同步库 无错误字段;40.6 万文件已同步为”仅在线”
本地未上传文件 仅 390 个,无任何卡住失败项
命名合规 0 个硬性不兼容文件
Windows 图标缓存 已重建,无效
desktop.ini 指向干净图标,无辜
注册表节点 红叉随节点删/恢复同步消失/重现 → 由 Dropbox 实时输出

基于现有本地证据,没找到任何真正无法同步的文件。那个红叉更像是 Dropbox 文件资源管理器集成为根目录留下的一个”陈旧徽标”——它和 Dropbox 自己的引擎状态相矛盾,是显示层的 bug。很可能驻留在 Dropbox 自身的状态 / 缓存里(从”重启系统、重启 Dropbox、清 Windows 图标缓存、重置注册表节点全都无效”这一点反推),又由 Dropbox 实时喂给外壳扩展(所以注册表删了再恢复会立刻重现)。

这类”引擎说 up to date、角标却卡在错误”的情况,在各家网盘里都不罕见(参见 Dropbox Help: 检查同步状态 以及社区里大量同类反馈)。


收尾:一条命令搞定

既然是 Dropbox 自己的状态缓存陈旧,正确做法不是再去碰 Windows,而是让 Dropbox 重算并重新推送徽标。Dropbox 官方排障步骤里就有”清空缓存文件夹”这一招,把这一招用上:.dropbox.cache 只是上传/下载用的临时暂存数据,删除不会动你的 Dropbox 正式文件,Dropbox 会按需自动重建它(Dropbox Help: Clear the cache folder)。下面的命令针对本机这种标准布局(个人账户、根目录在 %USERPROFILE%\Dropbox);团队账户或 File Provider 布局下缓存路径不同,需相应调整:

# 1) 退出 Dropbox(官方做法是托盘菜单「退出」;强杀只是脚本化的便捷替代)
Stop-Process -Name Dropbox -Force

# 2) 清空内部缓存(不是你的文件;Dropbox 会自动重建)
Get-ChildItem "$env:USERPROFILE\Dropbox\.dropbox.cache" -Force |
  Remove-Item -Recurse -Force

# 3) 重启 Dropbox
Start-Process "C:\Program Files (x86)\Dropbox\Client\Dropbox.exe"

重启后 Dropbox 重新评估了根目录状态,红叉消失,图标恢复成干净的蓝色盒子。 收工。

注:托盘菜单里的「暂停同步」→「恢复同步」对本案无效——这台机器此前已多次暂停/恢复,红叉依旧;只有清掉 .dropbox.cache、让 Dropbox 重建状态才奏效。可见这个广为流传的建议并非万能。


几条可复用的经验

  1. 别被”显而易见的答案”骗了。 “网盘装太多,15 个覆盖图标名额被吃满”是本案最诱人的解释,而且数据初看完全吻合——但实测 explorer 已加载模块直接推翻了它。注册表占名额 ≠ 运行时真加载。
  2. 区分”显示层缓存”与”数据源实时输出”: 做”删除载体—恢复载体”对照实验。本案中红叉随注册表节点删/复而灭/现,直接证明它是 Dropbox 实时喂的。
  3. 善用”仅在线”占位符状态做反向推理: 能脱水 = 已上传成功。这一步把”40 万嫌疑文件”瞬间缩小到”390 个本地文件”,而这些里也查不出真正卡住的项。
  4. 修复要打在正确的层: 症状显示在 Windows 资源管理器,病根却在 Dropbox 的内部状态。对着 Windows 一通重启、清缓存、改注册表,都是在错误的层上使劲。
  5. 用户的直觉常常是对的: 他从一开始就说”Dropbox 其实没问题”。最终所有取证都指向同一结论——未发现任何实际同步失败的证据,红叉更像显示层的 bug,不影响文件同步。

参考资料

  • Raymond Chen, *Why is there a limit of 15 shell icon overlays?* — The Old New Thing, Microsoft DevBlogs. [oldnewthing]
  • Tim Anderson, *The battle to own Windows Explorer shell overlay icons, or why your OneDrive green ticks have stopped working.* [itwriting]
  • Wikipedia, *List of shell icon overlay identifiers.* [wikipedia]
  • Gareth J. M. Saunders, *Managing overlay icons for Dropbox and TortoiseSVN and TortoiseGit.* [gareth]
  • ownCloud client, Issue #3141: *[Windows] Shell icon overlays take unwarranted precedence in registry list.* [owncloud]
  • CentreStack / Gladinet, *Why can’t I see any overlay icon on the files in the cloud drive?* [gladinet]
  • Dropbox Help, *How to clear the Dropbox cache folder.* [dropbox-cache]
  • Dropbox Help, *How to check if your files and folders are syncing.* [dropbox-sync-check]
  • Dropbox Help, *Naming Dropbox files and folders.* [dropbox-naming]

*排查环境:Windows 11 Home (22631) · Dropbox 95.x · 同步约 40 万文件(Files On-Demand)· 同机并存 OneDrive / 百度网盘 / Adobe CoreSync。*


致谢与署名

本文由 Claude Code 实地探索并撰写初稿:从复现症状、逐层取证(重建图标缓存、解析覆盖图标注册表、检查 explorer 已加载模块、提取图标资源、读取 Dropbox 内部 SQLite 库、全盘只读元数据扫描),到最终定位与修复,全程留痕。网站管理员 oopus 在过程中做了若干关键的问题决断与验证(包括坚持”Dropbox 实际无故障”的判断、对动注册表/扫描等操作的把关与授权、以及”暂停/恢复同步无效”等一手事实的纠正)。最终的修订与审校由 oopuscodex(OpenAI Codex)共同完成——codex 对技术准确性、逻辑严谨度与引用规范做了一轮严格 review,促成了多处”把’很像’收紧为’已证实’”的改写。