Item Icons and Thumbnails

What is the difference between icons and thumbnails?

  • Icon is a Windows binary format. An icon may contain one or more small images at multiple sizes and color depths, so that they can be scaled appropriately. Because CBFS Shell supports only Windows 7 and higher, we recommend you use 32-bit color depth format for your icons. Icons may be stored in .ICO files (that can contain all images at multiple sizes) or in binary files like .EXE or .DLL, as Win32 resources. In the latter case, an icon (and all images at multiple sizes) is represented by the binary file path and an icon index. Note that the icon index is not the same as resource index.
  • Thumbnail is an image that the Shell uses to represent a Shell item. Thumbnails are stored not in binary icon format but rather in standard PNG, JPG, or BMP format. We recommend the PNG format.

Note: The maximum size of an image displayed by the Shell in the standard folder view is 256 pixels (icon or thumbnail). This is different from what can be displayed by the Explorer's preview pane. Although a maximum size exists, the Shell is capable of resizing images, to some extent, and at variable quality level.

The best way to support transparency for Shell icons and thumbnails is to use either icon files with 32-bit color depth or .PNG files, which support transparency through an alpha channel.

Depending on how the Shell is configured or used, each Shell item can be represented physically by an icon or a thumbnail (historically, the Shell supported only icons). This is sometimes confusing because the terminology in the Shell UI always uses the term "icon". The following screenshot shows an opened extension that contains photos as Shell items. Even in the Details view, it is possible to display a dynamically computed thumbnail of the image itself (16x16 pixels). This works only if we use a dynamic image file (which is easier to create than a dynamic icon file):


CBFS Shell proposes a unified experience to developers with the ShellItem's class Thumbnail property, of type ShellThumbnail:

public virtual ShellThumbnail Thumbnail { get; protected set; }

The ShellThumbnail class is also provided by CBFS Shell and can use icons or images, always stored in physical files, to represent a Shell item visually, which is independent from the Shell context. You can provide this class with only an icon file path (possibly containing multiple images of different sizes), with only an image file path, or with both. If an image is required by the Shell and you have provided only an icon, CBFS Shell will convert the icon to an image. If an icon is required by the Shell and you have provided only an image, CBFS Shell will convert the image to an icon, and so on.

For example, following is how the ShellThumbnail of a folder is defined:

public static readonly ShellThumbnail Folder = new ShellThumbnail {
    IconLocation = "shell32.dll",
    IconIndex = 4 
};
...
public ShellFolder(ShellFolder parent, ShellItemId id)
    : base(parent, id)
{
    ...
    Thumbnail = ShellThumbnail.Folder;
    ...
}
 

This uses the standard Windows shell32.dll file (note the full path does not need to be specified for Windows well-known DLLs), and the icon index 4, which is this icon (in multiple sizes) in Windows 10 (only 32-bit color depth are shown and used):


If you need custom icons, you should use the standard ShellThumbnail class. This example is demonstrated in the Device Manager Folder sample.

CBFS Shell also provides two classes derived from ShellThumbnail:

  • IconShellThumbnail: This class can use a GDI+/Winforms. System.Drawing.Icon instance to represent a Shell item: This class writes icons to a physical file path as a cache.
  • AssemblyResourceShellThumbnail: This class can retrieve the thumbnail from a .NET assembly resource. Note that this class writes the icon to a physical file path as a cache. Usage is demonstrated in the Registry Folder sample.

The IconShellThumbnail class is useful if you add Icons to a .resX file in your .NET project. Consider, for example, that you have a Resource1.resx file that contains two icon resources in your .NET project:



In this case, you could declare an instance of the IconShellThumbnail like this to declare a ShellThumbnail corresponding to Icon1:

// write the file only once per session
// here typeof(MyFolder).FullName is just used as a unique and permanent string
// Icon1 is a property of System.Drawing.Icon type that is automatically generated by the Visual Studio's RESX generator.
private static readonly IconShellThumbnail MyIcon = new IconShellThumbnail(Resource1.Icon1, typeof(MyFolder).FullName, true);

Icons as Win32 Resources

The previous chapter explained how to use icons from .ICO files or .NET resources using the ShellThumbnail classes. However, when working with the Shell, it's often easier (or mandatory as for overlay icons support; see the dedicated chapter for more information) to use icons stored as Win32 resources of a binary file (.DLL or .EXE). Win32 resources are completely different than .NET embedded resources.

CBFS Shell comes with a utility class named IconUtilities. This class has a method that can create a Win32 resource-only DLL from scratch using either bitmaps or icons, stored as files or streams. Note that this class only writes 32-bit ARGB icons (using the underlying PNG format for icons).

For example, if you look at the Local Folder sample project in Visual Studio, you will see this:


In this project, the three files, namely Attribute.ico, ErrorOverlay.ico, and WarningOverlay.ico, are declared as "Embedded Resource". This is a standard way of including a resource in a .NET binary (Winforms, WPF, or any other). The following example shows how to create a Win32 DLL from these three embedded resources:

IconsDllPath = Path.GetFullPath("icons.dll");
if (!File.Exists(IconsDllPath))
{
    // use CBFS Shell utility to extract .NET embedded resources and insert them into a Win32 resource-only .DLL file
    IconUtilities.SaveAsDll(Assembly.GetExecutingAssembly(), 
                            typeof(Program).Namespace + ".Resources.Attribute.ico", 
                            IconsDllPath, 
                            100, 
                            1033, 
                            false);
   
    IconUtilities.SaveAsDll(Assembly.GetExecutingAssembly(), 
                            typeof(Program).Namespace + ".Resources.ErrorOverlay.ico", 
                            IconsDllPath, 
                            101, 
                            1033, 
                            false);

    IconUtilities.SaveAsDll(Assembly.GetExecutingAssembly(), 
                            typeof(Program).Namespace + ".Resources.WarningOverlay.ico", 
                            IconsDllPath, 
                            102, 
                            1033, 
                            false);
}

This code will create a file named icons.dll that will contain the three files as Win32 resources. If you open icons.dll with Visual Studio (as a binary file), you should see the following:


100 contains Attribute.ico, 101 contains ErrorOverlay.ico, 102 contains WarningOverlay.ico. "English (United States)" is the resource language, corresponding to the 1033 (the locale id for "en-US") parameter of SaveAsDll. Note that all resources contain the full icon with all its sizes.

Now, you may use this DLL just like we used the shell32.dll earlier (to get the Folder standard icon from the Windows Shell). However, there is a difference:

// here, 4 is positive, so it's an index, the 4th icon in the file. 
public static readonly ShellThumbnail Folder = new ShellThumbnail {
    IconLocation = "shell32.dll",
    IconIndex = 4 
};
 
// here -100 is negative, so it's an id, not an index
// we use what we pass as the id of SaveAsDll, and that's also what Visual Studio or other tools show when looking at this dll
public static readonly ShellThumbnail MyAttributeIcon = new ShellThumbnail { 
    IconLocation = @"c:\mypath\icons.dll",
    IconIndex = -100 
};
 

Most of the time, if you are going to use Win32 resources DLL, you want to create them at program startup, like what's done in the Local Folder sample.

Overlay icons support

A shell item icon or thumbnail may have an overlay icon over it. For example:


CBFS Shell supports overlays icons for any shell item. This is demonstrated in the Local Folder sample.

You don't have to register anything special for that, but the ShellItem instance must override the TryGetPropertyValue and respond to CBFS Shell when it requires the OverlayIconLocation property (specific to CBFS Shell, this is not a Windows property) of the PropertyStore class in the callback.ShellBoost.Core namespace. The value of this property should follow the Windows Shell Icon location syntax:

<Win32 resources DLL path>,<icon index> //icon index is positive

Or

<Win32 resources DLL path>,-<icon resource id> // icon resource id is positive, but we must prepend a -

Here is some sample code that demonstrates this:

public override bool TryGetPropertyValue(PropertyKey key, out object value)
{
    // OverlayIconLocation is not a Windows property, it's a CBFS Shell special property
    if (key == PropertyStore.OverlayIconLocation)
    {
        // note the icon index syntax: the index must be negative when passed to the Windows Shell
        switch (Attribute.Value) // make decision on some custom logic
        {
            case "Error":
                value = IconsDllPath + ",-101";

                return true;
 
            case "Warning":
                value = IconsDllPath + ",-102>>>";
                return true;
        }
    }
    return base.TryGetPropertyValue(key, out value);
}

And here is the result of a custom Shell Item icon combined with the "warning" overlay:


Note there is a hard limit of 15 possible shell icons overlays (including system's ones) for the whole machine as explained in this article: Why is there a limit of 15 shell icon overlays?

Copyright (c) 2022 Callback Technologies, Inc. - All rights reserved.
CBFS Shell 2022 .NET Edition - Version 22.0 [Build 8367]