获取给定扩展名的图标

29

我知道可以使用以下方法提取文件的图标:

using (System.Drawing.Icon sysicon = System.Drawing.Icon.ExtractAssociatedIcon(filePath))
{
    icon = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(
        sysicon.Handle,
        System.Windows.Int32Rect.Empty,
        System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}

但是如果没有文件,我如何获取特定扩展名的图标呢?


你的意思是使用Windows分配给新文件的默认图标吗?如果是这种情况,它取决于文件扩展名。请提供更多关于你想要的细节。 - Mariano Desanze
可能不是最优雅的解决方案(实际上是新手解决方案),但我所做的是创建一个带有所需扩展名的临时空文件,然后使用您上面展示的方法获取该文件的图标,最后将其删除。这样,您就无需访问注册表,也不需要进入Win32相关内容。 - Matheus Rocha
4个回答

38

使用这篇来自Paul Ingles的CodeProject文章中的GetFileIcon方法,并将.ext作为name参数传递。

GetFileIcon方法是对本地SHGetFileInfo的包装器,这里进行演示:

public static System.Drawing.Icon GetFileIcon(string name, IconSize size, 
                                              bool linkOverlay)
{
    Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
    uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES;

    if (true == linkOverlay) flags += Shell32.SHGFI_LINKOVERLAY;


    /* Check the size specified for return. */
    if (IconSize.Small == size)
    {
        flags += Shell32.SHGFI_SMALLICON ; // include the small icon flag
    } 
    else 
    {
        flags += Shell32.SHGFI_LARGEICON ;  // include the large icon flag
    }

    Shell32.SHGetFileInfo( name, 
        Shell32.FILE_ATTRIBUTE_NORMAL, 
        ref shfi, 
        (uint) System.Runtime.InteropServices.Marshal.SizeOf(shfi), 
        flags );


    // Copy (clone) the returned icon to a new object, thus allowing us 
    // to call DestroyIcon immediately
    System.Drawing.Icon icon = (System.Drawing.Icon)
                         System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();
    User32.DestroyIcon( shfi.hIcon ); // Cleanup
    return icon;
}

4
目前32x32像素的作品已经不再适用于任何事情了。 - Ben Philipp
1
这个答案看起来非常全面。顺便感谢提供代码。这对扩展也适用吗?还是需要特定的文件类型? - demoncrate

27

以下代码也基于Paul Ingles的解决方案,但是:

  • 可用于WPF (ImageSource而不是Icon)
  • 具有简单的缓存
  • 删除了与目录相关的所有内容(我只有文件)
  • 使用R#提示进行重构,并封装为单个类的简单API

我在Windows 7Windows XP SP3上进行了测试,它可以正常工作并接受任何字符串作为fileName

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;

/// <summary>
/// Internals are mostly from here: http://www.codeproject.com/Articles/2532/Obtaining-and-managing-file-and-folder-icons-using
/// Caches all results.
/// </summary>
public static class IconManager
{
    private static readonly Dictionary<string, ImageSource> _smallIconCache = new Dictionary<string, ImageSource>();
    private static readonly Dictionary<string, ImageSource> _largeIconCache = new Dictionary<string, ImageSource>();
    /// <summary>
    /// Get an icon for a given filename
    /// </summary>
    /// <param name="fileName">any filename</param>
    /// <param name="large">16x16 or 32x32 icon</param>
    /// <returns>null if path is null, otherwise - an icon</returns>
    public static ImageSource FindIconForFilename(string fileName, bool large)
    {
        var extension = Path.GetExtension(fileName);
        if (extension == null)
            return null;
        var cache = large ? _largeIconCache : _smallIconCache;
        ImageSource icon;
        if (cache.TryGetValue(extension, out icon))
            return icon;
        icon = IconReader.GetFileIcon(fileName, large ? IconReader.IconSize.Large : IconReader.IconSize.Small, false).ToImageSource();
        cache.Add(extension, icon);
        return icon;
    }
    /// <summary>
    /// https://dev59.com/6XNA5IYBdhLWcg3wAI3e#6580799
    /// </summary>
    static ImageSource ToImageSource(this Icon icon)
    {
        var imageSource = Imaging.CreateBitmapSourceFromHIcon(
            icon.Handle,
            Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions());
        return imageSource;
    }
    /// <summary>
    /// Provides static methods to read system icons for both folders and files.
    /// </summary>
    /// <example>
    /// <code>IconReader.GetFileIcon("c:\\general.xls");</code>
    /// </example>
    static class IconReader
    {
        /// <summary>
        /// Options to specify the size of icons to return.
        /// </summary>
        public enum IconSize
        {
            /// <summary>
            /// Specify large icon - 32 pixels by 32 pixels.
            /// </summary>
            Large = 0,
            /// <summary>
            /// Specify small icon - 16 pixels by 16 pixels.
            /// </summary>
            Small = 1
        }
        /// <summary>
        /// Returns an icon for a given file - indicated by the name parameter.
        /// </summary>
        /// <param name="name">Pathname for file.</param>
        /// <param name="size">Large or small</param>
        /// <param name="linkOverlay">Whether to include the link icon</param>
        /// <returns>System.Drawing.Icon</returns>
        public static Icon GetFileIcon(string name, IconSize size, bool linkOverlay)
        {
            var shfi = new Shell32.Shfileinfo();
            var flags = Shell32.ShgfiIcon | Shell32.ShgfiUsefileattributes;
            if (linkOverlay) flags += Shell32.ShgfiLinkoverlay;
            /* Check the size specified for return. */
            if (IconSize.Small == size)
                flags += Shell32.ShgfiSmallicon;
            else
                flags += Shell32.ShgfiLargeicon;
            Shell32.SHGetFileInfo(name,
                Shell32.FileAttributeNormal,
                ref shfi,
                (uint)Marshal.SizeOf(shfi),
                flags);
            // Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly
            var icon = (Icon)Icon.FromHandle(shfi.hIcon).Clone();
            User32.DestroyIcon(shfi.hIcon);     // Cleanup
            return icon;
        }
    }
    /// <summary>
    /// Wraps necessary Shell32.dll structures and functions required to retrieve Icon Handles using SHGetFileInfo. Code
    /// courtesy of MSDN Cold Rooster Consulting case study.
    /// </summary>
    static class Shell32
    {
        private const int MaxPath = 256;
        [StructLayout(LayoutKind.Sequential)]
        public struct Shfileinfo
        {
            private const int Namesize = 80;
            public readonly IntPtr hIcon;
            private readonly int iIcon;
            private readonly uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxPath)]
            private readonly string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = Namesize)]
            private readonly string szTypeName;
        };
        public const uint ShgfiIcon = 0x000000100;     // get icon
        public const uint ShgfiLinkoverlay = 0x000008000;     // put a link overlay on icon
        public const uint ShgfiLargeicon = 0x000000000;     // get large icon
        public const uint ShgfiSmallicon = 0x000000001;     // get small icon
        public const uint ShgfiUsefileattributes = 0x000000010;     // use passed dwFileAttribute
        public const uint FileAttributeNormal = 0x00000080;
        [DllImport("Shell32.dll")]
        public static extern IntPtr SHGetFileInfo(
            string pszPath,
            uint dwFileAttributes,
            ref Shfileinfo psfi,
            uint cbFileInfo,
            uint uFlags
            );
    }
    /// <summary>
    /// Wraps necessary functions imported from User32.dll. Code courtesy of MSDN Cold Rooster Consulting example.
    /// </summary>
    static class User32
    {
        /// <summary>
        /// Provides access to function required to delete handle. This method is used internally
        /// and is not required to be called separately.
        /// </summary>
        /// <param name="hIcon">Pointer to icon handle.</param>
        /// <returns>N/A</returns>
        [DllImport("User32.dll")]
        public static extern int DestroyIcon(IntPtr hIcon);
    }
}

这个能够使用巨型图标吗? - David Zech
@Nighthawk441 我不确定什么是Jumbo Icon(大图标?)。你最好自己试一下。 - astef
1
记录一下:不,SHGetFileInfo只能返回最大32x32像素的图标。 - Ben Philipp
理想情况下,应该将StringComparer.OrdinalIgnoreCase传递给图像构造函数,以便文件扩展名比较不区分大小写。 - Kirill Osenkov
偶尔会抛出“System.ArgumentException: 'Win32 handle that was passed to Icon is not valid or is the wrong type.'”异常。不可靠。 - antikbd

21

这件事可以更简单地完成:

System.Drawing.Icon.ExtractAssociatedIcon("<fullPath>");

重要提示:如果文件不存在,则会抛出一个异常,因此值得添加验证。


4
添加一条注释以说明文件必须存在,但不必有效 - Icon.ExtractAssociatedIcon(@"c:\temp\this_is_actually_a_text_file.xls"); 将返回正确的 Excel 图标,即使该文件只是一个文本文件。 - stuartd
2
还有一件需要注意的事情是,尽管文档表明fullPath是一个UNC路径,在我的测试中,即使File.Exists()确认文件存在,调用仍会在位于网络共享上的文件上抛出异常。 - Arieleo
只是想补充一下,这也会修改文件的LastAccessTime;/我正在寻找一种提取图标但不修改访问时间的方法。 - IronHide
@stuartd,Windows也是这样的.... :) OP要求按扩展名显示图标,而不是按实际文件类型 - 我猜这根本不可能 :) - dba
在我的UseCase中,我只有扩展名本身,所以我创建了一个临时文件,并再次使用我的扩展名保存它(如果有很多条目要迭代,我承认这将是一种负担...)。然而,在调用ExtractAssociatedIcon之后,我无法直接调用IO.File.Delete(),因为该文件仍在使用中。因此,请注意这种情况,如果使用这种方式,则需要进行额外的清理。 :) - dba

0

像@astef一样,我也需要在WPF中使用它,但是我确实需要文件夹的图标。 由于驱动器和文件夹没有扩展名,因此我使用DirectoryInfo来确定哪一个是哪一个。

对于文件夹,我始终使用其关闭的图标,但是您可以轻松地将其扩展到考虑打开或关闭。

/// <summary>
/// Internals are mostly from here: http://www.codeproject.com/Articles/2532/Obtaining-and-managing-file-and-folder-icons-using
/// Caches all results.
/// </summary>
public static class IconManager
{
    private static readonly Dictionary<string, ImageSource> _smallIconCache = new Dictionary<string, ImageSource>();
    private static readonly Dictionary<string, ImageSource> _largeIconCache = new Dictionary<string, ImageSource>();
    /// <summary>
    /// Get an icon for a given filename
    /// </summary>
    /// <param name="fileName">any filename</param>
    /// <param name="large">16x16 or 32x32 icon</param>
    /// <returns>null if path is null, otherwise - an icon</returns>
    public static ImageSource FindIconForFilename(string fileName, bool large)
    {
        DirectoryInfo di = new DirectoryInfo(fileName);
        string extension = di.Extension;
        if (extension == null)
            return null;
        if (extension == string.Empty && di.Root.FullName == di.FullName) {
            extension = "ROOT";
        }
        if (extension == string.Empty && di.Attributes.HasFlag(FileAttributes.Directory)) {
            extension = "FOLDER";
        } else {
            if (di.Attributes.HasFlag(FileAttributes.Directory) && extension != "ROOT") {
                extension = "FOLDER";
            }
        }
        var cache = large ? _largeIconCache : _smallIconCache;
        ImageSource icon;
        if (cache.TryGetValue(extension, out icon))
            return icon;

        if (di.Attributes.HasFlag(FileAttributes.Directory) && extension != "ROOT") {
            icon = IconReader.GetFolderIcon(fileName, large ? IconReader.IconSize.Large : IconReader.IconSize.Small, IconReader.FolderType.Closed).ToImageSource();
        } else {
            icon = IconReader.GetFileIcon(fileName, large ? IconReader.IconSize.Large : IconReader.IconSize.Small, false).ToImageSource();
        }
        if (extension != "ROOT") cache.Add(extension, icon);
        return icon;
    }
    /// <summary>
    /// https://dev59.com/6XNA5IYBdhLWcg3wAI3e#6580799
    /// </summary>
    static ImageSource ToImageSource(this System.Drawing.Icon icon)
    {
        var imageSource = Imaging.CreateBitmapSourceFromHIcon(
            icon.Handle,
            Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions());
        return imageSource;
    }
    /// <summary>
    /// Provides static methods to read system icons for both folders and files.
    /// </summary>
    /// <example>
    /// <code>IconReader.GetFileIcon("c:\\general.xls");</code>
    /// </example>
    static class IconReader
    {
        /// <summary>
        /// Options to specify the size of icons to return.
        /// </summary>
        public enum IconSize
        {
            /// <summary>
            /// Specify large icon - 32 pixels by 32 pixels.
            /// </summary>
            Large = 0,
            /// <summary>
            /// Specify small icon - 16 pixels by 16 pixels.
            /// </summary>
            Small = 1
        }
        /// <summary>
        /// Returns an icon for a given file - indicated by the name parameter.
        /// </summary>
        /// <param name="name">Pathname for file.</param>
        /// <param name="size">Large or small</param>
        /// <param name="linkOverlay">Whether to include the link icon</param>
        /// <returns>System.Drawing.Icon</returns>
        public static System.Drawing.Icon GetFileIcon(string name, IconSize size, bool linkOverlay)
        {
            var shfi = new Shell32.SHFILEINFO();
            var flags = Shell32.SHGFI_ICON;
            if (linkOverlay) flags += Shell32.SHGFI_LINKOVERLAY;
            /* Check the size specified for return. */
            if (IconSize.Small == size)
                flags += Shell32.SHGFI_SMALLICON;
            else
                flags += Shell32.SHGFI_SMALLICON;
            Shell32.SHGetFileInfo(name,
                Shell32.FILE_ATTRIBUTE_NORMAL,
                ref shfi,
                (uint)Marshal.SizeOf(shfi),
                flags);
            // Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly
            var icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();
            User32.DestroyIcon(shfi.hIcon);     // Cleanup
            return icon;
        }
        /// <summary>
        /// Options to specify whether folders should be in the open or closed state.
        /// </summary>
        public enum FolderType
        {
            /// <summary>
            /// Specify open folder.
            /// </summary>
            Open = 0,
            /// <summary>
            /// Specify closed folder.
            /// </summary>
            Closed = 1
        }
        /// <summary>
        /// Used to access system folder icons.
        /// </summary>
        /// <param name="size">Specify large or small icons.</param>
        /// <param name="folderType">Specify open or closed FolderType.</param>
        /// <returns>System.Drawing.Icon</returns>
        public static System.Drawing.Icon GetFolderIcon(string name, IconSize size, FolderType folderType)
        {
            // Need to add size check, although errors generated at present!
            uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_SYSICONINDEX;

            if (FolderType.Open == folderType) {
                flags += Shell32.SHGFI_OPENICON;
            }

            if (IconSize.Small == size) {
                flags += Shell32.SHGFI_SMALLICON;
            } else {
                flags += Shell32.SHGFI_LARGEICON;
            }

            // Get the folder icon
            Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
            Shell32.SHGetFileInfo(name,
                Shell32.FILE_ATTRIBUTE_DIRECTORY,
                ref shfi,
                (uint)System.Runtime.InteropServices.Marshal.SizeOf(shfi),
                flags);

            System.Drawing.Icon.FromHandle(shfi.hIcon); // Load the icon from an HICON handle

            // Now clone the icon, so that it can be successfully stored in an ImageList
            System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();

            User32.DestroyIcon(shfi.hIcon);     // Cleanup
            return icon;
        }
    }
}
/// <summary>
/// Wraps necessary Shell32.dll structures and functions required to retrieve Icon Handles using SHGetFileInfo. Code
/// courtesy of MSDN Cold Rooster Consulting case study.
/// </summary>
public class Shell32
{

    public const int MAX_PATH = 256;
    [StructLayout(LayoutKind.Sequential)]
    public struct SHITEMID
    {
        public ushort cb;
        [MarshalAs(UnmanagedType.LPArray)]
        public byte[] abID;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct ITEMIDLIST
    {
        public SHITEMID mkid;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct BROWSEINFO
    {
        public IntPtr hwndOwner;
        public IntPtr pidlRoot;
        public IntPtr pszDisplayName;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string lpszTitle;
        public uint ulFlags;
        public IntPtr lpfn;
        public int lParam;
        public IntPtr iImage;
    }

    // Browsing for directory.
    public const uint BIF_RETURNONLYFSDIRS = 0x0001;
    public const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
    public const uint BIF_STATUSTEXT = 0x0004;
    public const uint BIF_RETURNFSANCESTORS = 0x0008;
    public const uint BIF_EDITBOX = 0x0010;
    public const uint BIF_VALIDATE = 0x0020;
    public const uint BIF_NEWDIALOGSTYLE = 0x0040;
    public const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
    public const uint BIF_BROWSEINCLUDEURLS = 0x0080;
    public const uint BIF_BROWSEFORCOMPUTER = 0x1000;
    public const uint BIF_BROWSEFORPRINTER = 0x2000;
    public const uint BIF_BROWSEINCLUDEFILES = 0x4000;
    public const uint BIF_SHAREABLE = 0x8000;

    [StructLayout(LayoutKind.Sequential)]
    public struct SHFILEINFO
    {
        public const int NAMESIZE = 80;
        public IntPtr hIcon;
        public int iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)]
        public string szTypeName;
    };

    public const uint SHGFI_ICON = 0x000000100;     // get icon
    public const uint SHGFI_DISPLAYNAME = 0x000000200;     // get display name
    public const uint SHGFI_TYPENAME = 0x000000400;     // get type name
    public const uint SHGFI_ATTRIBUTES = 0x000000800;     // get attributes
    public const uint SHGFI_ICONLOCATION = 0x000001000;     // get icon location
    public const uint SHGFI_EXETYPE = 0x000002000;     // return exe type
    public const uint SHGFI_SYSICONINDEX = 0x000004000;     // get system icon index
    public const uint SHGFI_LINKOVERLAY = 0x000008000;     // put a link overlay on icon
    public const uint SHGFI_SELECTED = 0x000010000;     // show icon in selected state
    public const uint SHGFI_ATTR_SPECIFIED = 0x000020000;     // get only specified attributes
    public const uint SHGFI_LARGEICON = 0x000000000;     // get large icon
    public const uint SHGFI_SMALLICON = 0x000000001;     // get small icon
    public const uint SHGFI_OPENICON = 0x000000002;     // get open icon
    public const uint SHGFI_SHELLICONSIZE = 0x000000004;     // get shell size icon
    public const uint SHGFI_PIDL = 0x000000008;     // pszPath is a pidl
    public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;     // use passed dwFileAttribute
    public const uint SHGFI_ADDOVERLAYS = 0x000000020;     // apply the appropriate overlays
    public const uint SHGFI_OVERLAYINDEX = 0x000000040;     // Get the index of the overlay

    public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
    public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;

    [DllImport("Shell32.dll")]
    public static extern IntPtr SHGetFileInfo(
                  string pszPath,
                  uint dwFileAttributes,
                  ref SHFILEINFO psfi,
                  uint cbFileInfo,
                  uint uFlags
                  );
}
/// <summary>
/// Wraps necessary functions imported from User32.dll. Code courtesy of MSDN Cold Rooster Consulting example.
/// </summary>
static class User32
{
    /// <summary>
    /// Provides access to function required to delete handle. This method is used internally
    /// and is not required to be called separately.
    /// </summary>
    /// <param name="hIcon">Pointer to icon handle.</param>
    /// <returns>N/A</returns>
    [DllImport("User32.dll")]
    public static extern int DestroyIcon(IntPtr hIcon);
}

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接