【问题标题】:Is there a way to get the windows default folder icon using C#?有没有办法使用 C# 获取 Windows 默认文件夹图标?
【发布时间】:2017-08-12 03:30:11
【问题描述】:

我有一个带有文档列表的listView。我使用以下方法为每个人分配了一个图标:

private void SetDocumentIcon(ListViewItem item, FileInfo file)
{
    Icon iconForFile = Icon.ExtractAssociatedIcon(file.FullName);

    if (!documentsIconsImageList.Images.ContainsKey(file.Extension))
    {
        iconForFile = Icon.ExtractAssociatedIcon(file.FullName);
        documentsIconsImageList.Images.Add(file.Extension, iconForFile);
    }

    item.ImageKey = file.Extension;
}

我尝试将此方法用于文件夹,但失败了。据我了解,问题在于Icon.ExtractAssociatedIcon 用于文件而不是文件夹。那么如何提取文件夹的图标呢?

谢谢。

【问题讨论】:

  • 有winapi函数SHGetStockIconInfo,但是pinvoke似乎很复杂。如果我是你 - 我会在某处创建虚拟的空文件夹,使用我提供的重复答案(这将是默认答案)提取图标,然后删除文件夹。当然,您只需在应用程序启动时执行一次。
  • @Evk SHGetStockIconInfo 一点也不复杂。

标签: c#


【解决方案1】:

我敢打赌还有其他方法,但我认为最容易实现的方法是在您创建的临时文件夹上使用SHGetFileInfowin api 函数。示例代码:

public static class DefaultIcons
{
    private static readonly Lazy<Icon> _lazyFolderIcon = new Lazy<Icon>(FetchIcon, true);

    public static Icon FolderLarge
    {
        get { return _lazyFolderIcon.Value; }
    }

    private static Icon FetchIcon()
    {
        var tmpDir = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())).FullName;
        var icon = ExtractFromPath(tmpDir);
        Directory.Delete(tmpDir);
        return icon;
    }

    private static Icon ExtractFromPath(string path)
    {
        SHFILEINFO shinfo = new SHFILEINFO();
        SHGetFileInfo(
            path,
            0, ref shinfo, (uint)Marshal.SizeOf(shinfo),
            SHGFI_ICON | SHGFI_LARGEICON);
        return System.Drawing.Icon.FromHandle(shinfo.hIcon);
    }

    //Struct used by SHGetFileInfo function
    [StructLayout(LayoutKind.Sequential)]
    private 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("shell32.dll")]
    private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

    private const uint SHGFI_ICON = 0x100;
    private const uint SHGFI_LARGEICON = 0x0;
    private const uint SHGFI_SMALLICON = 0x000000001;
}

使用只是

var icon = DefaultIcons.FolderLarge

为小图标添加属性也很简单。

【讨论】:

  • 注意,使用SHGetFileInfo创建System.Drawing.Icon后返回的图标句柄需要删除。
  • 您可能还想订阅系统的图标更改事件。不管是什么^^
【解决方案2】:

SHGetStockIconInfo 是正确的做法,不需要添加不必要的文件 IO。它并不比SHGetFileInfo 复杂。

这是一个与 Evk 的类结构类似的示例类。需要注意的一些重要事项:

  1. 当您从SHGetStockIconInfo(或者甚至SHGetFileInfo,就此而言)获得图标句柄时,必须通过调用DestroyIcon() 清理原生图标,否则您将创建资源泄漏。
  2. 当您使用Icon.FromHandle() 创建图标时,该对象会存储您为其提供的句柄,并将其用于以后的操作。这意味着如果您立即调用DestroyIcon(),然后尝试对您刚刚创建的图标进行操作,则会导致异常。您可以通过使用Clone() 来获得不依赖于您原始本机句柄的Icon 来避免这种情况。
public static class DefaultIcons
{
    private static Icon folderIcon;

    public static Icon FolderLarge => folderIcon ?? (folderIcon = GetStockIcon(SHSIID_FOLDER, SHGSI_LARGEICON));

    private static Icon GetStockIcon(uint type, uint size)
    {
        var info = new SHSTOCKICONINFO();
        info.cbSize = (uint)Marshal.SizeOf(info);

        SHGetStockIconInfo(type, SHGSI_ICON | size, ref info);

        var icon = (Icon)Icon.FromHandle(info.hIcon).Clone(); // Get a copy that doesn't use the original handle
        DestroyIcon(info.hIcon); // Clean up native icon to prevent resource leak

        return icon;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct SHSTOCKICONINFO
    {
        public uint cbSize;
        public IntPtr hIcon;
        public int iSysIconIndex;
        public int iIcon;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szPath;
    }

    [DllImport("shell32.dll")]
    public static extern int SHGetStockIconInfo(uint siid, uint uFlags, ref SHSTOCKICONINFO psii);

    [DllImport("user32.dll")]
    public static extern bool DestroyIcon(IntPtr handle);

    private const uint SHSIID_FOLDER = 0x3;
    private const uint SHGSI_ICON = 0x100;
    private const uint SHGSI_LARGEICON = 0x0;
    private const uint SHGSI_SMALLICON = 0x1;
}

【讨论】:

猜你喜欢
  • 2010-11-05
  • 1970-01-01
  • 1970-01-01
  • 2018-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多