最近整理照片,但是一個檔案夾裡面上萬張照片 只透過檔名真的很難判斷
都是要靠系統 Windows 內建的縮圖來判斷 但是產生速度非常的慢 我就在想能不能先去把縮圖做好
目前問一下 GPT 的做法,看起來最好的解決方案就是去呼叫 Win32 先去產生縮圖
而且後來我才發現一件事情,以前會跟著檔案夾的 Thumbs.db 現在都已經統一在
C:\Users\[用戶名稱]\AppData\Local\Microsoft\Windows\Explorer\thumbcache_256.db
接下來就是程式碼的部分,首先我們得先製作 Win32 的呼叫介面
C# Code:
public static class WindowsThumbnailProvider
{
public static IntPtr GetThumbnail(string fileName, int width, int height)
{
Guid iid = new Guid("BCC18B79-BA16-442F-80C4-8A59C30C463B");
SHCreateItemFromParsingName(fileName, IntPtr.Zero, ref iid,
out IShellItemImageFactory factory);
SIZE size = new SIZE() { cx = width, cy = height };
factory.GetImage(size, 0x0, out IntPtr hBitmap);
return hBitmap; // 可再轉成 Bitmap
}
[DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
private static extern void SHCreateItemFromParsingName(
string pszPath,
IntPtr pbc,
ref Guid riid,
out IShellItemImageFactory ppv
);
}
[ComImport]
[Guid("BCC18B79-BA16-442F-80C4-8A59C30C463B")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItemImageFactory
{
void GetImage(SIZE size, int flags, out IntPtr phbm);
}
[StructLayout(LayoutKind.Sequential)]
internal struct SIZE
{
public int cx;
public int cy;
}
這段程式碼透過 Win32 API 取得 Windows 系統產生的縮圖 ,用 SHCreateItemFromParsingName 取得 IShellItemImageFactory
COM 物件 再呼叫 GetImage 產生指定尺寸的縮圖 並回傳 hBitmap你可以把它轉成 Bitmap 使用
重點在於這是系統級縮圖 不需要自己壓圖 ,比自己去滑動還簡單吧?
之後就是透過主程式呼叫讓你要去 "開光" 的檔案夾
static bool IsImage(string file)
{
string ext = Path.GetExtension(file).ToLower();
return ext == ".jpg" || ext == ".jpeg" || ext == ".png" ||
ext == ".bmp" || ext == ".gif" || ext == ".webp" ||
ext == ".heic" || ext == ".tiff";
}
async static Task Main()
{
Console.WriteLine("請輸入做快取的檔案夾");
var folder = Console.ReadLine();
string[] files = Directory.GetFiles(folder, "*.*", SearchOption.TopDirectoryOnly);
var options = new ParallelOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount / 2 // 避免 COM 爆掉
};
Parallel.ForEach(files, options, file =>
{
if (!IsImage(file)) return;
bool ok = GenerateThumbnailSTA(file);
Console.WriteLine($"{Path.GetFileName(file)} → {(ok ? "OK" : "Fail")}");
});
Console.WriteLine("全部縮圖建立完成!");
}
/// <summary>
/// 用 STA Thread 執行縮圖,避免 0x8000000A
/// </summary>
static bool GenerateThumbnailSTA(string file)
{
bool result = false;
var thread = new Thread(() =>
{
result = GenerateThumbnailWithRetry(file, 256, 256);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
return result;
}
static bool GenerateThumbnailWithRetry(string file, int width, int height, int retry = 3)
{
for (int i = 0; i < retry; i++)
{
try
{
WindowsThumbnailProvider.GetThumbnail(file, width, height);
return true;
}
catch (COMException ex)
{
if (ex.HResult == unchecked((int)0x8000000A)) // E_PENDING
{
Thread.Sleep(50); // 等一會兒再嘗試
continue;
}
return false;
}
catch
{
return false;
}
}
return false; // 重試仍失敗
}
這段程式碼會掃描指定資料夾的所有圖片,檢查副檔名後用 Parallel.ForEach 平行處理
每張圖都丟到 STA Thread 執行縮圖 避免 COM 出現 0x8000000A 的 E_PENDING 錯誤
若縮圖失敗就稍等後重試 最後把結果印出 目的就是提前建立 Windows 的縮圖快取 讓開資料夾時不用等縮圖生成
結果:
因為體感很難敘述或是感覺,所以我最後只能觀測
C:\Users\[用戶名稱]\AppData\Local\Microsoft\Windows\Explorer\thumbcache_256.db
是不是檔案有變大,然後,我發現當 thumbcache_256.db 大於 140MB 的時候,會開始出現錯誤
我問 GPT 他的猜測可能是我打到 Windows 回收機制了,為這我改寫很多次,看來目前最好的做法
可能是自己設計一個資料結構製作縮圖才會比較好管理照片,不過這次也讓我學到不少事情..
部分程式請 GPT 幫助寫成