18

This SO question on getting icons for common file types 中所见,Windows 程序很可能使用 C++ Shell API 获取已注册文件类型的图标。这些图标可能存在也可能不存在于磁盘上——例如,我们想要制作自己的自定义文件浏览器,并希望显示与文件相关的系统图标。

是否有本机 C# 方法来获取各种文件类型的图标(如果有,如何),还是必须通过带有 shell API 的 PInvoke 来完成?

作为后续,如果有本地 .NET 方式来做这件事,是否有跨平台方式来做这件事?

4

6 回答 6

39

我的一个旧开源项目包括一个完全可以做到这一点的Icon 类,请随意撕掉它,看看我把这个文件放在公共领域的年龄无论如何它只是 PInvoke 大部分。

要获取您使用的图标,例如:

Icon zipIcon = BlackFox.Win32.Icons.IconFromExtension(".zip", SystemIconSize.Small);

完整样本:

using System;
using System.Windows.Forms;
using BlackFox.Win32;
using System.Drawing;

class Program
{
    static void Main(string[] args)
    {
        PictureBox pict = new PictureBox();
        pict.Image = Icons.IconFromExtension(".zip", Icons.SystemIconSize.Large).ToBitmap();
        pict.Dock = DockStyle.Fill;
        pict.SizeMode = PictureBoxSizeMode.CenterImage;

        Form form = new Form();
        form.Controls.Add(pict);

        Application.Run(form);        
    }
}

图书馆 :

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Reflection;
using System.Collections.Generic;

namespace BlackFox.Win32
{
    public static class Icons
    {
        #region Custom exceptions class

        public class IconNotFoundException : Exception
        {
            public IconNotFoundException(string fileName, int index)
                : base(string.Format("Icon with Id = {0} wasn't found in file {1}", index, fileName))
            {
            }
        }

        public class UnableToExtractIconsException : Exception
        {
            public UnableToExtractIconsException(string fileName, int firstIconIndex, int iconCount)
                : base(string.Format("Tryed to extract {2} icons starting from the one with id {1} from the \"{0}\" file but failed", fileName, firstIconIndex, iconCount))
            {
            }
        }

        #endregion

        #region DllImports

        /// <summary>
        /// Contains information about a file object. 
        /// </summary>
        struct SHFILEINFO
        {
            /// <summary>
            /// Handle to the icon that represents the file. You are responsible for
            /// destroying this handle with DestroyIcon when you no longer need it. 
            /// </summary>
            public IntPtr hIcon;

            /// <summary>
            /// Index of the icon image within the system image list.
            /// </summary>
            public IntPtr iIcon;

            /// <summary>
            /// Array of values that indicates the attributes of the file object.
            /// For information about these values, see the IShellFolder::GetAttributesOf
            /// method.
            /// </summary>
            public uint dwAttributes;

            /// <summary>
            /// String that contains the name of the file as it appears in the Microsoft
            /// Windows Shell, or the path and file name of the file that contains the
            /// icon representing the file.
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string szDisplayName;

            /// <summary>
            /// String that describes the type of file.
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            public string szTypeName;
        };

        [Flags]
        enum FileInfoFlags : int
        {
            /// <summary>
            /// Retrieve the handle to the icon that represents the file and the index 
            /// of the icon within the system image list. The handle is copied to the 
            /// hIcon member of the structure specified by psfi, and the index is copied 
            /// to the iIcon member.
            /// </summary>
            SHGFI_ICON = 0x000000100,
            /// <summary>
            /// Indicates that the function should not attempt to access the file 
            /// specified by pszPath. Rather, it should act as if the file specified by 
            /// pszPath exists with the file attributes passed in dwFileAttributes.
            /// </summary>
            SHGFI_USEFILEATTRIBUTES = 0x000000010
        }

        /// <summary>
        ///     Creates an array of handles to large or small icons extracted from
        ///     the specified executable file, dynamic-link library (DLL), or icon
        ///     file. 
        /// </summary>
        /// <param name="lpszFile">
        ///     Name of an executable file, DLL, or icon file from which icons will
        ///     be extracted.
        /// </param>
        /// <param name="nIconIndex">
        ///     <para>
        ///         Specifies the zero-based index of the first icon to extract. For
        ///         example, if this value is zero, the function extracts the first
        ///         icon in the specified file.
        ///     </para>
        ///     <para>
        ///         If this value is �1 and <paramref name="phiconLarge"/> and
        ///         <paramref name="phiconSmall"/> are both NULL, the function returns
        ///         the total number of icons in the specified file. If the file is an
        ///         executable file or DLL, the return value is the number of
        ///         RT_GROUP_ICON resources. If the file is an .ico file, the return
        ///         value is 1. 
        ///     </para>
        ///     <para>
        ///         Windows 95/98/Me, Windows NT 4.0 and later: If this value is a 
        ///         negative number and either <paramref name="phiconLarge"/> or 
        ///         <paramref name="phiconSmall"/> is not NULL, the function begins by
        ///         extracting the icon whose resource identifier is equal to the
        ///         absolute value of <paramref name="nIconIndex"/>. For example, use -3
        ///         to extract the icon whose resource identifier is 3. 
        ///     </para>
        /// </param>
        /// <param name="phIconLarge">
        ///     An array of icon handles that receives handles to the large icons
        ///     extracted from the file. If this parameter is NULL, no large icons
        ///     are extracted from the file.
        /// </param>
        /// <param name="phIconSmall">
        ///     An array of icon handles that receives handles to the small icons
        ///     extracted from the file. If this parameter is NULL, no small icons
        ///     are extracted from the file. 
        /// </param>
        /// <param name="nIcons">
        ///     Specifies the number of icons to extract from the file. 
        /// </param>
        /// <returns>
        ///     If the <paramref name="nIconIndex"/> parameter is -1, the
        ///     <paramref name="phIconLarge"/> parameter is NULL, and the
        ///     <paramref name="phiconSmall"/> parameter is NULL, then the return
        ///     value is the number of icons contained in the specified file.
        ///     Otherwise, the return value is the number of icons successfully
        ///     extracted from the file. 
        /// </returns>
        [DllImport("Shell32", CharSet = CharSet.Auto)]
        extern static int ExtractIconEx(
            [MarshalAs(UnmanagedType.LPTStr)] 
            string lpszFile,
            int nIconIndex,
            IntPtr[] phIconLarge,
            IntPtr[] phIconSmall,
            int nIcons);

        [DllImport("Shell32", CharSet = CharSet.Auto)]
        extern static IntPtr SHGetFileInfo(
            string pszPath,
            int dwFileAttributes,
            out SHFILEINFO psfi,
            int cbFileInfo,
            FileInfoFlags uFlags);

        #endregion

        /// <summary>
        /// Two constants extracted from the FileInfoFlags, the only that are
        /// meaningfull for the user of this class.
        /// </summary>
        public enum SystemIconSize : int
        {
            Large = 0x000000000,
            Small = 0x000000001
        }

        /// <summary>
        /// Get the number of icons in the specified file.
        /// </summary>
        /// <param name="fileName">Full path of the file to look for.</param>
        /// <returns></returns>
        static int GetIconsCountInFile(string fileName)
        {
            return ExtractIconEx(fileName, -1, null, null, 0);
        }

        #region ExtractIcon-like functions

        public static void ExtractEx(string fileName, List<Icon> largeIcons,
            List<Icon> smallIcons, int firstIconIndex, int iconCount)
        {
            /*
             * Memory allocations
             */

            IntPtr[] smallIconsPtrs = null;
            IntPtr[] largeIconsPtrs = null;

            if (smallIcons != null)
            {
                smallIconsPtrs = new IntPtr[iconCount];
            }
            if (largeIcons != null)
            {
                largeIconsPtrs = new IntPtr[iconCount];
            }

            /*
             * Call to native Win32 API
             */

            int apiResult = ExtractIconEx(fileName, firstIconIndex, largeIconsPtrs, smallIconsPtrs, iconCount);
            if (apiResult != iconCount)
            {
                throw new UnableToExtractIconsException(fileName, firstIconIndex, iconCount);
            }

            /*
             * Fill lists
             */

            if (smallIcons != null)
            {
                smallIcons.Clear();
                foreach (IntPtr actualIconPtr in smallIconsPtrs)
                {
                    smallIcons.Add(Icon.FromHandle(actualIconPtr));
                }
            }
            if (largeIcons != null)
            {
                largeIcons.Clear();
                foreach (IntPtr actualIconPtr in largeIconsPtrs)
                {
                    largeIcons.Add(Icon.FromHandle(actualIconPtr));
                }
            }
        }

        public static List<Icon> ExtractEx(string fileName, SystemIconSize size,
            int firstIconIndex, int iconCount)
        {
            List<Icon> iconList = new List<Icon>();

            switch (size)
            {
                case SystemIconSize.Large:
                    ExtractEx(fileName, iconList, null, firstIconIndex, iconCount);
                    break;

                case SystemIconSize.Small:
                    ExtractEx(fileName, null, iconList, firstIconIndex, iconCount);
                    break;

                default:
                    throw new ArgumentOutOfRangeException("size");
            }

            return iconList;
        }

        public static void Extract(string fileName, List<Icon> largeIcons, List<Icon> smallIcons)
        {
            int iconCount = GetIconsCountInFile(fileName);
            ExtractEx(fileName, largeIcons, smallIcons, 0, iconCount);
        }

        public static List<Icon> Extract(string fileName, SystemIconSize size)
        {
            int iconCount = GetIconsCountInFile(fileName);
            return ExtractEx(fileName, size, 0, iconCount);
        }

        public static Icon ExtractOne(string fileName, int index, SystemIconSize size)
        {
            try
            {
                List<Icon> iconList = ExtractEx(fileName, size, index, 1);
                return iconList[0];            
            }
            catch (UnableToExtractIconsException)
            {
                throw new IconNotFoundException(fileName, index);
            }
        }

        public static void ExtractOne(string fileName, int index,
            out Icon largeIcon, out Icon smallIcon)
        {
            List<Icon> smallIconList = new List<Icon>();
            List<Icon> largeIconList = new List<Icon>();
            try
            {
                ExtractEx(fileName, largeIconList, smallIconList, index, 1);
                largeIcon = largeIconList[0];
                smallIcon = smallIconList[0];
            }
            catch (UnableToExtractIconsException)
            {
                throw new IconNotFoundException(fileName, index);
            }
        }

        #endregion

        //this will look throw the registry 
        //to find if the Extension have an icon.
        public static Icon IconFromExtension(string extension,
                                                SystemIconSize size)
        {
            // Add the '.' to the extension if needed
            if (extension[0] != '.') extension = '.' + extension;

            //opens the registry for the wanted key.
            RegistryKey Root = Registry.ClassesRoot;
            RegistryKey ExtensionKey = Root.OpenSubKey(extension);
            ExtensionKey.GetValueNames();
            RegistryKey ApplicationKey =
                Root.OpenSubKey(ExtensionKey.GetValue("").ToString());

            //gets the name of the file that have the icon.
            string IconLocation =
                ApplicationKey.OpenSubKey("DefaultIcon").GetValue("").ToString();
            string[] IconPath = IconLocation.Split(',');

            if (IconPath[1] == null) IconPath[1] = "0";
            IntPtr[] Large = new IntPtr[1], Small = new IntPtr[1];

            //extracts the icon from the file.
            ExtractIconEx(IconPath[0],
                Convert.ToInt16(IconPath[1]), Large, Small, 1);
            return size == SystemIconSize.Large ?
                Icon.FromHandle(Large[0]) : Icon.FromHandle(Small[0]);
        }

        public static Icon IconFromExtensionShell(string extension, SystemIconSize size)
        {
            //add '.' if nessesry
            if (extension[0] != '.') extension = '.' + extension;

            //temp struct for getting file shell info
            SHFILEINFO fileInfo = new SHFILEINFO();

            SHGetFileInfo(
                extension,
                0,
                out fileInfo,
                Marshal.SizeOf(fileInfo),
                FileInfoFlags.SHGFI_ICON | FileInfoFlags.SHGFI_USEFILEATTRIBUTES | (FileInfoFlags)size);

            return Icon.FromHandle(fileInfo.hIcon);
        }

        public static Icon IconFromResource(string resourceName)
        {
            Assembly assembly = Assembly.GetCallingAssembly();

            return new Icon(assembly.GetManifestResourceStream(resourceName));
        }

        /// <summary>
        /// Parse strings in registry who contains the name of the icon and
        /// the index of the icon an return both parts.
        /// </summary>
        /// <param name="regString">The full string in the form "path,index" as found in registry.</param>
        /// <param name="fileName">The "path" part of the string.</param>
        /// <param name="index">The "index" part of the string.</param>
        public static void ExtractInformationsFromRegistryString(
            string regString, out string fileName, out int index)
        {
            if (regString == null)
            {
                throw new ArgumentNullException("regString");
            }
            if (regString.Length == 0)
            {
                throw new ArgumentException("The string should not be empty.", "regString");
            }

            index = 0;
            string[] strArr = regString.Replace("\"", "").Split(',');
            fileName = strArr[0].Trim();
            if (strArr.Length > 1)
            {
                int.TryParse(strArr[1].Trim(), out index);
            }
        }

        public static Icon ExtractFromRegistryString(string regString, SystemIconSize size)
        {
            string fileName;
            int index;
            ExtractInformationsFromRegistryString(regString, out fileName, out index);
            return ExtractOne(fileName, index, size);
        }
    }
}
于 2009-03-05T22:27:18.213 回答
7

看看: http: //mvolo.com/display-pretty-file-icons-in-your-aspnet-applications-with-iconhandler/

这不是最干净的解决方案,但它有效。否则,请尝试使用基于 mime 类型或文件扩展名的图标库。

于 2009-03-05T21:58:36.410 回答
5

我确定您已经找到了解决问题的方法,但为了其他人的利益,我对 VirtualBlackFox 的解决方案进行了一些修改。

只需替换 IconFromExtension 方法...

public static Icon IconFromExtension(string extension,
                                            SystemIconSize size)
    {
        // Add the '.' to the extension if needed
        if (extension[0] != '.') extension = '.' + extension;

        //opens the registry for the wanted key.
        RegistryKey Root = Registry.ClassesRoot;
        RegistryKey ExtensionKey = Root.OpenSubKey(extension);
        ExtensionKey.GetValueNames();
        RegistryKey ApplicationKey =
            Root.OpenSubKey(ExtensionKey.GetValue("").ToString());

        RegistryKey CurrentVer = null;
        try
        {
            CurrentVer = Root.OpenSubKey(ApplicationKey.OpenSubKey("CurVer").GetValue("").ToString());
        }
        catch (Exception ex)
        {
            //current version not found... carry on without it?
        }

        if (CurrentVer != null)
            ApplicationKey = CurrentVer;

        //gets the name of the file that have the icon.
        string IconLocation =
            ApplicationKey.OpenSubKey("DefaultIcon").GetValue("").ToString();
        string[] IconPath = IconLocation.Split(',');


        IntPtr[] Large = null;
        IntPtr[] Small = null;
        int iIconPathNumber = 0;

        if (IconPath.Length > 1)
            iIconPathNumber = 1;
        else
            iIconPathNumber = 0;


        if (IconPath[iIconPathNumber] == null) IconPath[iIconPathNumber] = "0";
        Large = new IntPtr[1];
        Small = new IntPtr[1];



        //extracts the icon from the file.
        if (iIconPathNumber > 0)
        {
            ExtractIconEx(IconPath[0],
                Convert.ToInt16(IconPath[iIconPathNumber]), Large, Small, 1);
        }
        else
        {
            ExtractIconEx(IconPath[0],
                Convert.ToInt16(0), Large, Small, 1);
        }


        return size == SystemIconSize.Large ?
            Icon.FromHandle(Large[0]) : Icon.FromHandle(Small[0]);
    }
于 2009-08-28T07:49:38.877 回答
4

Icon.ExtractAssociatedIcon() 不会成功。正如MSDN所说,它只提取文件中包含的图标。所以创建虚拟文件也无济于事。据我所知,您必须使用 p/invoke 方式才能访问这些图标。与此相关的一个问题是thisMaLio似乎有一个非常完整的例子来说明如何使用 p/invoke 获取图标。

我也不知道有一种与平台无关的方法来做到这一点(而且我认为没有)。

对不起,我无法为您提供更好的消息!

于 2009-03-05T21:59:39.667 回答
4

可以使用IQueryAssociations接口,而不是通过注册表浏览。此接口还可用于获取有关已注册文件类型的更多信息(参见即 ASSOCSTR 类型)。下面我附上替换 VirtualBlackFox 解决方案中的 IconFromExtension 方法的代码(他的部分代码保持不变):

public static Icon IconFromExtension(string extension, SystemIconSize size)
  {
     if (extension[0] != '.') extension = '.' + extension;

     object obj;
     shell.AssocCreate(shell.CLSID_QueryAssociations, ref shell.IID_IQueryAssociations, out obj);
     var qa = (shell.IQueryAssociations)obj;
     qa.Init(shell.ASSOCF.INIT_DEFAULTTOSTAR, Convert.ToString(extension), UIntPtr.Zero, IntPtr.Zero);

     var bufSize = 0;
     qa.GetString(shell.ASSOCF.NOTRUNCATE, shell.ASSOCSTR.DEFAULTICON, null, null, ref bufSize);

     var sb = new StringBuilder(bufSize);
     qa.GetString(shell.ASSOCF.NOTRUNCATE, shell.ASSOCSTR.DEFAULTICON, null, sb, ref bufSize);

     if (!String.IsNullOrEmpty(sb.ToString()))
     {
        var iconLocation = sb.ToString();
        var iconPath = iconLocation.Split(',');

        var iIconPathNumber = iconPath.Length > 1 ? 1 : 0;

        if (iconPath[iIconPathNumber] == null) iconPath[iIconPathNumber] = "0";
        var large = new IntPtr[1];
        var small = new IntPtr[1];

        //extracts the icon from the file.
        ExtractIconEx(iconPath[0],
                      iIconPathNumber > 0 ? Convert.ToInt16(iconPath[iIconPathNumber]) : Convert.ToInt16(0),
                      large,
                      small, 1);

        return size == SystemIconSize.Large
                  ? Icon.FromHandle(large[0])
                  : Icon.FromHandle(small[0]);
     }
     return IntPtr.Zero;
  }

除了上面的代码之外,还需要一个“shell”类——Shell API 的一个包装器(下面仅限于 AssocCreate 和必要的类型):

using System;
using System.Runtime.InteropServices;
using System.Text;
#pragma warning disable 1591
// ReSharper disable InconsistentNaming

namespace <put_your_appropriate_namespace_here>
{
   public class shell
   {
      [DllImport("shlwapi.dll")]
      public extern static int AssocCreate(
         Guid clsid,
         ref Guid riid,
         [MarshalAs(UnmanagedType.Interface)] out object ppv);

      [Flags]
      public enum ASSOCF
      {
         INIT_NOREMAPCLSID = 0x00000001,
         INIT_BYEXENAME = 0x00000002,
         OPEN_BYEXENAME = 0x00000002,
         INIT_DEFAULTTOSTAR = 0x00000004,
         INIT_DEFAULTTOFOLDER = 0x00000008,
         NOUSERSETTINGS = 0x00000010,
         NOTRUNCATE = 0x00000020,
         VERIFY = 0x00000040,
         REMAPRUNDLL = 0x00000080,
         NOFIXUPS = 0x00000100,
         IGNOREBASECLASS = 0x00000200,
         INIT_IGNOREUNKNOWN = 0x00000400
      }

      public enum ASSOCSTR
      {
         COMMAND = 1,
         EXECUTABLE,
         FRIENDLYDOCNAME,
         FRIENDLYAPPNAME,
         NOOPEN,
         SHELLNEWVALUE,
         DDECOMMAND,
         DDEIFEXEC,
         DDEAPPLICATION,
         DDETOPIC,
         INFOTIP,
         QUICKTIP,
         TILEINFO,
         CONTENTTYPE,
         DEFAULTICON,
         SHELLEXTENSION
      }

      public enum ASSOCKEY
      {
         SHELLEXECCLASS = 1,
         APP,
         CLASS,
         BASECLASS
      }

      public enum ASSOCDATA
      {
         MSIDESCRIPTOR = 1,
         NOACTIVATEHANDLER,
         QUERYCLASSSTORE,
         HASPERUSERASSOC,
         EDITFLAGS,
         VALUE
      }

      [Guid("c46ca590-3c3f-11d2-bee6-0000f805ca57"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
      public interface IQueryAssociations
      {
         void Init(
           [In] ASSOCF flags,
           [In, MarshalAs(UnmanagedType.LPWStr)] string pszAssoc,
           [In] UIntPtr hkProgid,
           [In] IntPtr hwnd);

         void GetString(
           [In] ASSOCF flags,
           [In] ASSOCSTR str,
           [In, MarshalAs(UnmanagedType.LPWStr)] string pwszExtra,
           [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszOut,
           [In, Out] ref int pcchOut);

         void GetKey(
           [In] ASSOCF flags,
           [In] ASSOCKEY str,
           [In, MarshalAs(UnmanagedType.LPWStr)] string pwszExtra,
           [Out] out UIntPtr phkeyOut);

         void GetData(
           [In] ASSOCF flags,
           [In] ASSOCDATA data,
           [In, MarshalAs(UnmanagedType.LPWStr)] string pwszExtra,
           [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] out byte[] pvOut,
           [In, Out] ref int pcbOut);

         void GetEnum(); // not used actually
      }

      public static Guid CLSID_QueryAssociations = new Guid("a07034fd-6caa-4954-ac3f-97a27216f98a");
      public static Guid IID_IQueryAssociations = new Guid("c46ca590-3c3f-11d2-bee6-0000f805ca57");

   }
}

对于需要 ImageSource 的 WPF 用户:

将返回类型从 Icon 替换为 ImageSource 并将 return 子句替换为:

     var iconPtr = size == SystemIconSize.Large ? large[0] : small[0];
     if (iconPtr != IntPtr.Zero)
     {
        return Imaging.CreateBitmapSourceFromHIcon(
           iconPtr,
           Int32Rect.Empty,
           BitmapSizeOptions.FromEmptyOptions());
     }
于 2012-11-06T14:17:34.213 回答
3

这个类应该做的工作。传递文件名(带路径)或文件夹名称(带路径)。

public static class FileIcon
{
    [DllImport("shell32.dll")]
    private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
    [StructLayout(LayoutKind.Sequential)]
    private struct SHFILEINFO
    {
        public IntPtr hIcon;
        public IntPtr iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string szTypeName;
    };
    private const uint SHGFI_ICON = 0x100;
    private const uint SHGFI_LARGEICON = 0x0; // 'Large icon
    private const uint SHGFI_SMALLICON = 0x1; // 'Small icon

    public static System.Drawing.Icon GetLargeIcon(string file)
    {
        FileIcon.SHFILEINFO shinfo = new FileIcon.SHFILEINFO();
        IntPtr hImgLarge = FileIcon.SHGetFileInfo(file, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), FileIcon.SHGFI_ICON | FileIcon.SHGFI_LARGEICON);
        return System.Drawing.Icon.FromHandle(shinfo.hIcon);
    }

    public static System.Drawing.Icon GetSmallIcon(string file)
    {
        FileIcon.SHFILEINFO shinfo = new FileIcon.SHFILEINFO();
        IntPtr hImgLarge = FileIcon.SHGetFileInfo(file, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), FileIcon.SHGFI_ICON | FileIcon.SHGFI_SMALLICON);
        return System.Drawing.Icon.FromHandle(shinfo.hIcon);
    }
}
于 2012-06-29T20:11:52.223 回答