图片缩略图全是白的:HKCU 里一个 CLSID 指向了不存在的 dll
图片缩略图全是白的: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 上。
三种来源最像:
- 恶意程序的常驻劫持。这类木马惯用 AppData 或 ProgramData 下的伪装目录加载 dll,名字往系统组件上靠。查这台机的历史记录,同类手法在「银狐」那一批样本上见过。
- 某些国产优化工具和图片工具的 shell 扩展。它们会注册缩略图处理器来接管图片格式,卸载时删文件不删键,留下指向空路径的记录。
- 破解版软件或激活工具。安装包里带私货,注册完没清理。
这台机的 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。
判断这类故障的入口
碰到缩略图全灭、文件还能正常打开的情况,按这个顺序走,能省掉大部分时间:
- 清缓存重建。一轮就够,重建后照旧就往下走,别反复清。
- 用
IShellItemImageFactory带SIIGBF_THUMBNAILONLY测两三个不同来源的文件。全部返回 0x8007007E,直接进第 3 步。 - 用 reg query 对比 HKCU 和 HKLM 的
{50EF4544-AC9F-4A8E-B21B-8A26180DB13F}\InprocServer32,正确值是C:\Windows\System32\thumbcache.dll。 - 顺手扫一遍 HKCU 下其他 CLSID,看有没有同类残留。
- 磁盘层面查一次
chkdsk C: /scan就够,只为排除硬件,缩略图这类症状很少由坏道引起。
总结
这件事真正花时间的地方在方向选错。缩略图不出图,第一反应是缓存坏了、系统映像坏了、显卡驱动坏了,于是清缓存、跑 DISM、跑 SFC、更新驱动,一路下来几个小时,症状一动不动。DISM 和 SFC 被当成万能药这件事,在维修圈里已经成了条件反射,跑完输出「已修复」就当交差,但这两个工具的检查范围只有系统目录和 WinSxS,用户配置、COM 注册、shell 扩展、缓存数据库全在外面。症状出在用户层,跑十遍也白跑。
另一个容易被忽略的是错误码的区分。0x8007007E 这个「找不到指定的模块」在网络上一堆帖子里被写成「缺少 dll,重装软件」,方向直接偏掉。它说的是加载路径上的文件不存在,而加载路径本身可以是注册表里一条被改坏的记录,这种情况下重装软件也没用,因为装的是软件,坏的是 COM 注册。
还有个更现实的判断:这台机上的 dll 已经被杀软清走了,注册表键却完好无损地留着,继续把缩略图功能按住半年。安全软件清掉恶意文件不等于清掉恶意行为,落地成注册表、计划任务、服务的那些痕迹往往要人工收尾。删完那个键之后,重启一次杀软全盘扫一遍,该做的还是要做。