通过 ExtractIconEx 读取文件图标
参考(https://blog.csdn.net/bruce135lee/article/details/79790104),使用 ExtractIconEx 获取文件/扩展名图标,在实际使用过程中,会出现读取图标卡死情况;
经测试发现在文件少的目录不会出现卡死,但是在读取 c:\windows\system32 时,会出现卡死;
翻了MSDN,有提示需要通过 DestroyIcon 释放资源。
相对于参考资料,作以下修改:
1、添加参数:tcType 用于指定需要获取的是文件还是目录;(之前通过文件扩展名的方式获取有点问题,如果文件本身没扩展名的,会误认为是目录)
2、ExtractIconEx 声明中句柄列表使用 IntPtr 类型,方便后续判断;
3、获取图标资源之后,调用 DestroyIcon 释放资源;
4、读取注册表信息时,键值的数据中文件名部分可能会使用引号(如:"\"C:\Program Files\7-Zip\7z.rar\",1")而导致获取图片失败;因此:转为字符串数组后,去掉前后空格
5、直接读取文件中的图标,限定仅 .EXE和.ICO 才读取文件自带图标;原因:有些DLL中也带有图标资源,会导致DLL文件也提取到了自带图标
最终代码:
1 /// <summary> 2 /// 通过文件名称获取文件图标 3 /// </summary> 4 /// <param name="tcType">指定参数tcFullName的类型: FILE/DIR</param> 5 /// <param name="tcFullName">需要获取图片的全路径文件名</param> 6 /// <param name="tlIsLarge">是否获取大图标(32*32)</param> 7 /// <returns></returns> 8 private Icon GetIconByFileName(string tcType, string tcFullName, bool tlIsLarge = false) 9 { 10 Icon ico = null; 11 12 string fileType = tcFullName.Contains(".") ? tcFullName.Substring(tcFullName.LastIndexOf('.')).ToLower() : string.Empty; 13 14 RegistryKey regVersion = null; 15 string regFileType = null; 16 string regIconString = null; 17 string systemDirectory = Environment.SystemDirectory + "\\"; 18 IntPtr[] phiconLarge = new IntPtr[1]; 19 IntPtr[] phiconSmall = new IntPtr[1]; 20 IntPtr hIcon = IntPtr.Zero; 21 uint rst = 0; 22 23 if (tcType == "FILE") 24 { 25 //含图标的文件,优先使用文件中自带图标 26 if (".exe.ico".Contains(fileType)) 27 { 28 //文件名 图标索引 29 phiconLarge[0] = phiconSmall[0] = IntPtr.Zero; 30 rst = ExtractIconEx(tcFullName, 0, phiconLarge, phiconSmall, 1); 31 hIcon = tlIsLarge ? phiconLarge[0] : phiconSmall[0]; 32 ico = hIcon == IntPtr.Zero ? null : Icon.FromHandle(hIcon).Clone() as Icon; 33 if (phiconLarge[0] != IntPtr.Zero) DestroyIcon(phiconLarge[0]); 34 if (phiconSmall[0] != IntPtr.Zero) DestroyIcon(phiconSmall[0]); 35 if (ico != null) 36 { 37 return ico; 38 } 39 } 40 41 //通过文件扩展名读取图标 42 regVersion = Registry.ClassesRoot.OpenSubKey(fileType, false); 43 if (regVersion != null) 44 { 45 regFileType = regVersion.GetValue("") as string; 46 regVersion.Close(); 47 regVersion = Registry.ClassesRoot.OpenSubKey(regFileType + @"\DefaultIcon", false); 48 if (regVersion != null) 49 { 50 regIconString = regVersion.GetValue("") as string; 51 regVersion.Close(); 52 } 53 } 54 if (regIconString == null) 55 { 56 //没有读取到文件类型注册信息,指定为未知文件类型的图标 57 regIconString = systemDirectory + "shell32.dll,0"; 58 } 59 } 60 else 61 { 62 //直接指定为文件夹图标 63 regIconString = systemDirectory + "shell32.dll,3"; 64 } 65 66 string[] fileIcon = regIconString.Split(new char[] { ',' }); 67 //系统注册表中注册的标图不能直接提取,则返回可执行文件的通用图标 68 fileIcon = fileIcon.Length == 2 ? fileIcon : new string[] { systemDirectory + "shell32.dll", "2" }; 69 70 phiconLarge[0] = phiconSmall[0] = IntPtr.Zero; 71 rst = ExtractIconEx(fileIcon[0].Trim('\"'), Int32.Parse(fileIcon[1]), phiconLarge, phiconSmall, 1); 72 hIcon = tlIsLarge ? phiconLarge[0] : phiconSmall[0]; 73 ico = hIcon == IntPtr.Zero ? null : Icon.FromHandle(hIcon).Clone() as Icon; 74 if (phiconLarge[0] != IntPtr.Zero) DestroyIcon(phiconLarge[0]); 75 if (phiconSmall[0] != IntPtr.Zero) DestroyIcon(phiconSmall[0]); 76 if (ico != null) 77 { 78 return ico; 79 } 80 81 // 对于文件,如果提取文件图标失败,则重新使用可执行文件通用图标 82 if (tcType == "FILE") 83 { 84 //系统注册表中注册的标图不能直接提取,则返回可执行文件的通用图标 85 fileIcon = new string[] { systemDirectory + "shell32.dll", "2" }; 86 phiconLarge = new IntPtr[1]; 87 phiconSmall = new IntPtr[1]; 88 rst = ExtractIconEx(fileIcon[0], Int32.Parse(fileIcon[1]), phiconLarge, phiconSmall, 1); 89 hIcon = tlIsLarge ? phiconLarge[0] : phiconSmall[0]; 90 ico = hIcon == IntPtr.Zero ? null : Icon.FromHandle(hIcon).Clone() as Icon; 91 if (phiconLarge[0] != IntPtr.Zero) DestroyIcon(phiconLarge[0]); 92 if (phiconSmall[0] != IntPtr.Zero) DestroyIcon(phiconSmall[0]); 93 } 94 95 return ico; 96 } 97 98 [DllImport("shell32.dll", SetLastError = true)] 99 private static extern uint ExtractIconEx(string lpszFile, int nIconIndex, IntPtr[] phiconLarge, IntPtr[] phiconSmall, uint nIcons); 100 [DllImport("User32.dll", SetLastError = true)] 101 private static extern uint DestroyIcon(IntPtr phicon);