Obtener el icono de archivo utilizado por Shell

En .Net (C # o VB: no me importa), dada una cadena de ruta de archivo, estructura de FileInfo o estructura de FileSystemInfo para un archivo real existente, ¿cómo puedo determinar los íconos utilizados por el shell (explorador) para ese ¿archivo?

Actualmente no estoy planeando usar esto para nada, pero sentí curiosidad acerca de cómo hacerlo al mirar esta pregunta y pensé que sería útil tener archivado aquí en SO.

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 

¡Ignore a todos los que le digan que use el registro! El registro NO ES UNA API. La API que desea es SHGetFileInfo con SHGFI_ICON. Puede obtener una firma de P / Invoke aquí:

http://www.pinvoke.net/default.aspx/shell32.SHGetFileInfo

Debería usar SHGetFileInfo.

Icon.ExtractAssociatedIcon funciona tan bien como SHGetFileInfo en la mayoría de los casos, pero SHGetFileInfo puede funcionar con rutas UNC (por ejemplo, una ruta de red como “\\ ComputerName \ SharedFolder \”) mientras que Icon.ExtractAssociatedIcon no puede hacerlo. Si necesita o podría necesitar utilizar rutas UNC, sería mejor utilizar SHGetFileInfo en lugar de Icon.ExtractAssociatedIcon.

Este es un buen artículo de CodeProject sobre cómo usar SHGetFileInfo.

Nada más que una versión C # de la respuesta de Stefan.

 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; } } 

Esto funciona para mí en mis proyectos, espero que esto ayude a alguien.

Es C # con P / Invoca que funcionará hasta ahora en sistemas x86 / x64 desde WinXP.

(Shell.cs)

 using System; using System.Drawing; using System.IO; using System.Runtime.InteropServices; namespace IconExtraction { internal sealed class Shell : NativeMethods { #region OfExtension /// /// Get the icon of an extension /// ///filename ///bool symlink overlay ///Icon 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 /// /// Get the icon of an extension /// ///Icon ///bool symlink overlay 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 /// /// Get the normal,small assigned icon of the given path /// ///physical path ///bool small icon ///bool fileicon ///bool symlink overlay ///Icon 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 } } 

El problema con el enfoque de registro es que no obtiene explícitamente el ícono de identificación del índice. Algunas veces (si no todas las veces), obtienes un ícono ResourceID que es un alias que el desarrollador de la aplicación usó para nombrar el espacio del ícono.

Por lo tanto, el método de registro implica que todos los desarrolladores utilicen ResourceID, que son los mismos que el ícono del ícono índico (que es cero, absoluto, determinista).

Escanee la ubicación del registro y verá muchos números negativos, a veces incluso referencias de texto, es decir, no la id del índice del icono. Un método implícito parece mejor, ya que permite que el SO haga el trabajo.

Solo estoy probando este nuevo método ahora pero tiene sentido y con suerte resuelve este problema.

Si solo está interesado en un icono para una extensión específica y si no le importa crear un archivo temporal, puede seguir el ejemplo que se muestra aquí

C # code:

  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; } 

Este enlace parece tener algo de información. Implica una gran cantidad de registros atravesados, pero parece factible. Los ejemplos están en C ++

  • determinar la extensión
  • en el registro, vaya a "HKCR\.{extension}" , lea el valor predeterminado (vamos a llamarlo filetype )
  • en "HKCR\{filetype}\DefaultIcon" , lea el valor predeterminado: esta es la ruta al archivo de icono (o archivo de contenedor de icono, como un .exe con un recurso de icono incrustado)
  • si es necesario, use su método preferido para extraer el recurso del icono del archivo mencionado

edit / movido desde los comentarios:

Si el icono está en un archivo contenedor (esto es bastante común), habrá un contador después de la ruta, como este: "foo.exe,3" . Esto significa que es el ícono número 4 (el índice está basado en cero) de los íconos disponibles. Un valor de “, 0” es implícito (y opcional). Si el contador es 0 o falta, el icono disponible del puño será utilizado por el shell.