简体   繁体   中英

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.

Every documented solution is just a variation on the same approach: P/Invoking SHGetFileInfo(), passing the appropriate attributes/flags.

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 . 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".

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.

What I have noticed is that if one requests a small generic folder icon from SHGetFileInfo(), the result always appears in the "closed" state.

Conversely, if one requests a large generic folder icon from SHGetFileInfo(), the result always appears in the "open" state.

The following code represents the simplest possible demonstration of this. For a large icon, change the SmallIcon flag to LargeIcon. For a closed icon, remove the OpenIcon flag.

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:

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.

UPDATE: At Ian H's suggestion, I tried using the SHGetStockIconInfo API, but the result was unfortunately the same:

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 .

my requirement is a small (16x16) icon with "open" appearance.

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.

        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:

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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