Подтвердить что ты не робот

Как получить значок папки в Windows 7 с помощью Shell32.SHGetFileInfo

У меня есть следующий код, который работает в Windows XP и Vista - как 32, так и 64 бит:

public static Icon GetFolderIcon(IconSize size, FolderType folderType)
{
    // Need to add size check, although errors generated at present!
    uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES;

    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
    var shfi = new Shell32.SHFILEINFO();
    Shell32.SHGetFileInfo(  null, 
                            Shell32.FILE_ATTRIBUTE_DIRECTORY, 
                            ref shfi, 
                            (uint) Marshal.SizeOf(shfi), 
                            flags );

    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
    var icon = (Icon)Icon.FromHandle(shfi.hIcon).Clone();

    User32Dll.DestroyIcon( shfi.hIcon );        // Cleanup
    return icon;
}

Константы определяются следующим образом:

public const uint SHGFI_ICON = 0x000000100;
public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
public const uint SHGFI_OPENICON = 0x000000002;
public const uint SHGFI_SMALLICON = 0x000000001;
public const uint SHGFI_LARGEICON = 0x000000000;
public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;

Это дает следующие результаты в окнах 7 при извлечении значка папки:

alt text

В то время как в Vista - использование того же метода приводит к следующему значку папки:

alt text

Я также хотел бы "правильный" значок папки Windows для Windows 7 - не значок, используемый для указания диска, на котором установлена ​​Windows.

Я не знаю API win32, и мое не управляемое программирование на платформе Windows практически отсутствует.

4b9b3361

Ответ 1

Не следует указывать null как первый параметр yur для SHGeFileInfo. Вместо этого используйте путь к папке (обратите внимание, что некоторые папки имеют разные (нестандартные) значки). Например, вы можете использовать временную папку или корневую папку приложения.

Лучшей практикой было бы получить правильный значок для каждой папки (другими словами: изменить подпись GetFolderIcon на public static Icon GetFolderIcon(string folderPath, IconSize size, FolderType folderType) и вызвать ее для каждой отображаемой папки).

Кажется, есть библиотека с открытым исходным кодом, которая уже имеет управляемую оболочку для выбора значков в папке. Найдено на PInvoke.net(запись для SHGetFileInfo):

Однако это не работает, если вы хотите значок диска или папки.

В этом случае вы можете использовать класс ExtendedFileInfo, предоставленный проектом ManagedWindowsApi (http://mwinapi.sourceforge.net).

Если вы хотите придерживаться решения, созданного вручную, это работает для меня (Win7 x64 RTM,.NET 3.5 SP1):

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace IconExtractor
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    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;
    };

    public enum FolderType
    {
        Closed,
        Open
    }

    public enum IconSize
    {
        Large,
        Small
    }

    public partial class Form1 : Form
    {
        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, out SHFILEINFO psfi, uint cbFileInfo, uint uFlags);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool DestroyIcon(IntPtr hIcon);

        public const uint SHGFI_ICON = 0x000000100; 
        public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; 
        public const uint SHGFI_OPENICON = 0x000000002; 
        public const uint SHGFI_SMALLICON = 0x000000001; 
        public const uint SHGFI_LARGEICON = 0x000000000; 
        public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;

        public static Icon GetFolderIcon(IconSize size, FolderType folderType)
        {    
            // Need to add size check, although errors generated at present!    
            uint flags = SHGFI_ICON | SHGFI_USEFILEATTRIBUTES;    

            if (FolderType.Open == folderType)    
            {        
                flags += SHGFI_OPENICON;    
            }    
            if (IconSize.Small == size)    
            {       flags += SHGFI_SMALLICON;    
            }     
            else     
            {       
                flags += SHGFI_LARGEICON;    
            }    
            // Get the folder icon    
            var shfi = new SHFILEINFO();    

            var res = SHGetFileInfo(@"C:\Windows",                             
                FILE_ATTRIBUTE_DIRECTORY,                             
                out shfi,                             
                (uint) Marshal.SizeOf(shfi),                             
                flags );

            if (res == IntPtr.Zero)
                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());

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

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

            DestroyIcon( shfi.hIcon );        // Cleanup    

            return icon;}

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {

                Icon icon = GetFolderIcon(IconSize.Large, FolderType.Open);
                pictureBox1.Image = icon.ToBitmap();
                // Note: The image actually should be disposed somewhere
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}

Ответ 2

Вы не должны указывать значение null в качестве первого параметра yur для SHGeFileInfo.

Это правильно.

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

Это не обязательно должен быть реальный (существующий) путь к папке. Любая непустая строка будет выполнена. например:.

SHGetFileInfo("AnyNonEmptyStringWillDo", FILE_ATTRIBUTE_DIRECTORY, sfi,
      SizeOf(sfi), SHGFI_USEFILEATTRIBUTES or SHGFI_SYSICONINDEX)