通过 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 ///
2 /// 通过文件名称获取文件图标
3 ///
4 /// 指定参数tcFullName的类型: FILE/DIR
5 /// 需要获取图片的全路径文件名
6 /// 是否获取大图标(32*32)
7 ///
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);
View Code