Associate Icon To File Extension

J.C. SolvoTerra 0 Tallied Votes 1K Views Share

Take Heed

There are lots of things to be aware of with the Windows Registry. In most cases the best thing to be aware of is that ideally you should avoid playing with it at all costs. Alas, we are what we are and we do what we do. My advice to you is to make a back up of your registry. Remember though, values are read and written continuousley without our knowledge, and reverting to a backup made 10 minutes ago may not reflect your current systems settings so be careful.

Backup Your Registry

Start RegEdit, Search for RegEdit in either the run or search box in your system's start menu. Once you have opened RegEdit goto menu Start>Export. In the save dialog box you will see a group box titled "Export Range". Select the "All" radio, choose your destination and file name and hit export.

Using Visual Studio to Create Your Icon Association

If you would prefer a much safer option, use VS's Extension Manager. Properties>Publish>Options>File Association. The headings of each field reflect the const values in the source provided.

Using This Source (Assumes)

I have told this association to use the current project's executable Icon, so you need to set a default icon for your project. You can point to any .ico, .dll, .exe you wish, but remember that the referenced object needs to be available, in the same location on the users computer too.

In With the New, Out With the Old

This is the second snippet I have posted on this subject, the first I asked to be removed as it was not 100% accurate to most developers needs. The problem with the internet today is that, for so long information will be used, then when it is time to update this information, new articles are published but there remains hundreds of other articles referencing the same material. That's what happend to me with my first post.

99% of all sites I researched on this subject point the developer to the registry's HKEY_CLASSES_ROOT or HKCR. After publishing my first snippet, came across a document that stated that this key is still in use, primarily for backward compatibility, and shouldn't really be used. When I say backward I mean waaaaaaaaay backward, 16bit Win3.1 and 95 backward. Though many organisations including Microsoft and Adobe still use this key, it is not in our best interest to go through the security issues thrown up trying to access the keys.

No too much has changed, and many developers will be familiar with the HKEY_LOCAL_MACHINE(HKLM) and HKEY_CURRENT_USER(HKCU) keys. They have been around for almost as long as HKEY_CLASSES_ROOT if not for the same time, but it's these keys we should be working with now.

For our purposes we are going to be accessing the subkeys \Software\Classes.

A Little About HKEY_LOCAL_MACHINE\Software\Classes

The HKLM key and all it's sub keys are protected and your application needs to be run as administrator for it to be able to access the keys. Note: Being inside an Administrator Account doesn't mean your programs are being run as an administrator, you actually have to right-click on your program's icon and select "Run as Administrator".

There are ways round this, and I do have a class that takes ownership of registry keys, but that is way, way beyond the scope of this (what seems to be developing into an artical) snippet. I have no plans on publishing this class in the future either, mainly because I didn't write it, but the ability to take ownership is out there. click here for the developers site

Scratching the Surface of What This Source is Doing

Let's start by inspecting the registry, and looking at a familiar extension. In this case we'll have a look at .bmp.

The Registry path to locate this extension is as follows:

HKEY_CURRENT_USER\Software\Classes\.bmp

Once there, there are clues to what happens next

a7bb426cef9fd0fdb120e2909982663d

As you can see from the key values on the right, the default key is the ProgID. As you can see, this suggests my .bmp files open with photoshop* This ProgID also resides as a subkey of Classes, so lets have a look for:

HKCU\Software\Classes\PhotoShopBMPFile.12

948b81dbb89f5c72d5410bf435c2d9cd

The ProgID isn't there? Ok let's check the HKLM\SOFTWARE\Classes keys.

704628b0625f9f018417d8aa5292109d

There it is. Because PhotoShopBMPFile.12 is a user wide class, it resides as a subkey of HKLM key.

Now don't be lead into thinking it's as simple as that. There are keys which also hold the users currently selected open-with program, and keys that holds lists of OpenWith programs within HKCR, HKLM and HKCU.

Let's not go any deeper into this for now as it is in danger of blurring the simplicity of the source above.

What Does This Source Do.

We are going to be making new subkeys under HKCU. We will be creating the Extension Key, in this case ".test". A ProgID key "MyCompany.MySoftware", and a DefaultIcon key "DefaultIcon". These keys will mimic what we have seen above, but because we are only setting keys for the current user and not user-wide, the extension key and it's associated ProgID key will both be under HKCU\Software\Classes. DefaultIcon will be a subkey of ProgID.

The first thing the source does is to check to see if a key with our extension already exists. IF the extension does exist, we will not continue.

If Registry.CurrentUser.OpenSubKey("Software\Classes\" & APP_EXTENSION, True) Is Nothing Then

If the key doesn't exist, lets create it.

Registry.CurrentUser.CreateSubKey("Software\Classes\" & APP_EXTENSION). _
    SetValue("", PROG_ID, Microsoft.Win32.RegistryValueKind.String)

The SetValues "Name" argument is left blank, this will create a default value. The "Value" its-self will be set to our app's ProgID "MyCompany.MySoftware" and the final argument specifies the type of value, in this case it's a string. Each key can have any number of values.

48f04cba8e94ee6ad8a6a65c07956efa

Once we have created the extension key, we need to create a ProgID key which can be referenced from the extension key.

Registry.CurrentUser.CreateSubKey("Software\Classes\" & PROG_ID).SetValue("", FILE_DESCRIPTION)

Whilst creating the ID key we will add a default value which displays the file's description.

011dcaa141c1cfddb21c7a5203946337

Then Last of all we create a subkey of our ProgID, DefaultIcon.

Registry.CurrentUser.CreateSubKey("Software\Classes\" & PROG_ID & "\DefaultIcon").SetValue("", _
            Application.ExecutablePath & ",0")

Again we have left the values name blank so it is set to default. Because I am referencing an exe I have also passed an argument "0". This tells the system to find the object at index 0.

1dccfce4902326c0952dfce1f1396fb5

And that's it. We have created an association between file type and it's icon. Remember though, this is only available to the current user.

Removing The Association.

I'm not going to go into too much detail here as the code is pretty self explanitory. One thing to note, keys cannot be deleted that have subkeys, so you have to remove all children before deleting a key it's self. The second argument of the DeleteSubKey method is a boolean specifying if an exception should be thrown if the key acannot be found.

    'Remove Extension Key
    Registry.CurrentUser.DeleteSubKey("Software\Classes\" & APP_EXTENSION, False)

First we move the SubKey DefaultIcon

    'Remove DefaultIconKey
    Registry.CurrentUser.DeleteSubKey("Software\Classes\" & PROG_ID & "\DefaultIcon", False)

Then we remove the ProgID key

    'Remove PROG_ID Key
    Registry.CurrentUser.DeleteSubKey("Software\Classes\" & PROG_ID, False)

If you try to remove ProgID before DefaultIcon, an exception will be thrown.

Ok, So What's Next?

There have been a few clues in the images above as to "Shell\Open\Command" and "OpenWithProgids". In the future I hope to post two further snippets.

  1. How to assign a file extension to your program, and have your program open and have arguments passed to it when the associated file is double clicked. And Second, IF you don't wa

  2. Ensure your program is listed in the OpenWith menu on your system menu when your chosen file extension is right clicked.

Why the need for snippet 2 if I have snippet 1? Fair question. Let's say you are working on a program that edits .doc, or converts them into another format. Are you really going to want to hijak the current association e.g. WORD.EXE. The user may want to use your program a couple of times, but it's more likely they will want to open a word document in word. Actually providing the user with an option to open the document in your program instead of assuming they will always want to, I think, is better practice.

*My default program to open a .bmp isn't actually PhotoShop, but here it certainly seems it might be.

Imports Microsoft.Win32
	
	Private Const APP_EXTENSION As String = ".test"
    Private Const PROG_ID As String = "MyCompany.MySoftware"
    Private Const FILE_DESCRIPTION As String = "A Test File Icon Association"

    Public Sub CreateDefaultIconAssociation()

        'Check To See If Extension Exists. Create New Keys If It Doesn't
        If Registry.CurrentUser.OpenSubKey("Software\Classes\" & APP_EXTENSION, True) Is Nothing Then

            'Create The Extension Key
            Registry.CurrentUser.CreateSubKey("Software\Classes\" & APP_EXTENSION).SetValue("", _
				PROG_ID, Microsoft.Win32.RegistryValueKind.String)
				
            'Create The PROG_ID Key and File Description Value
            Registry.CurrentUser.CreateSubKey("Software\Classes\" & PROG_ID).SetValue("", FILE_DESCRIPTION)
			
            'Create DefaultIcon And Set Default String To Icon File (ICO or EXE and DLL With Perameters)
            Registry.CurrentUser.CreateSubKey("Software\Classes\" & PROG_ID & "\DefaultIcon").SetValue("", _
				Application.ExecutablePath & ",0")

        End If

    End Sub

    Public Sub RemoveDefaultIconAssociation()

        'Remove Extension Key
        Registry.CurrentUser.DeleteSubKey("Software\Classes\" & APP_EXTENSION, False)
		
        'Remove DefaultIconKey
        Registry.CurrentUser.DeleteSubKey("Software\Classes\" & PROG_ID & "\DefaultIcon", False)
		
        'Remove PROG_ID Key
        Registry.CurrentUser.DeleteSubKey("Software\Classes\" & PROG_ID, False)

    End Sub