The Windows right-click menu is built from several different places, and Windows 11 hides half of it behind "Show more options". Most tools I found each cover one part of it. I wanted one list for all of it, so I built Context Menu Editor: a single PowerShell script with a WPF window, free and MIT licensed.
You can try it without installing anything:
irm https://rocisapps.com/cme | iex
You can read the source first, and the same script is attached to every GitHub release.
Here is how it works, and the parts that were harder than I expected.
Three kinds of entries
Windows builds the menu from three different places, and each one is switched off differently:
-
Shell verbs under
Software\Classes\<class>\shell\<verb>. Adding aLegacyDisablevalue turns one off without deleting it. -
Shell extension handlers under
shellex\ContextMenuHandlers(7-Zip, antivirus, PowerToys). You turn one off by adding its CLSID to the per-userShell Extensions\Blockedlist. -
Windows 11 packaged items, which apps declare in their
AppxManifest.xmlaswindows.fileExplorerContextMenus. They have no command line at all, only a COM handler, and they are blocked the same way as extensions.
Reading all three into one list, with the right "off" mechanism behind each switch, is most of the work.
# Turn a shell extension off for the current user (restart Explorer afterwards)
$blocked = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey(
'Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked')
$blocked.SetValue('{00000000-0000-0000-0000-000000000000}', '') # the handler's CLSID
Do not use the registry provider
All registry access goes through Microsoft.Win32.Registry. The PowerShell registry provider treats the * in HKCU\Software\Classes\*\shell as a wildcard, so a path that means "all files" quietly matches other keys. The .NET API takes the path literally:
$shell = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Software\Classes\*\shell')
$shell.GetSubKeyNames()
Send to, and items with no command line
Send to is a folder of shortcuts, so adding an app is easy. Windows 11 packaged items are not: Blip, for example, has a submenu of devices and no executable to point a shortcut at. The shortcut targets a small C# helper instead, which finds the item by its CLSID and calls it on the selected files, the way Explorer does. Submenu entries are matched by title first and by position only as a fallback, so pairing a new device does not change where an existing shortcut sends.
The helper is compiled at runtime with Add-Type and stored outside the script's folder, so shortcuts keep working if the editor moves.
One script, built from many files
The source is split into small files: core functions, UI, XAML, and JSON for the tweaks list. A Compile.ps1 stitches them into the single script people run. Two details only showed up with the one-line launch:
-
irm | iexpasses a byte order mark through as code, which breaks parsing. The output is therefore pure ASCII and written without a BOM, and the compiler refuses non-ASCII characters. - Windows PowerShell 5.1 reads a BOM-less file as ANSI, which is only safe because of the ASCII rule.
The short URL is a redirect on my own domain to the latest GitHub release asset, so a new release is live as soon as it is published.
Startup time
The first version took about two seconds before any window appeared, and most of that was reading the registry. Two changes helped.
Open the window first. It appears straight away with a "reading" state, and the scan runs once the first frame is on screen:
$Window.Add_ContentRendered({
$Window.Dispatcher.BeginInvoke(
[System.Windows.Threading.DispatcherPriority]::Background,
[Action]{ Invoke-Scan; Update-View })
})
The window now appears in 0.6 to 0.8 seconds, and the full list is ready at about the same total time as before.
Cache the compiled helper. Compiling the C# types cost 0.1 to 0.3 seconds on every start. Now they are compiled once and loaded from a cache file in about 15 milliseconds:
Add-Type -TypeDefinition $source -OutputAssembly $dll -OutputType Library # first run only
Add-Type -LiteralPath $dll # every run
An elevated run never loads that cache. An administrator process should not load code from a folder a standard user can write to, so it compiles in memory instead.
PowerShell traps I hit
- Variable names are case-insensitive, so a local
$squietly replaced a global$S. The shared state is now$App. - A function that returns an array with
return ,$arraycan come out nested after@()or a pipeline. Returning the array directly is safer. -
Ris an alias forInvoke-History, which matters when a script defines a short helper function. - The
`u{...}Unicode escape does not exist in Windows PowerShell 5.1.
Testing without touching your own menus
-LibraryOnly loads every function without opening the window, so the scanner and every write operation can be scripted. An early test run wrote into my real Send to folder, which taught me to point tests at a temporary Send to folder and a temporary backup folder. A separate sweep opens every page of the window and fails on any error, in both PowerShell 7 and 5.1.
Safety
Anything you edit or delete is exported to a .reg file first, and the last change can be undone from the status bar. Per-user changes need no administrator rights. The script makes no network calls except downloading itself again when you choose "run as administrator".
Try it, and tell me what is missing
- Run it:
irm https://rocisapps.com/cme | iex - Source (MIT): https://github.com/RoeeIlouz/ROCIsContextMenu-Editor
- Guides: get the full right-click menu back in Windows 11 and remove items from the right-click menu
I am the author. If something breaks on your setup, or an entry is missing from the list, open an issue on GitHub. Please include your Windows version, PowerShell edition and what caused it to break.





Top comments (0)