获取Shell使用的文件图标
在.Net(C#或VB:不关心),给定一个文件pathstring,FileInfo结构,或FileSystemInfo结构为一个真正的现有文件,我怎么能确定图标(S)使用的shell(资源pipe理器)文件?
目前我没有计划使用这个任何东西,但是当我看到这个问题时 ,我很好奇如何去做这件事,而且我认为在这里存档就会很有用。
Imports System.Drawing Module Module1 Sub Main() Dim filePath As String = "C:\myfile.exe" Dim TheIcon As Icon = IconFromFilePath(filePath) If TheIcon IsNot Nothing Then ''#Save it to disk, or do whatever you want with it. Using stream As New System.IO.FileStream("c:\myfile.ico", IO.FileMode.CreateNew) TheIcon.Save(stream) End Using End If End Sub Public Function IconFromFilePath(filePath As String) As Icon Dim result As Icon = Nothing Try result = Icon.ExtractAssociatedIcon(filePath) Catch ''# swallow and return nothing. You could supply a default Icon here as well End Try Return result End Function End Module
请忽略每个人告诉你使用registry! registry不是一个API。 你想要的API是带有SHGFI_ICON的SHGetFileInfo。 你可以在这里得到一个P / Invoke签名:
你应该使用SHGetFileInfo。
在大多数情况下,Icon.ExtractAssociatedIcon与SHGetFileInfo一样工作,但SHGetFileInfo可以与UNCpath(例如“\\ ComputerName \ SharedFolder \”之类的networkingpath)一起使用,而Icon.ExtractAssociatedIcon则不能。 如果您需要或可能需要使用UNCpath,最好使用SHGetFileInfo而不是Icon.ExtractAssociatedIcon。
这是很好的CodeProject文章如何使用SHGetFileInfo。
只不过是Stefan的答案的C#版本。
using System.Drawing; class Class1 { public static void Main() { var filePath = @"C:\myfile.exe"; var theIcon = IconFromFilePath(filePath); if (theIcon != null) { // Save it to disk, or do whatever you want with it. using (var stream = new System.IO.FileStream(@"c:\myfile.ico", System.IO.FileMode.CreateNew)) { theIcon.Save(stream); } } } public static Icon IconFromFilePath(string filePath) { var result = (Icon)null; try { result = Icon.ExtractAssociatedIcon(filePath); } catch (System.Exception) { // swallow and return nothing. You could supply a default Icon here as well } return result; } }
这在我的项目中适用于我,希望这可以帮助别人。
这是C#与P /调用它将工作到目前为止在x86 / x64系统自WinXP。
(Shell.cs)
using System; using System.Drawing; using System.IO; using System.Runtime.InteropServices; namespace IconExtraction { internal sealed class Shell : NativeMethods { #region OfExtension ///<summary> /// Get the icon of an extension ///</summary> ///<param name="filename">filename</param> ///<param name="overlay">bool symlink overlay</param> ///<returns>Icon</returns> public static Icon OfExtension(string filename, bool overlay = false) { string filepath; string[] extension = filename.Split('.'); string dirpath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "cache"); Directory.CreateDirectory(dirpath); if (String.IsNullOrEmpty(filename) || extension.Length == 1) { filepath = Path.Combine(dirpath, "dummy_file"); } else { filepath = Path.Combine(dirpath, String.Join(".", "dummy", extension[extension.Length - 1])); } if (File.Exists(filepath) == false) { File.Create(filepath); } Icon icon = OfPath(filepath, true, true, overlay); return icon; } #endregion #region OfFolder ///<summary> /// Get the icon of an extension ///</summary> ///<returns>Icon</returns> ///<param name="overlay">bool symlink overlay</param> public static Icon OfFolder(bool overlay = false) { string dirpath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "cache", "dummy"); Directory.CreateDirectory(dirpath); Icon icon = OfPath(dirpath, true, true, overlay); return icon; } #endregion #region OfPath ///<summary> /// Get the normal,small assigned icon of the given path ///</summary> ///<param name="filepath">physical path</param> ///<param name="small">bool small icon</param> ///<param name="checkdisk">bool fileicon</param> ///<param name="overlay">bool symlink overlay</param> ///<returns>Icon</returns> public static Icon OfPath(string filepath, bool small = true, bool checkdisk = true, bool overlay = false) { Icon clone; SHGFI_Flag flags; SHFILEINFO shinfo = new SHFILEINFO(); if (small) { flags = SHGFI_Flag.SHGFI_ICON | SHGFI_Flag.SHGFI_SMALLICON; } else { flags = SHGFI_Flag.SHGFI_ICON | SHGFI_Flag.SHGFI_LARGEICON; } if (checkdisk == false) { flags |= SHGFI_Flag.SHGFI_USEFILEATTRIBUTES; } if (overlay) { flags |= SHGFI_Flag.SHGFI_LINKOVERLAY; } if (SHGetFileInfo(filepath, 0, ref shinfo, Marshal.SizeOf(shinfo), flags) == 0) { throw (new FileNotFoundException()); } Icon tmp = Icon.FromHandle(shinfo.hIcon); clone = (Icon)tmp.Clone(); tmp.Dispose(); if (DestroyIcon(shinfo.hIcon) != 0) { return clone; } return clone; } #endregion } }
(NativeMethods.cs)
using System; using System.Drawing; using System.Runtime.InteropServices; namespace IconExtraction { internal class NativeMethods { 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; }; [DllImport("user32.dll")] public static extern int DestroyIcon(IntPtr hIcon); [DllImport("shell32.dll", CharSet = CharSet.Auto, BestFitMapping = false, ThrowOnUnmappableChar = true)] public static extern IntPtr ExtractIcon(IntPtr hInst, string lpszExeFileName, int nIconIndex); [DllImport("Shell32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true)] public static extern int SHGetFileInfo(string pszPath, int dwFileAttributes, ref SHFILEINFO psfi, int cbFileInfo, SHGFI_Flag uFlags); [DllImport("Shell32.dll")] public static extern int SHGetFileInfo(IntPtr pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, int cbFileInfo, SHGFI_Flag uFlags); } public enum SHGFI_Flag : uint { SHGFI_ATTR_SPECIFIED = 0x000020000, SHGFI_OPENICON = 0x000000002, SHGFI_USEFILEATTRIBUTES = 0x000000010, SHGFI_ADDOVERLAYS = 0x000000020, SHGFI_DISPLAYNAME = 0x000000200, SHGFI_EXETYPE = 0x000002000, SHGFI_ICON = 0x000000100, SHGFI_ICONLOCATION = 0x000001000, SHGFI_LARGEICON = 0x000000000, SHGFI_SMALLICON = 0x000000001, SHGFI_SHELLICONSIZE = 0x000000004, SHGFI_LINKOVERLAY = 0x000008000, SHGFI_SYSICONINDEX = 0x000004000, SHGFI_TYPENAME = 0x000000400 } }
registry方法的问题是,您没有明确地获取图标索引ID。 有时(如果不是所有时间),你会得到一个图标ResourceID,它是应用程序开发人员用来命名图标的插槽的别名。
因此,registry方法意味着所有开发人员使用与隐式图标索引ID(基于零,绝对,确定性)相同的ResourceID。
扫描registry位置,你会看到很多负数,有时甚至是文本引用 – 即不是图标索引ID。 隐含的方法似乎更好,因为它可以让操作系统完成工作。
现在只testing这个新的方法,但它是有道理的,并希望解决这个问题。
如果您只对特定扩展名的图标感兴趣,并且如果您不介意创build临时文件,则可以按照此处显示的示例进行操作
C#代码:
public Icon LoadIconFromExtension(string extension) { string path = string.Format("dummy{0}", extension); using (File.Create(path)) { } Icon icon = Icon.ExtractAssociatedIcon(path); File.Delete(path); return icon; }
这个链接似乎有一些信息。 它涉及很多registry遍历,但似乎可行。 这些例子是用C ++编写的
- 确定延伸
- 在registry中,转到
"HKCR\.{extension}"
,读取默认值(我们称之为filetype
) - 在
"HKCR\{filetype}\DefaultIcon"
,读取默认值:这是图标文件的path(或图标容器文件,如embedded图标资源的.exe) - 如果需要的话,使用您提到的文件中提取图标资源的首选方法
编辑/从意见上移:
如果图标位于容器文件中(这很常见),path后面会有一个计数器,如下所示: "foo.exe,3"
。 这意味着它是可用图标的图标编号4(索引从零开始)。 “,0”的值是隐含的(和可选的)。 如果计数器为0或丢失,则第一个可用图标将被shell使用。