Extract extra large Icon from a file, including network paths!


Edit (04/16/2014): Added a new function called GetBitmapFromFolderPath, which returns the associated icon from the path to a Folder (or drive!).

image

Sometimes we are required to show a list of files with their associated icons. This task sounds quite easy, and in fact it is… Except if you have to deal with files in network paths or you want to get different icons sizes, apart the typical 32×32.

If you want to achieve this using managed code (the easy way), there is a static method called ExtractAssociatedIcon under the class Icon to achieve that, but sadly this method doesn’t work with UNC paths neither return other sizes that 32×32 pixels.

In my current project I needed to show four different icons sizes (including the extra-large icon, also called “jumbo”), so I’ve decided to use some functions and structures from the Win32 API. Of course, if anyone knows a better way to do it, please contact with me ASAP :)

First, let’s see the final code:


private static IntPtr GetIconHandleFromFilePath(
    string filepath, IconSizeEnum iconsize)
{
    var shinfo = new Shell.SHFILEINFO();
    const uint SHGFI_SYSICONINDEX = 0x4000;
    const int FILE_ATTRIBUTE_NORMAL = 0x80;
    const int ILD_TRANSPARENT = 1;
    uint flags = SHGFI_SYSICONINDEX;
    var retval = SHGetFileInfo(filepath, FILE_ATTRIBUTE_NORMAL,
        ref shinfo, Marshal.SizeOf(shinfo), flags);
    if (retval == 0) throw (new System.IO.FileNotFoundException());
    var iconIndex = shinfo.iIcon;
    var iImageListGuid = newGuid("46EB5926-582E-4017-9FDF-E8998DAA0950");
    Shell.IImageList iml;
    var hres = SHGetImageList((int)iconsize, ref iImageListGuid, out iml);
    var hIcon = IntPtr.Zero;
    hres = iml.GetIcon(iconIndex, ILD_TRANSPARENT, ref hIcon);
    return hIcon;
}

In this code we’re using several API calls. Here’s the tricky part:

First, we need to make call to the SHGetFileInfo function that receives a reference to a structure of type SHFILEINFO, which contains the index of the icon image within the system image list. We will use this index later.

Then we’ve to make is a second call to the SHGetImageList function that receives an output parameter with an IImageList structure, which is modified within the function.

This struct retrieve a COM interface and we need to keep in mind a couple of things:

a) We must use the GUID of this interface in the declaration:

[ComImportAttribute()]
[GuidAttribute(“46EB5926-582E-4017-9FDF-E8998DAA0950”)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]

b) And in the improbable case you are going to deploy your project over XP, remember that SHGetImageList is not exported correctly in XP. For this reason you must hardcode the function’s entry point. Apparently (and hopefully) ordinal 727 isn’t going to change…

Once we have that COM interface, we only need to call its GetIcon method, passing a parameter with the desired size, and obtaining a handle to the icon by reference.

After having the handle its really simple create an Icon from the handle, and then convert to a Bitmap, BitmapSource or other:


public static System.Drawing.Bitmap GetBitmapFromFilePath(
 string filepath, IconSizeEnum iconsize)
{
    IntPtr hIcon = GetIconHandleFromFilePath(filepath, iconsize);
    var myIcon = System.Drawing.Icon.FromHandle(hIcon);
    var bitmap = myIcon.ToBitmap();
    myIcon.Dispose();
    DestroyIcon(hIcon);
    SendMessage(hIcon, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
    return bitmap;
}

Tip: It’s very important don’t forget to destroy the resources (Icon) when working with the Win32 API!

This method calls the previous one, obtains the icon’s handle and then creates the icon using the handle. Then creates a bitmap from the icon, destroys the icon and returns the bitmap.

I’ve also created an enumeration with the different flags values:

private const int SHGFI_SMALLICON = 0x1;
private const int SHGFI_LARGEICON = 0x0;
private const int SHIL_JUMBO = 0x4;
private const int SHIL_EXTRALARGE = 0x2;
private const int WM_CLOSE = 0x0010;

public enum IconSizeEnum
{
 SmallIcon16 = SHGFI_SMALLICON,
 MediumIcon32 = SHGFI_LARGEICON,
 LargeIcon48 = SHIL_EXTRALARGE,
 ExtraLargeIcon = SHIL_JUMBO
}

Finally, retrieving the icon from a file (inclusive if it’s in a network location) and specify the desired size it’s as easy as:

var size = ShellEx.IconSizeEnum.ExtraLargeIcon;
var ofd = new OpenFileDialog();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
    labelFilePath.Text = ofd.FileName;
    pictureBox1.Image = ShellEx.GetBitmapFromFilePath(ofd.FileName, size);

}

image

Hope this helps! ;)

Download the source code.

Advertisements

2 thoughts on “Extract extra large Icon from a file, including network paths!

  1. absolutly nice article. maybe there’s another suggestions for improvement your little demo-tool:
    1. set the Icons size right after you selected the size in the combobox (a present you must load the Icon new to show the right icon size in you form)
    2. spend a routine to load Icons from a dll (like imageres.dll or shell32.dll)

    (I know this article is about 2.5 years old…)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s