Using Shell Icons
Written by Ian Elliot   
Tuesday, 06 October 2009

Shell Icons

Windows uses a wide range of “standard” icons, the location of which seem to be well hidden from the average application programmer. If you spend time searching the documentation you probably won’t find out much about how you can use these icons but they aren’t deliberately hidden.

The .NET framework does provide some access to standard system icons in the SystemIcons class. This returns an Icon object for any of the icons that you will find in a message box or dialog box – but not the wider range of drive and folder icons. To do this you need to know that these are stored within the Shell32.dll file in the windows\system32 directory.

There is an ExtractAssociatedIcon method in the Icon class that will extract a single icon from a file but it only returns the first icon found in the file. To pick an icon from the Shell32.dll file we have no real choice but to use the  API call – ExtractIcon.

To make the shell icons look as much like the SystemIcon class as possible we need to create a new class within the project called ShellIcons. As well as the standard classes we also need to add:

using System.Drawing;
using System.Runtime.InteropServices;

There is no need to have more than one instance of the ShellIcons class so we can declare it to be static. A static class is automatically instantiated by the system and the resulting object has the same name as the class. The idea is that we are going to add static properties to the class that return each of the icons we want to use. For example, to get the “hard disk” icon you would use something like:

Icon icon=ShellIcons.HardDisk;

To make the class static, just add the modifier “static”:

static class ShellIcons
{

We also need some constants to specify which icon. They are stored in order with the first icon having index 0. I don’t know of any documentation that gives the order of the icons in the file but it is fairly easy to discover what you are looking for by enumeration. The following constants give the position of the device icons that we are specifically interested in, you can add more:

private const int Floppy = 6;
private const int Hard = 8;
private const int Net = 9;
private const int CD = 11;
private const int RAM = 12;
private const int Unknown = 53;

The API call is easy to translate to C#:

[DllImport("shell32.dll", 
EntryPoint = "ExtractIcon")]
extern static IntPtr ExtractIcon(
IntPtr hInst,
string lpszExeFileName,
int nIconIndex);

It returns a handle to the icon stored in the file given by the values of nIconIndex and lpszExeFileName. The hInst parameter is supposed to be set to the application’s handle which is used by Windows to keep track of it.

It is quite difficult to obtain the application instance handle and, after spending some time implementing other API calls to discover it, a programming error revealed that the ExtractIcon API call doesn’t actually seem to make use of it or need it. This is undocumented so if it you find it doesn’t work with another version of Windows (I used XP and Vista) you might have to implement the code needed to obtain the application handle.

As well as using constants for the icon indexes, it also makes good programming sense to use a constant for the location of the Shell32.dll file:

const string ShellIconsLib =
@"C:\WINDOWS\System32\shell32.dll";

It is worth adding a public method that will return the icon corresponding to any index:

static public Icon GetIcon(int index)
{
IntPtr Hicon = ExtractIcon(
IntPtr.Zero, ShellIconsLib, index);
Icon icon = Icon.FromHandle(Hicon);
return icon;
}

Notice the use of the FromHandle static method to create the icon object. Now we can add the properties to return the specific icons we are going to use. As there is no logical reason why the user should even need to set these properties, we only define a get:

static public Icon FloppyDisk
{
get { return GetIcon(Floppy); }
}

static public Icon HardDisk
{
get { return GetIcon(Hard); }
}

static public Icon NetDisk
{
get { return GetIcon(Net); }
}

static public Icon CDROM
{
get { return GetIcon(CD); }
}

static public Icon RamDisk
{
get { return GetIcon(RAM); }
}

static public Icon UnknownDisk
{
get { return GetIcon(Unknown); }
}

You can, of course, add further properties to return other icons – the only problem being thinking up a name for some of them!

Trying it out

Now all we need is a little test program. Add a button to a form and enter the following code in its click event handler:

private void button1_Click(
object sender, EventArgs e)
{
Graphics g = this.CreateGraphics();
for(int i=1;i<=10;i++)
{
Icon MyIcon=ShellIcons.GetIcon(i);
g.DrawIcon(MyIcon, 10,i*32);
MyIcon.Dispose();
}
g.Dispose();
}

The first line simply gets a graphics object so that we can draw on the form using the GDI+. Next the for loop retrieves the first 10 icons and uses the DrawIcon method to draw them with a suitable spacing on the form. Finally its is always a good idea to dispose of any GDI+ resources you have created to avoid memory leaks.

icons

If you would like the code for this project then register and click on CodeBin.

<ASIN:0321116208>

<ASIN:1590594398>

<ASIN:1930110286>

<ASIN:186100737X>

 

 

 

 

 

Last Updated ( Tuesday, 06 October 2009 )