在 Windows Forms 或 WPF 應用程式中顯示檔案系統圖示,需呼叫 Shell API SHGetFileInfo。本文提供完整的 P/Invoke 定義、旗標列舉與封裝方法,可直接取得大/小圖示 Icon 物件。
using System;
using System.Runtime.InteropServices;
using System.Drawing;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
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;
}
[Flags]
public enum SHGFI : uint
{
ICON = 0x000000100,
DISPLAYNAME = 0x000000200,
TYPENAME = 0x000000400,
ATTRIBUTES = 0x000000800,
ICONLOCATION = 0x000001000,
EXETYPE = 0x000002000,
SYSICONINDEX = 0x000004000,
LINKOVERLAY = 0x000008000,
SELECTED = 0x000010000,
ATTR_SPECIFIED = 0x000020000,
LARGEICON = 0x000000000,
SMALLICON = 0x000000001,
OPENICON = 0x000000002,
SHELLICONSIZE = 0x000000004,
PIDL = 0x000000008,
USEFILEATTRIBUTES = 0x000000010,
ADDOVERLAYS = 0x000000020,
OVERLAYINDEX = 0x000000040,
}
[Flags]
public enum FILE_ATTRIBUTE : uint
{
READONLY = 0x00000001,
HIDDEN = 0x00000002,
SYSTEM = 0x00000004,
DIRECTORY = 0x00000010,
ARCHIVE = 0x00000020,
DEVICE = 0x00000040,
NORMAL = 0x00000080,
TEMPORARY = 0x00000100,
SPARSE_FILE = 0x00000200,
REPARSE_POINT = 0x00000400,
COMPRESSED = 0x00000800,
OFFLINE = 0x00001000,
NOT_CONTENT_INDEXED = 0x00002000,
ENCRYPTED = 0x00004000,
}
public static class ShellApi
{
[DllImport("shell32.dll", CharSet = CharSet.Auto, EntryPoint = "SHGetFileInfo")]
public static extern IntPtr SHGetFileInfo(
string pszPath,
uint dwFileAttributes,
ref SHFILEINFO psfi,
uint cbFileInfo,
uint uFlags);
}
public static class FileIconHelper
{
public static Icon GetLargeIcon(string filePath)
{
SHFILEINFO info = new SHFILEINFO();
ShellApi.SHGetFileInfo(filePath, 0, ref info, (uint)Marshal.SizeOf(info),
(uint)(SHGFI.ICON | SHGFI.LARGEICON));
try
{
return (info.hIcon != IntPtr.Zero) ? Icon.FromHandle(info.hIcon) : null;
}
catch
{
return null;
}
}
public static Icon GetSmallIcon(string filePath)
{
SHFILEINFO info = new SHFILEINFO();
ShellApi.SHGetFileInfo(filePath, 0, ref info, (uint)Marshal.SizeOf(info),
(uint)(SHGFI.ICON | SHGFI.SMALLICON));
try
{
return (info.hIcon != IntPtr.Zero) ? Icon.FromHandle(info.hIcon) : null;
}
catch
{
return null;
}
}
// 進階:依副檔名取得圖示(檔案不需實際存在)
public static Icon GetIconByExtension(string extension, bool largeIcon)
{
SHFILEINFO info = new SHFILEINFO();
uint flags = (uint)(SHGFI.ICON | SHGFI.USEFILEATTRIBUTES |
(largeIcon ? SHGFI.LARGEICON : SHGFI.SMALLICON));
ShellApi.SHGetFileInfo(extension, (uint)FILE_ATTRIBUTE.NORMAL,
ref info, (uint)Marshal.SizeOf(info), flags);
return (info.hIcon != IntPtr.Zero) ? Icon.FromHandle(info.hIcon) : null;
}
}
// 取得實體檔案圖示
Icon large = FileIconHelper.GetLargeIcon(@"C:\Windows\notepad.exe");
Icon small = FileIconHelper.GetSmallIcon(@"C:\Windows\notepad.exe");
// 顯示於 PictureBox 或 TreeView
pictureBox1.Image = large?.ToBitmap();
// 僅依副檔名取得圖示(較快,不需檔案存在)
Icon docIcon = FileIconHelper.GetIconByExtension(".docx", true);
Icon.FromHandle 回傳的圖示在縮放時可能失真。建議使用 ImageList 搭配 SHGetFileInfo 取得系統影像清單索引(SHGFI.SYSICONINDEX),直接指派 ImageList 給控制項。Icon.FromHandle 建立的圖示需手動 DestroyIcon(P/Invoke user32.dll)釋放非受控資源,長期運行應用程式必須處理。Icon 或影像清單索引。SHGetImageList 取得高解析度系統影像清單。