【发布时间】:2026-01-06 20:45:02
【问题描述】:
我需要提到一个第 3 方程序,或者更好地说是 WinThumbsPreloader 程序的源代码,它包含用 C# 编写的所有必要代码,用于在 Windows 缩略图缓存中预加载文件的缩略图,如您在这个演示:
问题是我需要预加载的是文件夹的图标,但是IThumbnailCache::GetThumbnail方法不允许传递文件夹项,会返回错误码0x8004B200(WTS_E_FAILEDEXTRACTION):
Shell 项目不支持缩略图提取。例如,.exe 或 .lnk 项目。
换句话说,我需要做与 WinThumbsPreloader 程序相同的事情,但是对于文件夹图标而不是文件夹/图标缩略图。
所以,我的意思是我有一个文件夹,里面有一个 desktop.ini 文件,您可能知道它可以用来替换存储该文件的文件夹的默认图标/缩略图desktop.ini 文件。 desktop.ini 文件内容示例:
[.ShellClassInfo]
IconResource=FolderPreview.ico,0
我需要预加载文件夹的原因是为了避免每次浏览文件夹时生成图标缓存。
为了消除疑虑,我想避免这种缓慢的文件夹图标生成:
相反,获得这种改进的速度:
我的问题是:在 C# 或 VB.NET 中,如何以编程方式在 Windows 图标缓存中预加载特定文件夹的图标?
看来IThumbnailCache接口不是解决这个问题的办法……
更新 1
根据@Jimi 的评论建议,这是我正在尝试的方法:
Public Shared Sub PreloadInIconCache(path As String,
Optional iconSize As Integer = 256)
Dim iIdIShellItem As New Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")
Dim shellItem As IShellItem = Nothing
Dim shellItemIF As IShellItemImageFactory
Dim iconFlags As IShellItemImageFactoryGetImageFlags =
IShellItemImageFactoryGetImageFlags.IconOnly
SHCreateItemFromParsingName(path, IntPtr.Zero, iIdIShellItem, shellItem)
shellItemIF = DirectCast(shellItem, IShellItemImageFactory)
shellItemIF.GetImage(New NativeSize(iconSize, iconSize), iconFlags, Nothing)
Marshal.ReleaseComObject(shellItemIF)
Marshal.ReleaseComObject(shellItem)
shellItemIF = Nothing
shellItem = Nothing
End Sub
它缓存图标。如果我调用这个方法让我们说一个包含 1000 个带有自定义图标的子文件夹的目录,那么 iconcache_256.db 的大小会增加大约 250 MB,因此它清楚地证明了图标正在被缓存,但是我做错了,因为操作系统不使用这些缓存的图标。我的意思是,如果在调用该方法然后手动使用 Explorer.exe 导航到该目录后,操作系统会再次开始提取和缓存创建新图标引用的所有 1000 个子文件夹的图标,所以 iconcache_256.db 将其文件大小翻倍至 500 MB 左右,这证明 iconcache_256.db 包含我使用上述方法缓存的图标和操作系统本身缓存的图标,所以我调用上述方法生成的图标缓存引用在某种程度上与操作系统本身生成的图标缓存引用不同,这就是我做错了......
我做错了什么?
更新 2
使用 WindowsAPICodePack v1.1 库(来自 Nuget 管理器)我遇到了与使用 IShellItemImageFactory.GetImage 的 UPDATE 1 代码中描述的相同的问题:我可以提取图标并将图标缓存在 iconcache_256.db 文件中(以及其他较小的 con 缓存大小的 *.db 文件),但是如果我通过 Explorer.exe 导航到该目录,则操作系统开始再次提取和缓存我已经缓存的相同文件夹的图标...
完整代码示例:
Imports Microsoft.WindowsAPICodePack.Shell
...
Dim directoryPath As String = "C:\Directory"
Dim searchPattern As String = "*"
Dim searchOption As SearchOption = SearchOption.AllDirectories
For Each dir As DirectoryInfo In New DirectoryInfo(directoryPath).EnumerateDirectories(searchPattern, searchOption)
Console.WriteLine($"Extracting icon for directory: '{dir.FullName}'")
Using folder As ShellFolder = DirectCast(ShellFolder.FromParsingName(dir.FullName), ShellFolder)
folder.Thumbnail.FormatOption = ShellThumbnailFormatOption.IconOnly
folder.Thumbnail.RetrievalOption = ShellThumbnailRetrievalOption.Default
Using ico As Bitmap = folder.Thumbnail.Bitmap ' Or: folder.Thumbnail.Icon
' PictureBox1.Image = ico
' PictureBox1.Update()
' Thread.Sleep(50)
End Using
End Using
Next dir
我不确定为什么操作系统在我已经缓存它们时坚持提取新图标并缓存它们,这将 iconcache_256.db 中的缓存图标引用乘以 x2(以及另一个 * .db 文件),因为如果我从一个目录中迭代 1000 个子文件夹以提取和缓存它们的图标,如果在我这样做之后我通过 Explorer.exe 导航到该文件夹,那么操作系统将再次提取并缓存那些在 iconcache_256.db (以及其他 *.db 文件)中创建新条目的 1.000 个子文件夹图标。
我不知道如何读取iconcache_256.db文件的数据库格式,所以我不知道结构格式,但是如果结构将目录路径作为其字段之一然后我怀疑我使用的代码方法可能会强制添加一个目录路径字段,当我导航到文件夹以通过 Explorer.exe 缓存图标时,该字段与操作系统在图标缓存字段中添加的内容不同,并且可能出于这个原因图标缓存引用乘以 x2...只是我在推测...
更新 3
我认为真正的问题可能是图标缓存 *.db 文件中添加的引用可能是每个进程的,因此当我使用 Explorer.exe 导航到目录时,它开始再次提取和缓存图标我在 Visual Studio 中调试我的可执行文件时已经提取和缓存了...
所以我做了一个测试,运行相同的代码来从两个不同的进程中提取/缓存文件夹图标,只需将同一个项目构建到两个具有不同名称和不同程序集 guid 的可执行文件。我发现当我运行第二个可执行文件时,*.db 文件中的图标缓存引用没有增加/复制,因此放弃了每个进程的想法。
我真的不知道我还能尝试什么...以及 *.db 图标缓存文件中的“重复”引用有什么不同。
【问题讨论】:
-
见IShellItemImageFactory::GetImage备注部分的注释
-
@Jimi 感谢您的评论,但不确定我必须在那里看到什么,您能详细解释一下吗?理论上 ShellItemImageFactory::GetImage 将用于提取图标(或获取缓存的图标,如果存在)但不会强制操作系统提取它然后将其缓存在系统的图标/缩略图缓存中,是这不对吗?。
-
在您的示例文件夹中,没有缩略图,因为您将其强制为带有 desktop.ini 的图标 (folder.ico)。
-
我不能保证什么,但测试起来并不难。图标有点过时了。 IShellItemImageFactory(注意“图像”通用术语)看起来 1)用于缩略图和 2)用于图标(顺便说一句,当它查找缩略图时它使用 IThumbnailCache)。它并不是真正的缓存机制。
-
您是否尝试过使用ThumbCache Viewer 来检查数据库内容,然后发布您的代码?
标签: c# .net vb.net winapi icons