ShellBoost

There are 3 types of menus that can be supported when developing a namespace extension:

Folder Context Menu: this menu is shown when the end-user right-clicks on a folder view, but not on a Shell Item. This is not to be confused with the menu shown when the user clicks on a Shell Folder.

Item Context Menu: this menu is show when the end-user right-clicks on a Shell Item (item or folder). Note this menu can be shown in Explorer’s Left Tree View, or in the list of Shell Items (the view).

“New” Item menu: this menu is the standard Windows “New” menu. It can be a sub menu of the Folder Context Menu (its display text is “New”). It allows end-users to create new items from the Shell, as opposed as creating new items from application (probably using Common Dialogs like Save).

“Send To” Item menu: this menu is the standard Windows “Send to” menu. It can be a sub menu of the Folder Context.

Folder context menu

Here is an example of such a menu in the Registry Folder sample. Mouse was right clicked anywhere in the folder’s view white area, but not on one of the 5 folders:

Folder context menu - Picture 26

In this case, the menu is the standard Windows 10 shell menu. Here is another example where a “New” item with a sub menu was added by the namespace extension (since this is the Registry Folder sample, it’s meant to mimic “Regedit” behavior):

Folder context menu - Picture 27

Here is the code that adds this sub menu:

// override ShellFolder’s class MergeContextMenu method.
// existingMenu contains the menu as it was already prepared by the shell.
protected override void MergeContextMenu(ShellFolder folder, IReadOnlyList<ShellItem> items, ShellMenu existingMenu, ShellMenu appendMenu)
{
    ...
    if (items.Count == 1 && !items[0].IsFolder)
    {
        // this is one shell item, do something else
    }
    else
    {
        // tags are placeholder for anything. we use them to remember what's the real command behind the menu item
        if (existingMenu.Items.FirstOrDefault(i => i.Text == "New") == null)
        {
            var newItem = new ShellMenuItem(appendMenu, "New");
            appendMenu.Items.Add(newItem);
 
            newItem.Items.Add(new ShellMenuItem(appendMenu, "Key") { Tag = MenuCommand.NewKey });
            newItem.Items.Add(new ShellMenuSeparatorItem());
            newItem.Items.Add(new ShellMenuItem(appendMenu, "String Value") { Tag = MenuCommand.NewValueString });
            newItem.Items.Add(new ShellMenuItem(appendMenu, "Binary Value") { Tag = MenuCommand.NewValueBinary });
            newItem.Items.Add(new ShellMenuItem(appendMenu, "DWORD (32-bit) Value") { Tag = MenuCommand.NewValueDWord });
            newItem.Items.Add(new ShellMenuItem(appendMenu, "QWORD (64-bit) Value") { Tag = MenuCommand.NewValueQWord });
            newItem.Items.Add(new ShellMenuItem(appendMenu, "Multi-String Value") { Tag = MenuCommand.NewValueMultiString });
            newItem.Items.Add(new ShellMenuItem(appendMenu, "Expandable String Value") { Tag = MenuCommand.NewValueExpandString });
        }
    }
    ...
}

Note: starting with Windows 11, the root folder context menu will not show the complete context menu like previous versions of Windows did. Instead, everything custom will be put in a special “Show more options” sub menu item. This is expected and the only solution is to use static verb (as opposed to dynamic verbs, i.e.: IContextMenu Shell extensions), because this is the direction Microsoft is talking for the future as explained in this article: Extending the Context Menu and Share Dialog in Windows 11

Item context menu

The list of items and sub items in a Shell Item (item or folder) context menu is the result of various combined operations from the Windows Shell and the 3rd parties installed on a PC (including the namespace extension that contains the item).

This is how you can add a “Modify…” menu item to a Shell Item (not a folder) in your namespace extension:

// override ShellFolder’s class MergeContextMenu method.
// existingMenu contains the menu as it was already prepared by the shell.
protected override void MergeContextMenu(ShellFolder folder, IReadOnlyList<ShellItem> items, ShellMenu existingMenu, ShellMenu appendMenu)
{
    ...
    // if there is only one shell item selected and it's not a folder
    if (items.Count == 1 && !items[0].IsFolder)
    {
        var modifyItem = new ShellMenuItem(appendMenu, "Modify...");
        modifyItem.Tag = MenuCommand.Modify;
        modifyItem.IsDefault = true;
        appendMenu.Items.Add(modifyItem);
    }
    ...
}

Added menu items can have an associated bitmap. To set a menu item bitmap, set it’s BitmapPath property to a file path that points to the bitmap, for example like this:

…
var item = new ShellMenuItem(appendMenu, "My Menu Item");
item.BitmapPath = @"c:\myPath\myBitmap.png";
…

Note for a context menu to support transparency it’s recommended to use an 8bits depth (using a palette of 256 colors) bitmap, not a 32bits depth (full ARGB) bitmap, event with a .png for example. You can build and distribute these images yourself of course, or you can use a ShellBoost-provided utility OctreeQuantizer class that converts ARGB bitmap to 256 colors bitmaps, for example with a code like this:

public string GetMenuIconBitmapPathFromResource()
{
       // come up with some cache path so we don’t rewrite a file each time
       var cachePath = Path.Combine(Path.GetTempPath(), "some unique identifier.png");
       if (!IOUtilities.FileExists(cachePath))
       {
             // for example, get an embedded resource bitmap from an assembly
             using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("myBitmap.png"))
             {
                    using (var bmp = Image.FromStream(stream)) // load the bitmap from the assembly resource
                    {
                           // resize to 16 x 16 (context menu standard size), still ARGB (32bits) using a ShellBoost-provided utility
                           using (var resized = ImageUtilities.ResizeImage(bmp, 16, 16))
                           {
                                  // the Shell's context menu doesn't like full ARGB images, convert to paletted 256 colors (8 bits)
                                  using (var bmp256 = new OctreeQuantizer(BitDepth.Bits8).Quantize(resized))
                                  {
                                        bmp256.Save(cachePath);
                                  }
                           }
                    }
             }
       }
       return cachePath;
}

Unwanted menu items

As a Shell Namespace Extension developer, you will not own everything that’s displayed in it, especially if the item is a physical Shell item. For example, if I have installed the renowned Notepad++ application and asked for Shell integration, Notepad++ setup will add the “Edit with Notepad++” menu item to any Shell item, including fully virtual ones (clicking on this item will do nothing as the Shell Item in this example does not support virtual content).

Unwanted menu items - Picture 28

When overriding MergeContextMenu, ShellBoost will extract for you the existing menu as it’s been prepared when the Shell called ShellBoost to merge new items to it. From there you can remove existing items that you don’t want. For that, you can add the value of their Id property to the RemoveIds collection property of the ShellMenu class. You can also use the RemoveIdsWhere utility method as shown below. Note, however, that in the example of Notepad++, it won’t work because, for some reason, the “Edit with Notepad++” menu item is added after the MergeContextMenu call.

In this, case, to remove such a menu item, you can override to the MergeContextMenuTop method, and remove, for example, all items containing the “notepad++” string:

protected override void MergeContextMenuTop(ShellFolder folder, IReadOnlyList<ShellItem> items, ShellMenu existingMenu, ShellMenu appendMenu)
{
    // many standard Shell menu items can be determined using their “Verb” property
    // for example, “properties” for the Properties menu item, “delete” for the Delete menu item, etc...
    // but other 3rd parties can only be determined by their display name.
    appendMenu.RemoveIdsWhere(existingMenu.AllItems, i => i.DisplayText?.IndexOf("notepad++", StringComparison.OrdinalIgnoreCase) >= 0);
}

Another solution is to fully implement your own custom menu. You can do that if you override the ShowDefaultContextMenu method, like this:

protected override bool ShowDefaultContextMenu(IntPtr hwndOwner, IReadOnlyList<ShellItem> items) => false;

In this case, you will lose the default item menus that the shell creates, such as “Pin to taskbar”, “Pin to Start”, “Pin to Quick Access”, “Open in new window”, “Create Shortcut”, “Share”, “Delete”, “Copy”, “Cut”, “Paste”, “Delete”, “Properties”, and others. If you still need them, you will have to add these by yourself. In some cases, is not always possible as some items and their corresponding actions are not documented.

“New item” menu

The “New item” menu is a sub menu of a Folder Context Menu that appears when right-clicking on the folder’s view, anywhere outside a shell item:

“New item” menu - Picture 29

This menu is entirely built by the Windows Shell and depends on applications installed on the machine.

Namespace Extensions developed using ShellBoost can support this New Item menu. It’s automatic when the Shell Folder in the view is a physical Shell Item (it’s mapped to a real physical directory). You can however prevent this menu to appear if you set a ShellFolder instance’s AddNewMenuTemplatePath property to null.

For fully virtual Shell Folders, this menu will not appear. You can however manually add it to the Folder Context Menu with a code like this (demonstrated in the Web Folder sample):

protected override void MergeContextMenu(ShellFolder folder, IReadOnlyList<ShellItem> items, ShellMenu existingMenu, ShellMenu appendMenu)
{
    // add an invoke handler
    appendMenu.AddInvokeItemHandler(OnShellMenuItemInvoke);
 
    // because we don't rely on file system, we won't have the "new" menu automatically set for us
    // so we must build one, from the standard one
    // fortunately, there is a helper for that
    appendMenu.MergeNewMenu();
}
 
private void OnShellMenuItemInvoke(object sender, ShellMenuInvokeEventArgs e)
{
    // here, e.Verb contains either:
    // * the file extension of the new file type chosen (for example “.txt”) 
    // * the “newfolder” string (new folder)
    // * the “newlink” string (new shortcut)
    // TODO: implement logic
}

As you see here, it’s shown the same way as for physical Shell Folders:

“New item” menu - Picture 15

Of course, since the folder is virtual, you will need to code whatever means creating a new item in this context.

“Send To” menu

The “Send To” menu is a sub menu of a Shell item’s Context Menu that can appear when right-clicking on the item. By default, it will not appear, but you can add it with a code like this:

protected override void MergeContextMenu(ShellFolder folder, IReadOnlyList<ShellItem> items, ShellMenu existingMenu, ShellMenu appendMenu)
{
    appendMenu.Items.Add(new ShellMenuSendToItem());
}

The « Send To » menu and its sub menu items will be added to the context menu. The content and position of the “Send To” menu is automatically decided by the Shell. When the user selects one of the sub item’s menu, the system will try to Copy / Paste or Drag & Drop the currently selected item(s) or access its stream.

Here is what the “Send To” menu shows in the case of the Registry Folder sample, which uses virtual Shell Items:

“Send To” menu - Picture 76

Note here the “Mail recipient” although displayed, isn’t capable by default to understand the sample virtual items.

Here is what the “Send To” menu shows in the case of the Cloud Folder sample, which uses physical Shell Items:

“Send To” menu - Picture 86