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.
There are two main ways to insert entries into the context menu:
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:
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!
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.
Background
key.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).command
key.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.
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.
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.
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.
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.
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.
To hide the context menu entry in safe mode, create an empty REG_SZ value called HideInSafeMode
inside the respective shell entry.
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.
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.
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
Pretty much the opposite of adding one.
shell
key.command
key. 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.
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.
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.
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.
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:
Properties
windowAdvanced
tabOwner: TrustedInstaller
. Click Change
Enter the object name to select (examples)
text area.OK
Replace owner on subcontainers and objects
checkboxReplace all child object permission entries
checkboxOK
Administrators (PC_NAME\Administrators)
in the group selector and then check Full Control
box in the Permissions
window down belowOK
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.
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
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.
Computer\HKEY_CLASSES_ROOT\Directory\shell\Powershell
ProgrammaticAccessOnly
without any dataComputer\HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell
ProgrammaticAccessOnly
without any dataComputer\HKEY_CLASSES_ROOT\Directory\shell\WSL
WSL
key altogetherComputer\HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell
ProgrammaticAccessOnly
without any dataComputer\HKEY_CLASSES_ROOT\SystemFileAssociations\text\shell\edit
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!
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