web-dev-qa-db-fra.com

Obtenir l'icône de fichier utilisée par Shell

Dans .Net (C # ou VB: peu importe), étant donné une chaîne de chemin de fichier, une structure FileInfo ou une structure FileSystemInfo pour un fichier existant réel, comment puis-je déterminer les icônes utilisées par le Shell (Explorer) pour cela fichier?

Je ne prévois pas actuellement d'utiliser cela pour quoi que ce soit, mais je suis devenu curieux de savoir comment le faire en regardant cette question et j'ai pensé qu'il serait utile d'avoir archivé ici sur SO.

45
Joel Coehoorn
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
54
Stefan

Veuillez ignorer tous ceux qui vous disent d'utiliser le registre! Le registre n'est PAS UNE API. L'API que vous souhaitez est SHGetFileInfo avec SHGFI_ICON. Vous pouvez obtenir une signature P/Invoke ici:

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

16
user57200

Vous devez utiliser SHGetFileInfo.

Icon.ExtractAssociatedIcon fonctionne aussi bien que SHGetFileInfo dans la plupart des cas, mais SHGetFileInfo peut fonctionner avec des chemins UNC (par exemple un chemin réseau comme "\\ ComputerName\SharedFolder \"), contrairement à Icon.ExtractAssociatedIcon. Si vous avez besoin ou devez utiliser des chemins UNC, il serait préférable d'utiliser SHGetFileInfo au lieu de Icon.ExtractAssociatedIcon.

C'est un bon article CodeProject sur la façon d'utiliser SHGetFileInfo.

15
Zach Johnson

Rien de plus qu'une version C # de la réponse 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;
    }
}
6
damix911

Cela fonctionne pour moi dans mes projets, j'espère que cela aide quelqu'un.

C'est C # avec P/Invokes, cela fonctionnera jusqu'à présent sur les systèmes x86/x64 depuis 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
    }
}
4
k1ll3r8e

Le problème avec l'approche du Registre est que vous n'obtenez pas explicitement l'ID d'index d'icône. Parfois (sinon toutes les fois), vous obtenez une icône ResourceID qui est un alias utilisé par le développeur de l'application pour nommer l'emplacement de l'icône.

La méthode de registre implique donc que tous les développeurs utilisent des ResourceID qui sont identiques à l'ID d'index d'icône implicite (qui est basé sur zéro, absolu, déterministe).

Scannez l'emplacement du registre et vous verrez beaucoup de nombres négatifs, parfois même des références textuelles - c'est-à-dire pas l'ID d'index d'icône. Une méthode implicite semble meilleure car elle laisse le système d'exploitation faire le travail.

Je teste cette nouvelle méthode seulement maintenant, mais elle a du sens et, espérons-le, résoudra ce problème.

1
OnyxxOr

Si vous êtes uniquement intéressé par une icône pour une extension spécifique et si cela ne vous dérange pas de créer un fichier temporaire, vous pouvez suivre l'exemple affiché ici

Code 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;
    }
1
Default

Ce lien semble avoir quelques informations. Cela implique beaucoup de traversées de registre, mais cela semble faisable. Les exemples sont en C++

0
Jason Punyon
  • déterminer l'extension
  • dans le registre, accédez à "HKCR\.{extension}", lit la valeur par défaut (appelons-la filetype)
  • dans "HKCR\{filetype}\DefaultIcon", lisez la valeur par défaut: il s'agit du chemin d'accès au fichier icône (ou au fichier conteneur d'icônes, comme un fichier .exe avec une ressource icône intégrée)
  • si nécessaire, utilisez votre méthode préférée pour extraire la ressource icône du fichier mentionné

éditer/remonter des commentaires:

Si l'icône se trouve dans un fichier conteneur (c'est assez courant), il y aura un compteur après le chemin, comme ceci: "foo.exe,3". Cela signifie qu'il s'agit de l'icône numéro 4 (l'index est basé sur zéro) des icônes disponibles. Une valeur de ", 0" est implicite (et facultative). Si le compteur est 0 ou manquant, la première icône disponible sera utilisée par le Shell.

0
Tomalak