图片缩略图全是白的:HKCU 里一个 CLSID 指向了不存在的 dll

先给结论。这台机器的缩略图坏在一个注册表键上,删掉立刻恢复。DISM 修不好很正常,它管的是系统映像,坏的地方在用户级的 COM 注册。

修好的命令,管理员 CMD 两行:

reg export "HKCU\Software\Classes\CLSID\{50EF4544-AC9F-4A8E-B21B-8A26180DB13F}" "%TEMP%\clsid_backup.reg" /y
reg delete "HKCU\Software\Classes\CLSID\{50EF4544-AC9F-4A8E-B21B-8A26180DB13F}" /f

第一行是备份,出问题双击那个 reg 文件就能还原。删完立刻生效,已经打开的资源管理器窗口要重启一次才显示新缩略图。

下面讲这台机是怎么查到这一个键的,以及为什么前面的排查全部指向别处。

现象

联想 83LV 笔记本,Win11 家庭中文版 25H2,build 26200。所有图片和视频在资源管理器里都是白纸图标,双击能正常打开,进「照片」也能看,文件本身没坏。资源管理器右侧的预览窗格一片空白。

用户自己先跑过 DISM,checkhealth 报「组件存储可修复」,restorehealth 跑完,现象一点没动。这一步花掉的时间后来证明是白花的,理由放到后面说。

第一步:清缓存,先把简单可能排除掉

缩略图和图标都存在缓存数据库里,分别是 thumbcache_*.db 和 iconcache_*.db,位置在 %LocalAppData%\Microsoft\Windows\Explorer。缓存坏掉是最常见的原因,所以先按标准流程来:

结束 explorer,把 iconcache 和 thumbcache 全部挪到备份目录,重启 explorer,跑 ie4uinit.exe -show。

缓存重建之后现象照旧。这里还有个更直接的证据:%LocalAppData%\Microsoft\Windows\Explorer 目录里一个 thumbcache_*.db 都没生成,iconcache 文件大半是 0 字节。缓存文件根本没被写出来,说明问题在生成环节,不在存储环节。

到这一步,清缓存这条路可以彻底放弃了。

第二步:直接向系统要一张缩略图

资源管理器的显示结果会骗人,中间可能被缩略图、通用图标、扩展名图标几层降级填充。想知道系统到底能不能出图,只能调接口。

Windows 上取缩略图的标准接口是 IShellItemImageFactory::GetImage,参数里带 SIIGBF_THUMBNAILONLY(0x08)就只允许返回缩略图,不允许退回图标。在同一台机上测四类文件:

C:\Windows\Web\Wallpaper\Windows\img0.jpg     缩略图 0x8007007E
C:\Windows\System32\notepad.exe               缩略图 0x8007007E
用户 Pictures 目录下的 jpg                     缩略图 0x8007007E
用户 Pictures 目录下的 png                     缩略图 0x8007007E

四个来源完全不同的文件,返回同一个错误码。0x8007007E 是 HRESULT 包装过的 Win32 错误 126,ERROR_MOD_NOT_FOUND,找不到指定的模块。

这个码值得记住。文件损坏、格式不支持、编解码器缺失,报的都不会是这个。它的意思是系统去加载某个 dll,路径是有的,文件不在了。

顺便加了一组对照:直接从 shell32.dll 和 imageres.dll 里抽图标资源,颜色正常;字体渲染也正常。排除掉资源文件和字体这两层。

第三步:比 HKCU 和 HKLM 的同一个键

缩略图缓存在系统里是一个 COM 类,CLSID 是 {50EF4544-AC9F-4A8E-B21B-8A26180DB13F},名字叫「本地缩略图缓存」。查两处注册:

reg query "HKCU\Software\Classes\CLSID\{50EF4544-AC9F-4A8E-B21B-8A26180DB13F}\InprocServer32" /ve
reg query "HKLM\SOFTWARE\Classes\CLSID\{50EF4544-AC9F-4A8E-B21B-8A26180DB13F}\InprocServer32" /ve

结果:

HKCU\...\InprocServer32
    (默认) = C:\Users\MN GA\AppData\Local\Processor Data\procaddon.dll

HKLM\...\InprocServer32
    (默认) = C:\Windows\System32\thumbcache.dll
    ThreadingModel = Both

HKCU 那条指向的文件不存在。去资源管理器里看 AppData\Local\Processor Data 这个目录,整个目录都没有,procaddon.dll 全盘搜也搜不到。

这就是 0x8007007E 的来源。

为什么一个用户级的键能掐死整条链路

Windows 的 COM 组件注册有两套位置:HKCU\Software\Classes 和 HKLM\SOFTWARE\Classes。平时看到的 HKEY_CLASSES_ROOT(HKCR)只是这两处的合并视图,自身并没有独立的存储。同一个类在两处都有注册时,用户级优先,机器级被遮住。

微软这么设计是给每个用户留覆盖权限,比如不同用户用不同版本的组件。副作用是:用户级只要写一条坏记录,就能把整台机器上这个类的加载全部带偏,而系统本身的 dll 好好的躺在 System32 里,一点毛病没有。

InprocServer32 这个子键的作用是指定进程内组件的宿主 dll。系统要创建缩略图缓存对象时,读这个值,然后 LoadLibrary 那个路径。路径不存在,加载失败,返回 ERROR_MOD_NOT_FOUND,上层拿到的就是 0x8007007E。

所以症状范围由这个 CLSID 决定:凡是通过「本地缩略图缓存」拿图的调用全部失败,资源管理器、照片应用、文件对话框的预览区一起中招。文件能打开不受影响,因为打开走的是文件关联和 ShellExecute,与这个 COM 类无关。

也解释了为什么 DISM 和 SFC 跑多少遍都没用。这两个工具的作用域是 %WinDir% 下的受保护系统文件,外加组件存储(WinSxS)。HKCU\Software\Classes 属于用户配置,不在它们的检查范围内,它们甚至不会往这里看一眼。checkhealth 报「组件存储可修复」只是说 WinSxS 里有可修复的损坏,那个损坏和这次的症状没有因果关系。

procaddon.dll 是怎么进去的

Processor Data 这个目录名不对。AppData\Local 下的正常软件目录一般是产品名,不会用「处理器数据」这种听起来像系统组件的词,更不会在里面放一个 dll 挂到 CLSID 上。

三种来源最像:

  1. 恶意程序的常驻劫持。这类木马惯用 AppData 或 ProgramData 下的伪装目录加载 dll,名字往系统组件上靠。查这台机的历史记录,同类手法在「银狐」那一批样本上见过。
  2. 某些国产优化工具和图片工具的 shell 扩展。它们会注册缩略图处理器来接管图片格式,卸载时删文件不删键,留下指向空路径的记录。
  3. 破解版软件或激活工具。安装包里带私货,注册完没清理。

这台机的 dll 已经被杀软清掉了(机器上装着火绒),但注册表键没被清。杀软处理恶意文件时只针对文件,注册表残留要另外处理,这次就卡在这个缝里。

顺手扫了一遍 HKCU 下所有 CLSID 的 InprocServer32,看有没有第二个指向不存在文件的。查出来一个:WPS Office 的 kdesktopshellext64.dll,路径在 D 盘的 WPS 安装目录,随版本升级没了。不是恶意的,属于卸载或升级不干净,留着顶多报一次加载失败,删不删都行。

批量扫的命令,管理员 PowerShell:

$root = 'HKCU:\Software\Classes\CLSID'
Get-ChildItem $root -ErrorAction SilentlyContinue | ForEach-Object {
    $p = Join-Path $_.PSPath 'InprocServer32'
    if (Test-Path $p) {
        $dll = (Get-ItemProperty $p -ErrorAction SilentlyContinue).'(default)'
        if ($dll) {
            $f = [Environment]::ExpandEnvironmentVariables(($dll -split ',')[0].Trim('"'))
            if (-not (Test-Path $f)) { "{0}  ->  {1}" -f $_.PSChildName, $f }
        }
    }
}

只读,不改任何东西。输出里只要出现指向 AppData、Temp、ProgramData 又找不到文件的条目,就值得看一眼。

修复

只删那一个键,让它回落到 HKLM 的正常注册。

先备份,导出的文件不到 1KB,内容就是待删的坏值:

reg export "HKCU\Software\Classes\CLSID\{50EF4544-AC9F-4A8E-B21B-8A26180DB13F}" "%TEMP%\clsid_backup.reg" /y

再删:

reg delete "HKCU\Software\Classes\CLSID\{50EF4544-AC9F-4A8E-B21B-8A26180DB13F}" /f

影响范围只有当前用户这一个 COM 类,不动系统文件,不动其他用户,不用重启机器。

删完立刻复测,缩略图接口全部正常:

img0.jpg        THUMB=OK            ICON=OK
notepad.exe     THUMB=0x8004B200    ICON=OK
用户 jpg        THUMB=OK            ICON=OK
用户 png        THUMB=OK            ICON=OK

notepad.exe 那个 0x8004B200 是「该项没有缩略图」,exe 本来就没有缩略图处理器,属于正常返回,别当成失败。

thumbcache_256.db 从不存在变成 1MB,缓存开始正常写入。

图标那部分没只看返回码,逐像素统计了颜色分布再判断。空白图标的颜色数很少、亮度标准差接近 0,有内容的图标不是这个形状:

jpg 文件图标      colors=634   meanLum=108.6  sd=109.5
视频文件图标      colors=432   meanLum=108.8  sd=114.2
设置应用图标      colors=401   meanLum= 84.2  sd= 75.5
notepad           colors=2385  meanLum=100.2  sd= 94.1

四个都有真实内容。这一点纠正了我一开始的判断:初看截图,图片文件和系统设置图标都是白的,很容易认定是两套缓存同时损坏。实际上图标一直是好的,白的是资源管理器里的预览区和文件类型缩略图,两者在截图上长得差不多,肉眼分不出来。

远程操作的一个坑

这台机器是通过 SSH 连进去修的。用 taskkill /f /im explorer.exe 杀完再 start explorer.exe,新进程会起在 SSH 所在的 session 0,用户的桌面在 session 1,结果是桌面直接没了,得让用户重新登录。

正确做法是把重启动作投递到交互会话,用计划任务带 -Principal 指定用户 SID、登录类型 Interactive:

$action = New-ScheduledTaskAction -Execute 'cmd.exe' `
  -Argument '/c taskkill /f /im explorer.exe & ping -n 4 127.0.0.1 >nul & start "" explorer.exe'
$principal = New-ScheduledTaskPrincipal -UserId 'S-1-5-21-...-1001' -LogonType Interactive
Register-ScheduledTask -TaskName 'RestartExplorer' -Action $action -Principal $principal -Force
Start-ScheduledTask -TaskName 'RestartExplorer'

-UserId 必须用 SID。工作组机器上 $env:USERDOMAIN 是 WORKGROUP,拼成 WORKGROUP\用户名 传进 -UserId,映射不到 SID,会报 No mapping between account names and security IDs was done。

判断这类故障的入口

碰到缩略图全灭、文件还能正常打开的情况,按这个顺序走,能省掉大部分时间:

  1. 清缓存重建。一轮就够,重建后照旧就往下走,别反复清。
  2. 用 IShellItemImageFactory 带 SIIGBF_THUMBNAILONLY 测两三个不同来源的文件。全部返回 0x8007007E,直接进第 3 步。
  3. 用 reg query 对比 HKCU 和 HKLM 的 {50EF4544-AC9F-4A8E-B21B-8A26180DB13F}\InprocServer32,正确值是 C:\Windows\System32\thumbcache.dll。
  4. 顺手扫一遍 HKCU 下其他 CLSID,看有没有同类残留。
  5. 磁盘层面查一次 chkdsk C: /scan 就够,只为排除硬件,缩略图这类症状很少由坏道引起。

总结

这件事真正花时间的地方在方向选错。缩略图不出图,第一反应是缓存坏了、系统映像坏了、显卡驱动坏了,于是清缓存、跑 DISM、跑 SFC、更新驱动,一路下来几个小时,症状一动不动。DISM 和 SFC 被当成万能药这件事,在维修圈里已经成了条件反射,跑完输出「已修复」就当交差,但这两个工具的检查范围只有系统目录和 WinSxS,用户配置、COM 注册、shell 扩展、缓存数据库全在外面。症状出在用户层,跑十遍也白跑。

另一个容易被忽略的是错误码的区分。0x8007007E 这个「找不到指定的模块」在网络上一堆帖子里被写成「缺少 dll,重装软件」,方向直接偏掉。它说的是加载路径上的文件不存在,而加载路径本身可以是注册表里一条被改坏的记录,这种情况下重装软件也没用,因为装的是软件,坏的是 COM 注册。

还有个更现实的判断:这台机上的 dll 已经被杀软清走了,注册表键却完好无损地留着,继续把缩略图功能按住半年。安全软件清掉恶意文件不等于清掉恶意行为,落地成注册表、计划任务、服务的那些痕迹往往要人工收尾。删完那个键之后,重启一次杀软全盘扫一遍,该做的还是要做。