Click here to Skip to main content
15,889,281 members
Articles / Programming Languages / C++

Multiple Folder Icons

Rate me:
Please Sign up or sign in to vote.
4.17/5 (9 votes)
5 Jul 2007CPOL9 min read 69.2K   2K   44   10
Shell Icon Overlay Handler that allows you to map folder icons to folder paths.

Example of mutiple folder icons

Introduction

There are two ways to change a folder icon in Windows:

  • Change the icon for a specific folder (right click and choose "Customize This Folder...")
  • Change the icon used for all folders globally

But what if you want to use different icons for a subset of folders? Why would you want to? Consider the following situations:

  • You have a drive mapping (or subset) that sometimes points to one set of folders (say production) and sometimes to another set (say development)
  • You have two connected machines (say your laptop and your desktop) with identical folder structure for synchronized projects
  • You want to quickly identify parts of the folder structure
  • err...
  • ...that's it.

In all the above examples, the ability to select an icon for parts of the folder structure can be a real benefit. Unfortunately, there is no easy way in Windows to do this (you could of course change the icon manually for each folder!).

Multi-Folder Icons (MFI) lets you do this easily - the image above shows it in operation with several distinct groups of folders shown using different icons.

Many File Open dialogs are implemented using an Explorer view so you also get the benefit of the custom icons when opening and saving files:

File Open dialog

Background

MFI is an Icon Overlay Handler (IOH). Here is a brief extract from the Microsoft documentation about these components:

Icon overlays are small images placed at the lower-left corner of the icon that represents a Shell object in Microsoft Windows Explorer or on the desktop. They are used to add some extra information to the object's normal icon. A commonly used icon overlay is the small arrow that indicates that a file or folder is actually a link. You can specify custom icon overlays for Shell objects by implementing and registering an icon overlay handler.

The basics of implementing an IOH are described in various places - here for example. This article assumes familiarity with the basic concepts involved.

As noted above, the intention is that an IOH provides the Shell with "small images" to overlay on certain Shell objects. However, there is nothing to prevent us from using a 'full-sized' icon. Thus we can return an icon that completely overlays the normal folder icon with an image of our choosing.

It is easy enough to develop an IOH which checks each file path passed to the IsMemberOf method of the IShellIconOverlayIdentifier interface. The component can use simple string comparison functions on the path to identify the folders of interest, e.g., all folders on the "Z:" drive. Obviously, we also need to test whether the path actually points to a folder - again, this is a straightforward use of the Shell API function SHGetFileInfo.

Multiple Icons

Things get more complicated when we want to have a different icon for folders on the Z: drive and folders on the Y: drive, for instance. Unfortunately, Explorer only allows one icon for each IOH and this is requested when the IOH is initialized. There is no way for a single instance of an IOH to use different icons for different sets of folders.

The solution is to register multiple IOHs - one for each set of folders you want to match. Of course, each IOH can be an instance of the same component which configures itself when Explorer creates it (based on some information stored in the Registry). If you want another set of folders to be displayed with a different icon, you only have to add another configuration entry to the Registry - this is what MFI does.

The next two sections describe the implementation and the configuration required to use the component.

Implementation

We need to provide multiple IOH components in the same DLL. The obvious way to implement several near identical classes would be to provide a template class to handle the common functionality, e.g.:

C++
////////////////////////////////////////////////////////// 
// CMFI_IconImpl - Template class for common functionality 
           
template <CLASS _T,const CLSID* _pCLSID,UINT _RES, UINT _ICONINDEX> class 
ATL_NO_VTABLE CMFU_IconImpl : 
    public CComObjectRootEx<CCOMSINGLETHREADMODEL>, 
    public CComCoClass<_T,_pCLSID>,
    public IShellIconOverlayIdentifier 
{ 
public: 
    CMFI_IconImpl() { }

    DECLARE_MY_REGISTRY_RESOURCEID(_RES) 
    DECLARE_NOT_AGGREGATABLE(_T) 
    DECLARE_PROTECT_FINAL_CONSTRUCT()
    
    BEGIN_COM_MAP(_T) 
       COM_INTERFACE_ENTRY(IShellIconOverlayIdentifier) 
    END_COM_MAP()
    
public: 
    // IShellIconOverlayIdentifier Methods
    STDMETHOD(GetOverlayInfo)(LPWSTR pwszIconFile, 
              int cchMax,int* pIndex,DWORD* pdwFlags);
    STDMETHOD(GetPriority)(int* pIPriority);
    STDMETHOD(IsMemberOf)(LPCWSTR pwszPath, DWORD dwAttrib);
};

Specialised versions derived from this class can then be used to implement the specifics for each component, passing the CLSID as a template parameter.

This approach has the benefit of reducing the amount of code you need to write (in fact, I use it in another DLL which implements a number of IOH components which identify files which are ReadOnly, Unicode, or "Loaded-in-memory"; if you are interested, these will soon be available here).

Unfortunately, this would still not solve the problem of being able to dynamically create instances of the IOH for each folder class - we would have to rebuild the DLL each time we need to add a new one.

What is needed is a component which can initialize itself based on some piece of configuration information - the CLSID is the outstanding candidate for this. For each IOH, the CLSID will be passed to GetClassObject in order to get an IClassFactory interface so that Explorer can generate an instance of the IOH component. The class factory component that we create can cache this value and later use it to look up the appropriate settings for each IOH instance it generates (there will only be one as it happens). When CreateInstance is called on the IClassFactory interface, each new instance of a generic IOH component can then be initialized using the stored settings.

In the implementation of MFI, both the class factory object and the IOH component implement this interface:

C++
interface IMFI_Control : IUnknown
{
   [id(1)]
   HRESULT Configure(REFCLSID refclsid);
};

which does the necessary lookup of parameters based on CLSID.

The only thing left to do is to make sure our initialization code is called so that we can add the necessary entries under ShellIconOverlayIdentifiers. This will be done when we first register our COM component using RegSrv32.exe. Once this has happened, Explorer will always load our DLL (unless we remove all the keys).

Configuration & Settings

The MFI Configurator utility can be used to manage all MFI settings. The settings are described fully in the help text supplied with this application. The following summary is for information purposes only.

Explorer opens the following Registry key when it starts: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\ CurrentVersion\Explorer\ShellIconOverlayIdentifiers.

Each subkey found is treated as an Icon Overlay Handler component and the default value of the key is the CLSID of the component in question.

MFI dynamically creates entries under this key for each folder mapping that you want. It also creates the Registry entries that COM requires to locate the component. The configuration is held in the Registry and looks like this:

registry keys

Where the set of values for each key looks like this:

registry values

The entries have the following meaning:

  • CLSID - This is the CLSID of the component that Explorer will create. Note that MFI will generate a new CLSID if one is not present so you don't need to manually add these entries.
  • IconFile - This is the full path to the icon file to use for this set of folders. If it is not found, MFI will use its default blue folder icon.
  • Path - This is a partial path string that MFI will use to match against the path of each file that is being displayed.
  • Recurse ("Include Subfolders") - If this flag is not set, only one level of the folder structure will match. For instance, "d:\devel" will match "d:\devel" but not "d:\devel\test". The default is that this flag is set.
  • MatchType - This controls how the partial path is matched against the target path. The values and their explanation are as follows:
MatchTypeOperation
0Matches if the partial path appears anywhere in the target path. This is the default.
1Matches only if both paths are the same.
2Matches if the partial path matches the start of the target path. Additional trailing characters are ignored.
3Matches if the partial path matches the end of the target path. Preceding characters are ignored.

Network Drives

There is one more configuration parameter which MFI handles - IgnorePaths. This is a REG_SZ value of partial paths delimited by ";". Each path is matched against the start of the target path. If it matches then MFI ignores it - this can be useful for network drives which may not always be switched on. Note - it is a good idea to put the "A:" (or other floppy drive) in this list as MFI wastes about a second at startup trying to access this drive. The delay only happens when Explorer is started for the first time.

Comments & Outstanding Issues

  • If you make changes to the set of folder mappings, you need to force Explorer to reload the icon overlays. To do this, you need to restart your PC (no thanks) or kill all instances of Explorer.Exe and start a new instance. Doing this means you lose some things that Explorer controls like some of the systray icons. This can be annoying but as yet I haven't found a satisfactory way to get Explorer to reload MFI dynamically.
  • There is a Shell function called SHLoadNonloadedIconOverlayIdentifiers which is supposed to force the Shell to load new Icon Overlay Handlers - this would solve the above problem if it worked! Unfortunately, it doesn't appear to make any difference if the DLL is already loaded even if a new Registry entry has been added under the ShellIconOverlayIdentifiers key.
  • There is a noticeable delay when Explorer renders the folder icons. During this delay, you will see the normal folder icons which are then gradually replaced by the ones that we use. This is more noticeable in some situations than in others but is something that happens with all overlay icons. It is more noticeable with MFI because the whole icon can be 'replaced'. Personally, I can live with this but if you don't like it, then this component is not for you!
  • You might have noticed that there is a slight overlap in the tree view for the selected folder. This is because Explorer uses the open folder icon (Open folder icon) for the currently selected icon and the closed folder icon (Closed folder icon) for all the others. There is a slight overlap between these two icons so if the icon being used has a transparent background, you can see the original icon below. The simple solution is to use icons which have an opaque background - and one which matches the background colour of your windows. Alternatively, stop ruining your eyesight and ignore it!
  • If you try to start the UI portion of Explorer as the machine is starting up, you will notice a significant delay. This is because the folder view will display before network initialization has finished. If you have lots of network drives, MFI will try to access each one to determine if it is a folder - this will cause a delay of about 30 seconds for each network drive.
  • The Path setting could be extended so that it is a delimited list of partial paths. This way you could more easily map multiple sets of folders to the same icon. Currently you will have to create a mapping for each set.
  • A better test for network connectivity to a machine would eliminate the need for IgnorePaths.
  • According to Microsoft's documentation, there is a limit of 15 Icon Overlay Handlers.
  • I have not tried this on Vista - and probably won't for a long time.

History

  • 05/07/07: Links to Configurator added.
  • 04/05/07 First posted to CodeProject.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
RantTHIS IS ALL SO CONFUSING!!!! Pin
bob fog29-Aug-08 22:30
bob fog29-Aug-08 22:30 
QuestionCan not open and compile [modified] Pin
LivelyRoger20-Sep-07 9:07
LivelyRoger20-Sep-07 9:07 
AnswerRe: Can not open and compile Pin
Keith Skilling21-Sep-07 2:09
Keith Skilling21-Sep-07 2:09 
GeneralProject problem Pin
Shady.demon10-Jul-07 5:56
Shady.demon10-Jul-07 5:56 
GeneralRe: Project problem Pin
Keith Skilling10-Jul-07 11:29
Keith Skilling10-Jul-07 11:29 
GeneralClosing Windows Explorer Pin
djhayman5-Jul-07 20:41
djhayman5-Jul-07 20:41 
GeneralRe: Closing Windows Explorer Pin
Keith Skilling5-Jul-07 20:59
Keith Skilling5-Jul-07 20:59 
GeneralRe: Closing Windows Explorer Pin
Keith Skilling5-Jul-07 22:08
Keith Skilling5-Jul-07 22:08 
GeneralQuestion Pin
NGS 5496724-May-07 5:51
NGS 5496724-May-07 5:51 
AnswerRe: Question Pin
Keith Skilling4-May-07 21:53
Keith Skilling4-May-07 21:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.