【问题标题】:How do I fetch the folder icon on Windows 7 using Shell32.SHGetFileInfo如何使用 Shell32.SHGetFileInfo 在 Windows 7 上获取文件夹图标
【发布时间】:2010-12-08 15:01:46
【问题描述】:

我有以下代码适用于 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;

这在 Windows 7 中获取文件夹图标时会产生以下结果:

在 Vista 中 - 使用相同的方法会产生以下文件夹图标:

我还想要 Windows 7 的“正确”Windows 文件夹图标 - 而不是用于指示安装 Windows 的驱动器的图标。

我不知道 win32 API,而且我的非托管编程在 Windows 平台上几乎没有。

【问题讨论】:

  • 请澄清您的问题。 “这会导致 Windows 7 出现以下图标”是什么意思?

标签: c# .net shell windows-7


【解决方案1】:

您不应将null 指定为SHGeFileInfo 的第一个参数。请改用文件夹的路径(请注意,某些文件夹具有不同的(非标准)图标)。例如,您可以使用临时文件夹或应用程序的根文件夹。

最佳做法是为每个文件夹获取正确的图标(换句话说:将GetFolderIcon 的签名更改为public static Icon GetFolderIcon(string folderPath, IconSize size, FolderType folderType) 并为您显示的每个文件夹调用它)。

似乎有一个开源库已经有一个用于获取文件夹图标的托管包装器。 在 PInvoke.net 上找到(SHGetFileInfo 的条目):

但是,如果您想要驱动器或文件夹的图标,这将不起作用。

在这种情况下,您可以使用 ManagedWindowsApi 项目 (http://mwinapi.sourceforge.net) 提供的 ExtendedFileInfo 类。

如果您想坚持手工制作的解决方案,这对我有用(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);
            }
        }
    }
}

【讨论】:

  • 因为每个人的系统上都有'C:\Windows'
  • 即使您没有 'C:\Windows' 也可以使用,请参阅 Tom 的回答。无论如何,这就是为什么要评论最佳实践和“这对我有用”。
【解决方案2】:

不应将 null 指定为 SHGeFileInfo 的第一个参数。

没错。

请改用文件夹的路径(请注意,某些文件夹有不同的(非标准)图标)。例如,您可以使用临时文件夹或应用程序的根文件夹。

它不需要是真实的(现有的)文件夹路径。任何非空字符串都可以。例如:

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

【讨论】:

  • 这是一个非常有用且有效的答案。您不必四处寻找真正的文件夹即可通过。事实上,我确实通过了"AnyNonEmptyStringWillDo"
猜你喜欢
  • 2010-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-16
  • 1970-01-01
  • 2011-12-08
  • 1970-01-01
相关资源
最近更新 更多