How do I fetch the folder icon on Windows 7 using Shell32.SHGetFileInfo
I have the following code which works on Windows XP and Vista - both 32 and 64 bit:
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;
}
The constan开发者_StackOverflowts are defined the following way:
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;
This gives the following results in windows 7 when fetching the folder icon:
While at Vista - using the same method result in the following folder icon:
I would like the "correct" Windows folder icon for Windows 7 also - not the icon used to indicate the drive where Windows is installed.
I don't know the win32 API and my non-managed programming is next to none on the Windows platform.
You shouldn't specify null
as yur first parameter to SHGeFileInfo
. Use the path to a folder instead (please note that some folders have different (non-standard) icons). You could use the temp folder or your application's root folder for example.
Best practise would be to get the correct icon for each folder (in other words: Change the signature of GetFolderIcon
to public static Icon GetFolderIcon(string folderPath, IconSize size, FolderType folderType)
and call it for each folder you display).
There seems to be an open source library which already has a managed wrapper for fetching folder icons. Found on PInvoke.net (the entry for SHGetFileInfo):
However, this does not work if you want an icon of a drive or folder.
In that case, you can use the ExtendedFileInfo class provided by the ManagedWindowsApi project (http://mwinapi.sourceforge.net).
If you want to stick to a hand-crafted solution, this works for me (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);
}
}
}
}
You shouldn't specify null as yur first parameter to SHGeFileInfo.
That's right.
Use the path to a folder instead (please note that some folders have different (non-standard) icons). You could use the temp folder or your application's root folder for example.
It doesn't need to be a real (existing) folder path. Any non-empty string will do. e.g.:
SHGetFileInfo("AnyNonEmptyStringWillDo", FILE_ATTRIBUTE_DIRECTORY, sfi,
SizeOf(sfi), SHGFI_USEFILEATTRIBUTES or SHGFI_SYSICONINDEX)
精彩评论