How to Use Windows Shell Icons in VB.NET

Folder Options

Folder Options

This tutorial explains how to code a simple, reusable Shell Icon Manager component in VB.NET. Windows maintains registered file types and associated icons as seen in the Folder Options dialog box, but there is no way to access them through managed (.NET) code, so we must use Win32 API functions to access them through the Windows shell.

This shell icon manager is useful if you need to:

  • Produce a list of file type / icon associations similar to the “Registered file types” in Windows XP.
  • Get a file’s real icon instead of using icons that look similar, so you can develop an interface that is more streamlined with the native Windows interface.
  • Create a file browser similar to Windows Explorer by tapping into the same icons that Windows Explorer uses. Pair this idea with my other article titled How to Create a TreeView File Browser Component in VB.NET and you’ll have a file browser that closely resembles Windows Explorer.

Getting Started

I assume you are already familiar with Visual Basic and Visual Studio, so let’s jump right in.

  1. Open Visual Studio and create a new Windows Forms Application. Give it a name of Shell Icon Manager.
  2. Add a new class to your project. Give it a name of ShellIconManager.vb. This class will contain our re-usable Shell Icon Manager component.
  3. Open up the code view for the ShellIconManager.vb class and declare a new hashtable called cIcons. This will be used to store and retrieve icons that we have already retrieved from the Windows shell.

Your class should look like this:

Public Class ShellIconManager
    Dim cIcons As New Hashtable
End Class

Implementing the API functions

Because there is no way to get a file’s associated icon through managed code (as far as I know), we must implement some API functions. Open up the code view for the ShellIconManager.vb class, and add this code to the general declarations section:

' declare the Win32 API function SHGetFileInfo
Public Declare Auto Function SHGetFileInfo Lib "shell32.dll" (ByVal pszPath As String, ByVal dwFileAttributes As Integer, ByRef psfi As SHFILEINFO, ByVal cbFileInfo As Integer, ByVal uFlags As Integer) As IntPtr
' declare some constants that SHGetFileInfo requires
Public Const SHGFI_ICON As Integer = &H100
Public Const SHGFI_SMALLICON As Integer = &H1
' define the SHFILEINFO structure
Structure SHFILEINFO
    Public hIcon As IntPtr
    Public iIcon As Integer
    Public dwAttributes As Integer
    <Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst:=260)> _
    Public szDisplayName As String
    <Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst:=80)> _
    Public szTypeName As String
End Structure

Basically all we are doing here is coding everything that is needed by the SHGetFileInfo function.

  • Line 02 – we declare the Win32 API function SHGetFileInfo so we can call it from managed code. This is the function that will allow us to get a file’s associated icon.
  • Lines 04, 05 – we declare some constants which are required by the SHGetFileInfo function.
  • Lines 07 through 15 – we define the SHFILEINFO structure which is required by the SHGetFileInfo function.

According to this MSDN article, the SHFILEINFO structure contains information about a file and is used with the SHGetFileInfo function. OK, you might be wondering what the purpose of SHFILEINFO is when we already have the System.IO.FileInfo object available to us through managed code. The reason is that SHGetFileInfo requires an SHFILEINFO structure to be passed in as an argument, NOT a System.IO.FileInfo object. Now it’s time to start coding some of our own functions which will wrap up these API functions nicely and provide a more streamlined way of using them.

Implementing the RetrieveShellIcon method

This method should do exactly what the name implies: get the file’s associated icon from the Windows shell.

  1. Let’s start by defining the RetrieveShellIcon function. Add this code to the ShellIconManager.vb class:
Function RetrieveShellIcon(ByVal argPath As String) As Image

End Function

We designed this method to accept one argument, argPath, which is the path of the file whose icon we wish to retrieve. We also specified that the function should return an Image object.

  1. Next we’ll declare some variables in this new method:
Dim mShellFileInfo As SHFILEINFO
Dim mSmallImage As IntPtr
Dim mIcon As System.Drawing.Icon
Dim mCompositeImage As Image
  1. Now that we defined the basic structure for the RetrieveShellIcon method, it’s time to write some code that will actually do some work:
' get the file info and small image for this file from the shell
mShellFileInfo = New SHFILEINFO
mShellFileInfo.szDisplayName = New String(Chr(0), 260)
mShellFileInfo.szTypeName = New String(Chr(0), 80)
mSmallImage = SHGetFileInfo(argPath, 0, mShellFileInfo, System.Runtime.InteropServices.Marshal.SizeOf(mShellFileInfo), SHGFI_ICON Or SHGFI_SMALLICON)

Some of this code might seem a little absurd at first due to the quirky nature of Win32 API programming. Allow me to explain what’s happening:

  • Line 02 – we create a new instance of the SHFILEINFO structure called mShellFileInfo.
  • Line 03, 04 – we fill the szDisplayName and szTypeName strings with null characters. This must be done because these strings are defined with a fixed length in the SHFILEINFO structure.
  • Line 05 – we call the SHGetFileInfo function, which returns a pointer to the file’s icon and populates mShellFileInfo with values.

We’ll want to convert the file icon pointer into an actual image so we can make use of it:

' create the icon from the icon handle
Try
    mIcon = System.Drawing.Icon.FromHandle(mShellFileInfo.hIcon)
    mCompositeImage = mIcon.ToBitmap
Catch ex As Exception
    ' create a blank black bitmap to return
    mCompositeImage = New Bitmap(16, 16)
End Try
' return the composited image
Return mCompositeImage

So now we have a way to get a file’s associated icon from the Widows shell. However, we need a way to manage all of these icons so we can re-use the icons we have already retrieved from the Windows shell.

Implementing the GetIcon method

What good is our Shell Icon Manager if it doesn’t manage anything? What we need now is a method that will store the icons we retrieve from the Windows shell in a hashtable, as well as retrieve them from the hashtable:

Function GetIcon(ByVal argFilePath As String) As Image
    Dim mFileExtension As String = System.IO.Path.GetExtension(argFilePath)
        ' add the image if it doesn't exist
        If cIcons.ContainsKey(mFileExtension) = False Then
        cIcons.Add(mFileExtension, RetrieveShellIcon(argFilePath, True))
    End If
    ' return the image
    Return cIcons(mFileExtension)
End Function

Usage

This usage example will populate a DataGridView with a list of files from your C:\ drive, assuming the DataGridView has an ImageColumn and a TextColumn:

Dim mShellIconManager as New ShellIconManager
For Each mFilePath as String in My.Computer.FileSystem.GetFiles("C:\")
    DataGridView1.Rows.Add(mShellIconManager.GetIcon(mFilePath), mFilePath)
Next

As you can see, it’s very simple to use!

Adding Features

We can even add support for controls which require an ImageList, such as the ListView control. Add this code to the ShellIconManager.vb class declarations:

Public IconList As New ImageList

Now whenever you call GetIcon, it will also add the file’s extension/icon to the IconList. Here’s some code to make it work:

Dim mShellIconManager as New ShellIconManager
For Each mFilePath as String in My.Computer.FileSystem.GetFiles("C:\")
    DataGridView1.Rows.Add(mShellIconManager.GetIcon(mFilePath), mFilePath)
Next

Related

Tags: , ,

2 Comments

  1. Steve Lewis says:

    These tutorials are really marvellous for hobbiest code bodgers like me, many thanks for taking the time to share your knowledge and experience to struggling techno bodgers like me.

    Just one cheeky question, have you any information on interfacing with RS232 COM ports I know I have to use the Win32 API but just can’t figure it out

    Anyway, really effort and once again cheers

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>