Получить значок файла, используемый Shell

В .Net(С# или VB: не волнует), учитывая строку пути файла, структуру FileInfo или структуру FileSystemInfo для реального существующего файла, как я могу определить значок, используемый оболочкой (проводник ) для этого файла?

В настоящее время я не планирую использовать это для чего-либо, но мне стало любопытно, как это сделать при просмотре этого вопроса, и я подумал, что это будет полезно для архивирования здесь на SO.

Ответ 1

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

Ответ 2

Пожалуйста, игнорируйте всех, кто говорит вам использовать реестр! Реестр не является API. API, который вы хотите, это SHGetFileInfo с SHGFI_ICON. Здесь вы можете получить подпись P/Invoke:

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

Ответ 3

Вы должны использовать SHGetFileInfo.

Icon.ExtractAssociatedIcon работает как и SHGetFileInfo в большинстве случаев, но SHGetFileInfo может работать с UNC-путями (например, сетевой путь, например "\\ ComputerName\SharedFolder \" ), в то время как Icon.ExtractAssociatedIcon не может. Если вам нужно или, возможно, потребуется использовать UNC-пути, лучше использовать SHGetFileInfo вместо Icon.ExtractAssociatedIcon.

Это хорошая статья CodeProject о том, как использовать SHGetFileInfo.

Ответ 4

Не более чем на С# версии ответа Стефана.

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;
    }
}

Ответ 5

Это работает для меня в моих проектах, надеюсь, что это кому-то поможет.

Это С# с P/Invokes, он будет работать до сих пор в системах x86/x64 с 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
    }
}

Ответ 6

Проблема с подходом к реестру заключается в том, что вы явно не получаете идентификатор индекса значка. Иногда (если не все времена) вы получаете значок ResourceID, который является псевдонимом разработчика приложения, используемого для обозначения слота значка.

Поэтому метод реестра подразумевает, что все разработчики используют идентификаторы ResourceID, которые совпадают с неявным индексом идентификатора значка (который основан на нуле, абсолютный, детерминированный).

Сканируйте местоположение реестра, и вы увидите много отрицательных чисел, иногда даже текстовых ссылок, т.е. не идентификатор индекса значка. Неявный метод кажется лучше, поскольку он позволяет ОС выполнять работу.

Только тестирование этого нового метода сейчас, но оно имеет смысл и, надеюсь, решает эту проблему.

Ответ 7

Если вас интересует только значок для определенного расширения, и если вы не возражаете создать временный файл, вы можете следовать приведенному ниже примеру здесь

Код С#:

    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;
    }

Ответ 8

Эта ссылка, похоже, содержит некоторую информацию. Это связано с большим количеством изменений в реестре, но это кажется выполнимым. Примеры приведены в С++

Ответ 9

  • определить расширение
  • в реестре, перейдите к "HKCR\.{extension}", прочитайте значение по умолчанию (позвоните ему filetype)
  • в "HKCR\{filetype}\DefaultIcon", прочитайте значение по умолчанию: это путь к файлу значка (или файл контейнера значков, например .exe со встроенным ресурсом значка)
  • если необходимо, используйте предпочтительный метод извлечения ресурса значка из указанного файла.

отредактировать/отодвинуть от комментариев:

Если значок находится в файле контейнера (это довольно часто), после пути будет счетчик, например: "foo.exe,3". Это означает, что значок значка 4 (индекс основан на нулевом значении) доступных значков. Значение ", 0" неявно (и необязательно). Если счетчик равен 0 или отсутствует, значок кулака будет использоваться оболочкой.