# The Windows Context Menu – Is It a Lost Cause?

The Windows context menu is... poorly done.

And third-party software developers adding oil into the dumpster fire aren't helping with it. It's not their fault, though. There's no common standard and almost no documentation for adding entries into the context menu.

At this point, there's no return and no salvation for it. At very least it's working. What actually doesn't help is that Microsoft continued with their all time classic - piling up layers upon layers of indirection with their all new Windows 11 context menu, which is also poorly done and even more enclosed in terms of the API.

I want to dedicate this blog post to actually customizing the Windows 10/11 Legacy context menu to your liking instead of simply complaining about its inner workings. Using methods described in this article, you will be able to bring your menu from this abomination...

...to this!

Be aware - there's plenty of registry digging involved. And the truly worst part is...

Forget any logic when dealing with registry keys. The idea of registry's structural integrity has long since been abandoned and nobody seems to care about it as, well, the end user is never going to care about it, and if they do - they know what they're doing and they will not complain.
While there are layers of abstraction applied to the most Windows settings, the context menu is, unfortunately, left aside. And much to our dismay, its backend is also janky, terrible and tangled up.

The only fair question to reader from me is "Do you really believe it's worth your time?". And if your answer is yes, and you're as much of a perfectionist as I am, buckle up and get ready for upcoming few hours of pure joy.

Please note that any `sfc /scannow` or `dism /restorehealth` invocations will probably interfere with your context menu as Microsoft are really weird - they seem to allow customization of it, but still don't fully support it. It's been like this for at least 20 years.

##
Shell entries & Context Menu handlers

There are two main ways to insert entries into the context menu:

- Shell entries
- Context Menu handlers

Both of them are very poorly documented, but the idea is very simple: whereas Shell entries are managed by the registry, the Context Menu handlers are managed by the software in question.
Shell entries are much, much better in terms of flexibility and customization (in fact, you don't get any customization with Context Menu handlers at all, unless the developer was nice enough to let you tweak context menu settings through their app or provide necessary documentation for registry endpoints). 7-Zip would serve as a neat example of good software design, as the creator allows you to tweak settings of their handler.

MobaXterm would also serve as a nice example for the case, where shell entries are used, but you can tweak them from the app itself.

And so, there are 3 types of applications in terms of their context menu customization-friendliness:

- Context Menu handler apps with in-app settings (the easiest)
- Apps using shell entries (customizable via registry hacks or from within software)
- Context Menu handler apps without anything (uneditable entries)

The first case is the simplest - you just have to consult the documentation of software you're using to find out whether it supports context menu customization or not.

The other two cases is what I'm going to tackle in this write-up.

For starters, all modifications are done under `HKEY_CLASSES_ROOT` (the `HKEY_LOCAL_MACHINE\Software\Classes` alias). That hive contains data about shell components and file extensions. For example, any data under `exefile` will apply to all executable files. That's one of the methods companion viruses utilized back in the day to accompany their victims - the `.exe`/`.com` files. Either way, since the `HKCR` hive is basically divided into file/shell extensions, we will have to edit context menu for each file and shell extension manually. Yeah. This is absolutely abysmal. There is no better way to approach this. And while you might already be reconsidering your choices, let me teach you the basics so you could at least bring your most used extensions and most noticeable context menu areas up to par... Some generalizations are also made within the registry. For instance, there are wildcard entries for ALL files, or for ALL images and so on. That still does not excuse the horrible approach Microsoft has taken with the file extensions from the beginning.

Even though the incompleteness always annoyed me, unfortunately, that's how sometimes life is.

This is how the general structure of an extension looks like:

Editable entries are stored under the `Shell` key. Uneditable/highly customizable (thanks to the developer) entires are stored under `ShellEx`. Capitalization plays no role, the Windows registry key names is case-insensitive. The value names, however, are. What a nice spec!

###
Sort of editable entries?

The structure varies from extension to extension, but you know you've found it when you see the `Shell` key. Subkeys of it represent the "shell entries" - or, well, customizable entries of the context menu.

Each shell entry contains the `command` subkey, default value of which controls what executes upon clicking on the context menu entry.
The default entry follows the Batch syntax, albeit very poorly. You cannot have extended Batch logic in the entries, which made me immeasurably sad and also made me write utilities called `quiet` and `elevate` for that specific purpose. No matter how hard I tried, I could not find the spec for the extents of the Batch logic in the default value for `command`.

In general, I'd suggest you to treat the `command` value as the raw command line, but with the `%1` macro for the full input file path without quotation marks (e.g. if you right-click `C:\Users\bleh\p.txt`, and click on your own shell entry, the `%1` within `command` will be equivalent to `C:\Users\bleh\p.txt`) and `%V` for the full directory path. That must be one of the few values to be consistent across all the shell entries.

Sometimes `command`'s default value is left unused for `DelegateExecute` to take its place.
If that's the case, that means the command handler is implemented as an IExecuteCommand COM object, or, well, you can't really customize it. All it contains is the UUID of the read-only object.

####
Adding entries

1. Locate the extension (be it a file, or a shell one) you want to target.
2. Careful! Some extensions can be buried within other extensions. For example, the folder background extension is located inside the folder extension under the `Background` key.
3. Navigate to the `shell` key. If there is none and you're confident there should be one (by feel!), try creating one (it's highly likely not going to affect the system stability).
4. Create a new entry, you can call it whatever you want, but it should be alphanumeric.
5. Create a `command` key.
6. Specify the command line you want to execute upon the click of your entry.
7. The entry should appear in the context menu. If it does not, restart Explorer. If it still doesn't appear, you've probably messed up the procedure or the extension isn't registered in Windows.

####
Nesting entries

You can nest entries too! Repeat the first 4 steps from The Windows Context Menu > Adding entries, but don't add the `command` key yet. Instead, create another `shell` key, and then inside of it create a key not called `command`. And working in that new key, repeat last 3 steps. Finally, get back to your initial ancestor entry and add an empty REG_SZ value called `SubCommands`.

You can make an entry have only a single nested entry as well, I've tested it.

To control the parent context menu entry, use the outer `Shell` entry subkey.

To control its children, use the inner `Shell` entry subkeys.

####
Editing entries

Now you're probably wondering how to name the entries and add nice little icons next to them.
By default, the context menu entries duplicate their names from their respective shell subkeys.

######
Name

To override the default name, you can use either the `(Default)` or `MUIVerb` REG_SZ values inside the respective shell entry (not `command`). `MUIVerb` takes precedence over the default value.

You can put in anything you want, it's going to work.

######
Icon

To enable the icon for your shell entry, you should use the `Icon` REG_SZ value inside the respective shell entry. It uses that weird WinAPI resource syntax to create a direct link to the icon.

    PATH_TO_FILE_WITH_ICON_RESOURCES,INDEX

If you want to select the first icon (in that case, the `INDEX` part is 0), the `INDEX` part can be omitted completely, as in:

    PATH_TO_FILE_WITH_ICON_RESOURCES

For example, the following expressions are completely valid and are going to work:

    "C:\Windows\system32\cmd.exe"
    C:\Windows\regedit.exe
    "C:\Windows\explorer.exe",0
    C:\Windows\system32\cmd.exe,0
    cmd.exe
    shell32.dll,3
    SHELL32.DLL,10
    "C:\Windows\system32\SHELL32.DLL",9

You can omit the quotes and full paths depending on the `%PATH%` variable that unholy concoction of an API uses. Only God knows what paths it's able to parse by default. `C:\Windows`, `C:\Windows\system32` work for sure, though.

I still highly suggest supplying the full path to file, as even the `%PATH%`s of the Explorer's icon picker and the `Icon` value do not seem to correspond.

Tip: you can use the Explorer folder icon picker to acquire the icon DLL and calculate the offset of the desired icon. For example, the one I've selected in the screenshot has an index of `5`.

Indexing begins with `0` and ascends in columns.

You can also pull resource strings from files the same way for MUI Verbs and default values.

######
Separators

Another integral part of the context menu, which is hardly documented by anyone. Managing them is also rather quirky, but you get used to it after some time.

Separators in shell entries are relative to the element you're enabling them for and can overlap without any issue.

Use `SeparatorBefore` and `SeparatorAfter` empty REG_SZ values to add a separator before or after the entry you're editing, respectively.

######
Appear in the Shift+Click context menu only

You can make your shell entry be "hidden" from a normal context menu click. Such entry is called "extended". It's possible to enable that functionality by creating an empty REG_SZ value called `Extended` inside the respective shell entry.

######
Position override

See The Windows Context Menu > Sorting entries > Position override

######
Hide in Safe Mode

To hide the context menu entry in safe mode, create an empty REG_SZ value called `HideInSafeMode` inside the respective shell entry.

######
Never default

An example of a default entry would be "Open" for folders, or generally the bold context menu entry shown at the top. That's the entry Windows will default to when you open the file with a certain extension.

There can be no default entry at all, and that's perfectly fine. If you don't want your entry to show up as a default one, add an empty REG_SZ value called `NeverDefault` into the respective shell entry.

######
Pass through to Context Menu handlers

If you know what context handler you want to invoke on shell entry click, you can specify a so-called Verb Handler.

Create a new REG_SZ value `VerbHandler` and specify the context menu handler's UUID as data.

######
Other options

I am not ashamed to admit that I have absolutely zero idea what some of the options do, even though they might sound obvious.

If you do have the knowledge, please contact me on Twitter: @endermanch or send a mail my way: blogs@enderman.ch

For example:
`NoWorkingDirectory` - could be hiding the working directory from the app it's starting?
`CanonicalName`
`CommandStateHandler`
`CommandStateSync`
`Description`
`VerbName`

####
Deleting entries

Pretty much the opposite of adding one.

1. Locate the extension (be it a file, or a shell one) you want to target.
2. Sometimes multiple locations can correspond to the same place in the context menu, which might result in the entry remaining in place, even though you've deleted it.
3. Navigate to the `shell` key.
4. Find the shell entry you want to delete. You can find out the application behind it by looking into the default value of the `command` key.
5. Delete the key.

####
Sorting entries

Now that's the funniest one. I even memed about it on Twitter.
The only way to sort the context menu is via the alphabetical order of its respective shell entries in the registry. Yes. I am not kidding. Let the screenshots speak for themselves.

Yeah... I'd rather not comment that.

######
Position override

Another silly and janky feature that has a limited amount of use cases. You can manually override the position of your context menu item to be... either at the very top, or the very bottom. Not much better than alphabetical sorting if you ask me...

In your entry under `shell`, create a REG_SZ value called `Position`.
If you want to force the context menu handler to the top, put `Top` as data, otherwise put `Bottom`.

Sometimes this may be necessary, as, for example, Microsoft dumps their regular alphabetical sorting strategy at the bottom of the context menu, where display and personalization settings reside. They simply specify the `Bottom` position for each of the entries. If I recall correctly, they're still sorted alphabetically between themselves, as position override takes precedence.

####
Bottom line

Some of the default shell entries like `Powershell` have specific keys (for the sake of that example, `ProgrammaticAccessOnly`) to control their appearance context menu.

As a rule of thumb, every time you're dealing with existing shell entries, I REALLY suggest you to search for a way to modify them online before resorting to any of the general cases I've described above. If you can't find anything even on the Internet, well, anything works.

###
Uneditable entries

As for the Context Menu handlers, there's actually not much you can do. They're located under the `shellex\ContextMenuHandlers` subkey.

Most of the times, the only data they contain is a lonely UUID. That doesn't help much.

So, to remove the Context Menu handler, you should just delete the subkey under `ContextMenuHandlers`. I strongly suggest you save the UUID it contained so that you could roll back at any time later.

Adding your own... Well, that's complicated. You actually have to write your own handler in a language supporting Windows API or any sort of an abstraction of it. For example, C# is a decent choice. I don't suggest you waste time on this, unless you're making full-on software that you want to make more accessible to Windows users.

##
Dealing with locked keys

If the registry key is locked and you don't have write permissions for it, it's owned by TrustedInstaller and you have to manually reclaim ownership of the key and allow yourself to make edits to it.

Optimal way: Using Process Hacker with the TrustedInstaller plugin or Winaero Tweaker, run `regedit` as Trusted Installer and perform necessary tweaks.

General solution:

1. Navigate to the key you want to take ownership of
2. Open the `Properties` window
3. Head over to the `Advanced` tab
4. At the top of the Advanced Security Settings it states `Owner: TrustedInstaller`. Click `Change`
5. Input your username into the `Enter the object name to select (examples)` text area.
6. Click `OK`
7. Check the `Replace owner on subcontainers and objects` checkbox
8. Check the `Replace all child object permission entries` checkbox
9. Click `OK`
10. Select `Administrators (PC_NAME\Administrators)` in the group selector and then check `Full Control` box in the `Permissions` window down below
11. Click `OK`

After performing above steps, you must have gotten full and almost complete (some registry values and keys are still protected after the general procedure) access to the desired key.

##
Registry paths involved

- Bottom line of desktop:
  `Computer\HKEY_CLASSES_ROOT\DesktopBackground`

- Folder Background
  `Computer\HKEY_CLASSES_ROOT\Directory\Background\shell`

- Folder
  `Computer\HKEY_CLASSES_ROOT\Directory\shell`

- Folder Item (extends Folder)
  `Computer\HKEY_CLASSES_ROOT\Folder`

- Drive
  `Computer\HKEY_CLASSES_ROOT\Drive`

- Library folder
  `Computer\HKEY_CLASSES_ROOT\LibraryFolder`

  Library background inherits from Folder Background, but can't parse the `%V` macro, which results in the following error. This is Microsoft's fault. To my knowledge, there is no way to fix that.

- All Files
  `Computer\HKEY_CLASSES_ROOT\*`

- All Filesystem Objects (yep, that's different from All Files - it's a generalization that includes drives and symbols)
  `Computer\HKEY_CLASSES_ROOT\AllFilesystemObjects`

- Text Files
  `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\text`

- Image Files
  `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\image`

- Document Files
  `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\document`

- Audio Files
  `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\audio`

- Video Files
  `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\video`

##
Cleaning up the context menu

Most obnoxious default entries can be cleaned up using Winaero Tweaker. I suggest you use it to make the process way more bearable for yourself.

###
Areas Winaero Tweaker does not reach

######
Remove "Open PowerShell window here"

1. Navigate to `Computer\HKEY_CLASSES_ROOT\Directory\shell\Powershell`
2. Unlock key (The Windows Context Menu > Dealing with locked keys)
3. Create a new REG_SZ value `ProgrammaticAccessOnly` without any data
4. Navigate to `Computer\HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell`
5. Unlock key
6. Create a new REG_SZ value `ProgrammaticAccessOnly` without any data

######
Remove "Open Linux shell here"

1. Navigate to `Computer\HKEY_CLASSES_ROOT\Directory\shell\WSL`
2. Unlock key
3. Delete `WSL` key altogether
4. Navigate to `Computer\HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell`
5. Unlock key
6. Create a new REG_SZ value `ProgrammaticAccessOnly` without any data

######
Edit the "Edit" text context menu command

1. Navigate to `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\text\shell\edit`
2. Make sure you've read the article thoroughly, then do anything you want with it.

##
Bottom line

No matter how terrible the context menu API might be for Windows, there's still a way to customize it. Hope you found the tricks useful!

##
Contact

If you've spotted a mistake (be it orthographical, factual or any other) in this article or want to add some important details or share any important knowledge on the subject, please contact me on Twitter: @endermanch or hit me up on e-mail: blogs@enderman.ch