简体   繁体   English

How can I retrieve a generic folder icon in C# that appears small & open on Windows 10, using either the Win32 API or the Windows API Code Pack?

[英]How can I retrieve a generic folder icon in C# that appears small & open on Windows 10, using either the Win32 API or the Windows API Code Pack?

Using the Win32 API to retrieve file/folder icons in C# is a trivial process that has been documented countless times, going all the way back to the beginning of.Net.使用 Win32 API 检索 C# 中的文件/文件夹图标是一个微不足道的过程,已被无数次记录,一直追溯到 .Net 的开头。

Every documented solution is just a variation on the same approach: P/Invoking SHGetFileInfo(), passing the appropriate attributes/flags.每个记录在案的解决方案都只是同一方法的变体:P/Invoking SHGetFileInfo(),传递适当的属性/标志。

I am developing on Windows 10, which I assume uses Shell32.dll v6.1 -- the same version that shipped with Windows 7, and the last one released according to Microsoft's documentation .我正在 Windows 10 上进行开发,我假设它使用 Shell32.dll v6.1——与 Windows 文档 7 和最后一个根据Microsoft 发布的文档相同的版本。 The version displayed in the Details tab of the Properties window in Explorer on my development system for C:\Windows\system32\shell32.dll is "10.0.18362.719".在我的开发系统 C:\Windows\system32\shell32.Z06416233FE5EC4C5933122E4AB2186AF1Z 的资源管理器中属性 window 的详细信息选项卡中显示的版本是“190.0”。

My objective is to use this approach to generate a "generic" FOLDER icon.我的目标是使用这种方法来生成“通用”文件夹图标。 I need it to be SMALL (vs large) and in the OPEN (vs closed) state.我需要它是小(相对于大)并且在开放(相对于封闭)state 中。

What I have noticed is that if one requests a small generic folder icon from SHGetFileInfo(), the result always appears in the "closed" state.我注意到的是,如果从 SHGetFileInfo() 请求一个小的通用文件夹图标,结果总是出现在“关闭”的 state 中。

Conversely, if one requests a large generic folder icon from SHGetFileInfo(), the result always appears in the "open" state.相反,如果从 SHGetFileInfo() 请求一个大的通用文件夹图标,结果总是出现在“打开”state 中。

The following code represents the simplest possible demonstration of this.下面的代码代表了最简单的演示。 For a large icon, change the SmallIcon flag to LargeIcon.对于大图标,将 SmallIcon 标志更改为 LargeIcon。 For a closed icon, remove the OpenIcon flag.对于关闭的图标,删除 OpenIcon 标志。

public static Icon GetMyIcon()
{
    var flags = (uint)(ShellAttribute.Icon | ShellAttribute.SmallIcon | ShellAttribute.OpenIcon);

    var fileInfo = new ShellFileInfo();
    var size = (uint)Marshal.SizeOf(fileInfo);
    var result = Interop.SHGetFileInfo(string.Empty, (uint)FileAttribute.Directory, out fileInfo, size, flags);

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

    try
    {
        return (Icon)Icon.FromHandle(fileInfo.hIcon).Clone();
    }
    finally
    {
        Interop.DestroyIcon(fileInfo.hIcon);
    }
}

With Interop methods declared as follows:使用 Interop 方法声明如下:

public static class Interop
{
    [DllImport("shell32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SHGetFileInfo(string path,
        uint attributes,
        out ShellFileInfo fileInfo,
        uint size,
        uint flags);

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

Is there some kind of black magic required to obtain a generic folder icon that is either small & open, or large & closed?是否需要某种黑魔法才能获得一个小而打开或大而封闭的通用文件夹图标?

I'm also able to use the Windows API Code Pack in my project, so if it is possible to bypass the Win32 API and achieve my desired result using this library alone, this would satisfy.我还可以在我的项目中使用 Windows API 代码包,所以如果可以绕过 Win32 API 并单独使用这个库达到我想要的结果,这将满足。

UPDATE: At Ian H's suggestion, I tried using the SHGetStockIconInfo API, but the result was unfortunately the same:更新:在 Ian H 的建议下,我尝试使用 SHGetStockIconInfo API,但不幸的是结果相同:

private static Icon GetStockIcon()
{
    SHSTOCKICONINFO sii = new SHSTOCKICONINFO();
    sii.cbSize = (UInt32)Marshal.SizeOf(typeof(SHSTOCKICONINFO));

    Marshal.ThrowExceptionForHR(SHGetStockIconInfo(SHSTOCKICONID.SIID_FOLDEROPEN,
            SHGSI.SHGSI_ICON | SHGSI.SHGSI_SMALLICON,
            ref sii));
    return Icon.FromHandle(sii.hIcon);
}

The system open icon size is 32*32 which you can get using GetObject .系统打开图标大小为 32*32,您可以使用GetObject获得。

my requirement is a small (16x16) icon with "open" appearance.我的要求是一个具有“开放”外观的小 (16x16) 图标。

The following is an example in C++ using IShellItemImageFactory for creating size 16*16 BITMAP based on system open icon appearance you can refer to as a workaround.以下是 C++ 中使用IShellItemImageFactory创建大小 16*16 BITMAP 基于系统打开图标外观的示例,您可以参考作为解决方法。

        SHFILEINFOW sfi = { 0 };
        ICONINFO iconinfo;
        HBITMAP hBitmap;
        IShellItemImageFactory *pImageFactory;
        SIZE size = { 16, 16 };

        CoInitialize(NULL);

        hr = SHGetFileInfo(L"D:\\Test",
            -1,
            &sfi,
            sizeof(sfi),
            SHGFI_SYSICONINDEX | SHGFI_OPENICON | SHGFI_ICON);

        if (FAILED(hr))
        {
            OutputDebugString(L"SHGetFileInfo fails.\n");
        }

        GetIconInfo(sfi.hIcon, &iconinfo);
        hBitmap = iconinfo.hbmColor;

        BITMAP bmp;
        ZeroMemory(&bmp, sizeof(bmp));

        if (iconinfo.hbmColor)
        {
            // Get initial size is 32*32
            int nWrittenBytes = GetObject(iconinfo.hbmColor, sizeof(bmp), &bmp);

            hr = SHCreateItemFromParsingName(L"D:\\Test", NULL, IID_PPV_ARGS(&pImageFactory));
            if (SUCCEEDED(hr))
            {
                // Get expected size 16*16
                hr = pImageFactory->GetImage(size, SIIGBF_BIGGERSIZEOK, &hBitmap);
                if (FAILED(hr))
                {
                    OutputDebugString(L"IShellItemImageFactory::GetImage failed with error code %x");
                }
                pImageFactory->Release();
            }

            // Confirm if the size is 16*16
            nWrittenBytes = GetObject(hBitmap, sizeof(bmp), &bmp);
        }

The small open icon will show like this:打开的小图标将如下所示:

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM