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);
}