如何通过ExtractIconEx获取shell32.dll中256x256的PNG格式图标?
提取shell32.dll中256x256 PNG格式图标解决方案
问题根源
ExtractIconEx是老旧的Windows API,仅支持提取传统BMP格式的图标,无法识别shell32.dll中嵌入的PNG压缩高分辨率图标,因此默认返回64x64的BMP版本。
方案1:通过系统图标列表获取高分辨率图标
利用SHGetImageList获取系统256x256图标列表(SHIL_JUMBO),直接从中提取目标索引的图标,系统会自动返回PNG格式的高分辨率版本。
示例代码:
using System; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; public class ShellIconExtractor { [DllImport("shell32.dll")] private static extern int SHGetImageList(int iImageList, ref Guid riid, out IImageList ppv); [Guid("46EB5926-582E-4017-9FDF-E8998DAA0950")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IImageList { [PreserveSig] int GetIcon(int i, int flags, out IntPtr picon); } [DllImport("user32.dll")] private static extern bool DestroyIcon(IntPtr hIcon); private const int SHIL_JUMBO = 0x4; // 对应256x256图标列表 private const int ILD_TRANSPARENT = 0x1; public ImageSource Get256x256ShellIcon(int iconIndex) { Guid iidImageList = typeof(IImageList).GUID; if (SHGetImageList(SHIL_JUMBO, ref iidImageList, out IImageList imageList) != 0) return null; imageList.GetIcon(iconIndex, ILD_TRANSPARENT, out IntPtr hIcon); if (hIcon == IntPtr.Zero) return null; ImageSource source = Imaging.CreateBitmapSourceFromHIcon( hIcon, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); DestroyIcon(hIcon); return source; } }
方案2:直接读取shell32.dll中的PNG资源
shell32.dll内的256x256图标以独立PNG资源存储,可通过资源读取API直接提取原始PNG数据,再转换为ImageSource。
示例代码:
using System; using System.IO; using System.Runtime.InteropServices; using System.Windows.Media.Imaging; public class ShellPngExtractor { [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr FindResource(IntPtr hModule, int lpName, string lpType); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr LockResource(IntPtr hResData); [DllImport("kernel32.dll", SetLastError = true)] private static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FreeLibrary(IntPtr hModule); private const uint LOAD_LIBRARY_AS_DATAFILE = 0x8; public ImageSource GetPngIconFromShell32(int iconIndex) { string shellPath = Path.Combine(Environment.SystemDirectory, "shell32.dll"); IntPtr hModule = LoadLibraryEx(shellPath, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE); if (hModule == IntPtr.Zero) return null; IntPtr hResInfo = FindResource(hModule, iconIndex, "PNG"); if (hResInfo == IntPtr.Zero) { FreeLibrary(hModule); return null; } IntPtr hResData = LoadResource(hModule, hResInfo); IntPtr pResData = LockResource(hResData); uint resourceSize = SizeofResource(hModule, hResInfo); byte[] pngBuffer = new byte[resourceSize]; Marshal.Copy(pResData, pngBuffer, 0, (int)resourceSize); FreeLibrary(hModule); using (MemoryStream ms = new MemoryStream(pngBuffer)) { return BitmapFrame.Create(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad); } } }
注意事项
- 方案1依赖系统图标列表,自动适配高DPI环境,适合常规场景
- 方案2直接读取原始PNG资源,需注意shell32.dll中图标索引与PNG资源ID的对应关系(多数情况下索引与ID一致)
- 所有获取的图标句柄必须调用
DestroyIcon释放,避免内存泄漏
内容的提问来源于stack exchange,提问作者Ross
相关产品推荐
相关产品推荐

