Obter ícone de arquivo usado pelo Shell

Em .Net (C # ou VB: não importa), dada uma string de caminho de arquivo, FileInfo struct ou FileSystemInfo struct para um arquivo real existente, como posso determinar o (s) ícone (s) usado (s) pelo shell (explorer)? Arquivo?

No momento, não estou planejando usar isso para nada, mas fiquei curioso sobre como fazer isso ao analisar essa questão e achei que seria útil ter arquivado aqui no 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 

Por favor, ignore todos dizendo para você usar o registro! O registro não é uma API. A API que você deseja é SHGetFileInfo com SHGFI_ICON. Você pode obter uma assinatura P / Invoke aqui:

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

Você deve usar o SHGetFileInfo.

Icon.ExtractAssociatedIcon funciona tão bem quanto SHGetFileInfo na maioria dos casos, mas SHGetFileInfo pode trabalhar com caminhos UNC (por exemplo, um caminho de rede como “\\ ComputerName \ SharedFolder \”) enquanto Icon.ExtractAssociatedIcon não pode. Se você precisar ou precisar usar caminhos UNC, seria melhor usar SHGetFileInfo em vez de Icon.ExtractAssociatedIcon.

Este é um bom artigo do CodeProject sobre como usar o SHGetFileInfo.

Nada mais do que uma versão em C # da resposta 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; } } 

Isso funciona para mim em meus projetos, espero que isso ajude alguém.

É o C # com P / Invokes que funcionará até agora em sistemas x86 / x64 desde o 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 } } 

O problema com a abordagem do registro é que você não está obtendo explicitamente o id do índice do ícone. Às vezes (se não todas as vezes), você obtém um ícone ResourceID, que é um alias usado pelo desenvolvedor do aplicativo para nomear o slot do ícone.

Portanto, o método de registro implica que todos os desenvolvedores usem ResourceIDs, que são os mesmos que o ID de índice de ícone implícito (que é baseado em zero, absoluto, determinístico).

Escaneie o local do registro e você verá muitos números negativos, às vezes até referências de texto – ou seja, não o ID do índice do ícone. Um método implícito parece melhor, pois permite que o SO faça o trabalho.

Apenas testando esse novo método agora, mas faz sentido e, espera-se, resolve esse problema.

Se você estiver interessado apenas em um ícone para uma extensão específica e se não se importar em criar um arquivo temporário, siga o exemplo exibido aqui

Código 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; } 

Este link parece ter alguma informação. Envolve muito a travessia do registro, mas parece factível. Os exemplos estão em C ++

  • determinar extensão
  • no registro, vá para "HKCR\.{extension}" , leia o valor padrão (vamos chamá-lo filetype )
  • em "HKCR\{filetype}\DefaultIcon" , leia o valor padrão: este é o caminho para o arquivo de ícone (ou arquivo contêiner de ícone, como um .exe com um recurso de ícone incorporado)
  • se necessário, use seu método preferido de extrair o recurso de ícone do arquivo mencionado

editar / movido dos comentários:

Se o ícone estiver em um arquivo contêiner (isso é bastante comum), haverá um contador após o caminho, como este: "foo.exe,3" . Isso significa que é o ícone número 4 (o índice é baseado em zero) dos icons disponíveis. Um valor de “, 0” é implícito (e opcional). Se o contador for 0 ou ausente, o primeiro ícone disponível será usado pelo shell.