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.
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
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:
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.
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;
}
}
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
}
}
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.
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;
}
Ce lien semble avoir quelques informations. Cela implique beaucoup de traversées de registre, mais cela semble faisable. Les exemples sont en C++
"HKCR\.{extension}"
, lit la valeur par défaut (appelons-la filetype
)"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)é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.