Introduction

How can we get the icon of a file if it does not exist? If we only know its name or even its extension, how do we get the icon associated with it? This is a simple project of mine to get the registered file types and the associated icons by reading from Windows Registry. The idea belongs to Aaron Young in his article "Listing the Registered File Extensions and Their Associated Icons" at CodeGuru, but the original source code was in Visual Basic. I know a little bit of VB, so I tried to rewrite it in C#. Thanks very much to Aeron Young!

Background

Because this method reads information from the Windows registry, we need to know some basics.

Firstly, where are the registered file types? They are contained in the key HKEY_CLASSES_ROOT . Now, you launch the Regedit (some way can be: Start > Run > regedit), and go to that key. You may find a lot of sub keys like this: " .zip ", " .mp3 ", etc. Those are the file types we need. Pay attention to the value named empty (Default) as in the picture below:

The value (Default) is not a file path, you can easily find out that the data of default value (here: NeroPhotoSnapPreview.File7.png ) is the name of another sub key in HKEY_CLASSES_ROOT . Following is what we can find:

Now, what do you see for the value of DefaultIcon? That is the path to the file that contains the icon we need! Our tasks now are getting that file path and retrieving it to Windows Form.

Secondly, we need to interact with the Registry. .NET supports class RegistryKey in Microsoft.Win32 namespace and makes it easy for us to open a registry key and get its values as well as its sub keys. Walking a registry tree is the same as walking a file/folder tree structure, it is very easy!

Thirdly, how to retrieve an icon from a file ? There is a little bit of difficulty here, we need to use an API function: ExtractIcon, but don't worry! All our works are redefining it in C#, and passing the correct parameters for it. Another choice is the function of ExtractIconEx, but we will consider the ExtractIcon first.

Using the Code

I packed all necessary methods and API functions in the class RegisteredFileType.

Note that this function returns a Hashtable, you can store information in an array of string, but by using Hashtable we will more conveniently get the icon file name from its file extension. Remember to use Microsoft.Win32 namespace which contains class RegistryKey.

First of all, we need to get HKEY_CLASSES_ROOT:

RegistryKey rkRoot = Registry.ClassesRoot;

Next, we get all sub keys of it to get all file types:

string[] keyNames = rkRoot.GetSubKeyNames();

To prevent the sub key that is not a file type (eg: ACLFile), we detect the dot sign:

This will lead us to the key that contains the icon file, the last one is adding this file path and the index of the icon to the hash table as a value and its associating filetype as the key:

iconsInfo.Add(keyName, fileParam);

The EmbeddedIconInfo Struct

Note that the information got by the above method is just draw data. We need a structure to store the information associated with the icon file. Therefore, we create the EmbeddedIconInfo to pack the necessary information which are the name of file containing the icon and the index of the icon:

In this section, I give you another solution to solve the problem. Instead of using the function ExtractIcon, we can use the Windows API ExtractIconEx. This method is more powerful than ExtractIcon because it creates an array of handles to large or small icons extracted from the specified executable file, DLL, or icon file.

When I find the definition of ExtractIconEx [^] in www.pinvoke.net, I also see a good example in the site. That's why I use it in this application. Based on the example, I add an overloaded version of the method ExtractIconFromFile in class RegisteredFileType:

The code is easy to understand. The unsafe keyword helps the encapsulated code to run faster. To compile the application with unsafe code, we must configure the application that allows unmanaged code. In Visual Studio, right click on the project and go to tab "Build":

Now, we make some changes in IconForm to test the new version of the method ExtractIconFromFile.

Points of Interest

I had added a simple search feature. It is absolutely easy because all the keys and their values were stored in Hashtable icons, so we can quickly access the searched information like accessing an array.

"If this value is a negative number and either phiconLarge or phiconSmall is not NULL, the function begins by extracting the icon whose resource identifier is equal to the absolute value of nIconIndex. For example, use -3 to extract the icon whose resource identifier is 3."

The following two lines should be removed from getEmbeddedIconInfo(string fileAndParam):

Public Shared Function GetFileTypeAndIcon() As Hashtable
Try
' Create a registry key object to represent the HKEY_CLASSES_ROOT registry section
Dim rkRoot As RegistryKey = Registry.ClassesRoot
'Gets all sub keys' names.
Dim keyNames As String() = rkRoot.GetSubKeyNames()
Dim iconsInfo As New Hashtable()
'Find the file icon.
For Each keyName As String In keyNames
If [String].IsNullOrEmpty(keyName) Then
Continue For
End If
Dim indexOfPoint As Integer = keyName.IndexOf(".")
'If this key is not a file exttension(eg, .zip), skip it.
If indexOfPoint <> 0 Then
Continue For
End If
Dim rkFileType As RegistryKey = rkRoot.OpenSubKey(keyName)
If rkFileType Is Nothing Then
Continue For
End If
'Gets the default value of this key that contains the information of file type.
Dim defaultValue As Object = rkFileType.GetValue("")
If defaultValue Is Nothing Then
Continue For
End If
'Go to the key that specifies the default icon associates with this file type.
Dim defaultIcon As String = defaultValue.ToString() & "\DefaultIcon"
Dim rkFileIcon As RegistryKey = rkRoot.OpenSubKey(defaultIcon)
If rkFileIcon IsNot Nothing Then
'Get the file contains the icon and the index of the icon in that file.
Dim value As Object = rkFileIcon.GetValue("")
If value IsNot Nothing Then
'Clear all unecessary " sign in the string to avoid error.
Dim fileParam As String = value.ToString().Replace("""", "")
iconsInfo.Add(keyName, fileParam)
End If
rkFileIcon.Close()
End If
rkFileType.Close()
Next
rkRoot.Close()
Return iconsInfo
Catch exc As Exception
Throw exc
End Try
End Function

I see in the code for "GetFileTypeAndIcon" that you call "RegistryKey.Close" directly, but without any "try...finally" block. In case of exception, these registry keys won't be properly disposed. To keep it clean and safe, you should use "using" when you get the keys, as in the revised source below. According to the documentation, calling "Close" or disposing a system key has no effect, so the first "using" is technically superfluous, but avoids confusion for those who aren't sure whether system keys should be closed or not.

hey thanks a lot this really helped me but i have this problem, i modified the code so it would accept negative indexes, but when it comes to pdf and xml files , it doesnt return the right path and index, in xml it returns a %1, i can manually insert the path and index for both because i know where they are but if i use the same program in another computer it wont work for those two file types

While looking for the same solution I stumbled across this article and cringed at it's complexity. Ferocks, you are exactly correct the simple solution to the finding a file icon is to use System.Drawing.Icon.ExtractAssociatedIcon(string FilePath). If you want to show the icon in a PictureBox then add the output method .ToBitmap().

For some type of file icons are wrong or not present, ex: txt, html,pdf and othe common icons.
I debugged step by step the code and I found the problem, code always take the first icon of package! and the first default extension!Fix n1This code fix pdf extension [proposed by type5demonlord]
In in method

Of course add some error processing. Regular expressions make the code more readable and smaller.

Second if the index is less than zero you make it zero. I don't believe this is correct, ExtractIcon uses negative numbers, EXECPT for -1. I think ExtractIconEx avoids this problem. I would not modify the index unless it was -1.

I think it is great that you post your effort, this is not to critize you but to give you a few things to think about.