右键菜单是 Windows 上每个开发者每天都要点几十下的东西,但它的构成远比想象中复杂。Windows 11 还把它一半的项藏在“显示更多选项”里,搞得人找东西要多点一下。市面上各种右键菜单管理工具我几乎都试过,每个都只覆盖一部分:有的能管传统项,有的能管扩展,但从来没有人把三类来源整合到一个界面里。所以我用 PowerShell 写了一个单文件右键菜单编辑器,带 WPF 窗口,MIT 协议,免费。你可以不安装直接试:

```

irm https://rocisapps.com/cme | iex

```

先用着不放心可以先读源码,GitHub 每个 release 都附了同一个脚本。

Cover image for A Windows context menu editor in one PowerShell file
Cover image for A Windows context menu editor in one PowerShell file

三类菜单项,三种关闭方式

Windows 构建右键菜单有三个完全不同的来源,而且各自的“关闭”机制互不相通。第一类是传统的 Shell verbs(即注册表里 `Software\Classes\<类>\shell\<动词>` 下面定义的命令,比如“用记事本打开”)。要停用某个动词只需加一个 `LegacyDisable` 值,它不会删除原项,只是隐藏。第二类是 Shell extension handlers(扩展处理器,就是 7-Zip、杀毒软件、PowerToys 这些往菜单里塞插件的东西),它们注册在 `shellex\ContextMenuHandlers` 下。要禁用某个扩展,得把它的 CLSID(一个全局唯一标识符,类似身份证号)加到当前用户的 `Shell Extensions\Blocked` 注册表列表里。第三类是 Windows 11 的打包应用项(packaged items),应用在它们的 `AppxManifest.xml` 中声明 `windows.fileExplorerContextMenus`,这些项根本没有命令行,只有 COM 处理器(一种组件对象模型接口,供系统调用),所以要靠同一套 Blocked 列表来禁用。

aligning 这三类来源并统一显示到一个列表里,每个开关背后对应正确的“关闭”机制,是这个工具工作量的大头。代码里有一个典型的片段,把某个扩展关闭掉(改完要重启 Explorer 才生效):

```powershell

$blocked = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey(

'Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked')

$blocked.SetValue('{00000000-0000-0000-0000-000000000000}', '')

```

ROCI
ROCI

不要用 PowerShell 的注册表提供程序

这里有个坑值得拿出来说:所有注册表访问我都是直接用 .NET 的 `Microsoft.Win32.Registry` API,刻意绕开 PowerShell 的注册表提供程序(`HKCU:` 之类)。原因是 PowerShell 把路径里的星号 `*` 当通配符处理——比如 `HKCU\Software\Classes\*\shell` 本意是“所有文件类型”,但它会悄悄匹配到其他意外的键。而 .NET 的 API 会把路径当字面量:

```powershell

$shell = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Software\Classes\*\shell')

$shell.GetSubKeyNames()

```

这样才不会“误伤”其他名字里含星号的键。

Windows 11 shows a short right-click menu with
Windows 11 shows a short right-click menu with

“发送到”和没有命令行的项

“发送到”(Send to)是一个文件夹里的快捷方式集合,加一个应用很简单——放个快捷方式进去就行。但 Windows 11 的打包应用项就不一样了。比如 Blip(一个蓝牙文件传输应用)在右键菜单里有个子菜单,列出附近的设备,但你找不到它的可执行文件给快捷方式指向。我用的办法是让快捷方式指向一个很小的 C# 辅助程序:它通过 CLSID 找到那个打包应用项,然后像 Explorer 那样把选中的文件传给它。子菜单里的条目优先按标题匹配,找不到再按位置兜底,这样新增一个设备时不会把已有快捷方式搞乱。

这个 C# 辅助程序是在脚本运行时用 `Add-Type` 动态编译的,存放位置在脚本文件夹之外,所以就算脚本移动过,已有的快捷方式依然可用。

Context Menu Editor's Tweaks page: a checklist of menu clean-ups with Minimal an
Context Menu Editor's Tweaks page: a checklist of menu clean-ups with Minimal an

一个脚本,从多个文件拼起来

源码本身拆成多个小文件:核心函数、UI、XAML 布局、以及用于“杂项清理”(Tweaks)列表的 JSON 数据。构建脚本 `Compile.ps1` 把它们缝合成一个可执行的单文件。

“一行命令运行”这个特性逼出两个细节:`irm | iex` 会把字节序标记(BOM,一种文件开头的不可见字符)当成代码传进去,直接导致解析失败。所以发布版脚本必须是纯 ASCII、不含 BOM,编译脚本干脆拒绝任何非 ASCII 字符。另外一个点是 Windows PowerShell 5.1 读取无 BOM 文件时按 ANSI 编码解析,但正因为我们强制 ASCII,才安全。

那个短链接是我自己域名上的一个重定向,指向 GitHub 最新 release 的附件,所以新版本一发布,短链接立即生效。

Diagram: three sources of right-click entries (shell verbs, shell extensions, Wi
Diagram: three sources of right-click entries (shell verbs, shell extensions, Wi

启动提速:从 2 秒到 0.6 秒

第一版启动要两秒左右才出窗口,其中大部分时间花在读注册表上。我做了两个改动。

第一,先开窗口。窗口立刻出现,显示“正在读取”状态,真正扫描等第一帧画出来之后再做:

```powershell

$Window.Add_ContentRendered({

$Window.Dispatcher.BeginInvoke(

[System.Windows.Threading.DispatcherPriority]::Background,

[Action]{ Invoke-Scan; Update-View })

```

现在窗口在 0.6 到 0.8 秒内出现,而完整列表就绪的时间基本和原来差不多,纯粹是心理感知上的巨大提升。

第二,缓存编译好的辅助程序。以前每次启动编译 C# 类型都要花 0.1 到 0.3 秒。现在只在第一次编译,之后从缓存 DLL 文件加载,大约 15 毫秒:

```powershell

Add-Type -TypeDefinition $source -OutputAssembly $dll -OutputType Library # 仅首次运行

Add-Type -LiteralPath $dll # 每次运行

```

注意以管理员身份运行时绝不加载那个缓存 DLL——管理员进程不应该从不信任的普通用户文件夹加载代码,这种场景直接内存编译。

Diagram: a Send to shortcut calls a small C# helper, which calls the Explorer co
Diagram: a Send to shortcut calls a small C# helper, which calls the Explorer co

PowerShell 的几处陷阱

写脚本的过程中踩了几个典型的 PowerShell 坑,可能对同行也有参考价值。

Bar chart: time until the window appears dropped from 1.9 to 0.6 seconds on Wind
Bar chart: time until the window appears dropped from 1.9 to 0.6 seconds on Wind

安全第一:不动真菜单就能测试

我特意提供了 `-LibraryOnly` 参数:只加载全部函数但不弹出窗口,这样扫描器和所有写操作都能被脚本化测试。有一次早期测试直接写进了我真实的“发送到”文件夹,从那以后我就把测试指向一个临时目录和临时备份目录。另外还有一个单独的“扫屏”测试,会在 PowerShell 7 和 5.1 里把窗口每一页都打开一遍,任何报错都算失败。

你编辑或删除的任何项,都会先导出一个 `.reg` 备份文件,状态栏里有“撤销最后一次更改”的功能。当前用户的修改不需要管理员权限。脚本除了你选择“以管理员身份运行”时自动重新下载自身之外,不做任何网络请求。

pic
pic

拿来就用

如果你也经常被 Windows 11 的右键菜单烦到,直接:

```

irm https://rocisapps.com/cme | iex

```

源码在 GitHub:https://github.com/RoeeIlouz/ROCIsContextMenu-Editor。

这个工具的通用思路是:当你面对一个系统功能被分散在多处管理时,不要急着写一堆一次性脚本,而是先做一个“统一视图 + 每种开关单独映射”的编辑器。同样的模式也可以迁移到其他平台——比如 macOS 的右键服务(Services)、VS Code 的右键菜单扩展,甚至浏览器的右键菜单。关键点是搞清楚每种菜单项的底层机制,不要把注册表通配符当普通路径,以及永远先备份再修改。

如果你是作者所说的那种喜欢拆解系统机制的开发者,读这份源码能学到很多 PowerShell 与 Win32 交互的细节,特别是如何避开 PowerShell 提供程序在注册表路径上的通配符陷阱,以及如何在单文件脚本里安全地动态编译缓存代码。

阅读原文 → 返回 AI 技术文档

内容与图片版权归原作者所有 · 原文: https://dev.to/rocisapps/a-windows-context-menu-editor-in-one-powershell-file-2mc