shgetfileinfo是获取windows原生文件图标的最可靠方式,支持多尺寸、叠加层及真实shell渲染效果;需用icon.fromhandle转换并手动destroyicon释放,路径须绝对化,标志位需完整设置。
用 SHGetFileInfo 获取文件图标句柄最可靠
Windows 原生图标(包括不同大小、状态)必须通过 Shell API 提取,.NET 自带的 Icon.ExtractAssociatedIcon 只能拿到小尺寸(16×16)且常缓存过期,对快捷方式、注册表关联缺失的文件经常返回默认图标。SHGetFileInfo 是唯一能按需获取 16/32/48/256px 图标、叠加层(如快捷方式箭头、加密锁)、以及真实 Shell 渲染效果的途径。
关键点:
-
SHGetFileInfo返回的是 GDIHICON,需用Icon.FromHandle转为托管Icon,但注意:该句柄**不能直接释放**,否则图标立刻失效;应调用DestroyIcon手动清理(尤其在循环加载大量图标时) - 必须传入
SHGFI_ICON | SHGFI_LARGEICON或SHGFI_SMALLICON控制尺寸,仅传SHGFI_ICON默认返回小图标 - 路径必须是**绝对路径**,相对路径或 UNC 路径(如
\servershareile.txt)需先用Path.GetFullPath规范化,否则返回空图标
SHGetFileInfo 的典型调用参数组合
以下是最常用且稳定的参数组合(C# P/Invoke):
[DllImport("shell32.dll")]
public static extern IntPtr SHGetFileInfo(
string pszPath,
uint dwFileAttributes,
out SHFILEINFO psfi,
uint cbSizeFileInfo,
uint uFlags);
<p>[StructLayout(LayoutKind.Sequential)]
public struct SHFILEINFO {
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
}</p><p>// 示例:获取 32×32 图标(含叠加层)
var shinfo = new SHFILEINFO();
var flags = (uint)(0x000000100 | // SHGFI_ICON
0x000000040 | // SHGFI_LARGEICON (32×32)
0x000000002 | // SHGFI_USEFILEATTRIBUTES (无文件时可用属性模拟)
0x000000010); // SHGFI_OVERLAYINDEX (需要叠加层索引)</p><p>SHGetFileInfo(filePath, 0, out shinfo, (uint)Marshal.SizeOf(shinfo), flags);
if (shinfo.hIcon != IntPtr.Zero) {
var icon = Icon.FromHandle(shinfo.hIcon);
// 使用 icon...
}
注意:SHGFI_USEFILEATTRIBUTES 允许传入不存在的路径(比如只给扩展名),此时需手动指定 dwFileAttributes(如 FileAttributes.Directory),否则图标为空。