PowerToys Run Value Generator 插件解析:一行命令完成哈希、Base64、GUID 与 URL 编解码
【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys
本文以 PowerToys 官方开发文档 Value Generator 插件说明 为主体,完整梳理 Value Generator 插件支持的 18 个查询命令、IComputeRequest计算请求契约、查询解析规则与错误处理约定,并深入 InputParser.cs、GUIDGenerator.cs、HashRequest.cs 等源码,还原每个命令背后的实际 API 调用链,最终给出扩展新生成器的完整步骤。读完后你可以直接使用全部命令,也能按仓库约定为插件新增自己的生成器。
插件定位与基本用法
Value Generator 是 PowerToys Run(launcher)内置的社区插件,用于对字符串做哈希、计算 Base64 编解码、对 URL/URI 进行转义与反转义、生成版本 1/3/4/5/7 的 GUID。插件源码位于 Community.PowerToys.Run.Plugin.ValueGenerator 目录,其 plugin.json 声明了:
ActionKeyword为#,即在 PowerToys Run 查询框中输入#加空格即可触发该插件的命令补全;ExecuteFileName为Community.PowerToys.Run.Plugin.ValueGenerator.dll,插件以独立 DLL 形式由 launcher 加载;isGlobal为false,仅通过#关键字激活。
插件入口 Main.cs 实现IPlugin, IPluginI18n, IDisposable,其Query()方法把用户输入交给InputParser.ParseInput()得到计算请求对象,再调用Compute()生成Result。每条结果以固定Score = 300展示,点击后在 STA 线程中调用Clipboard.SetText(request.ResultToString())将结果复制到剪贴板;仅当命令正确而参数有误(ArgumentException)时才展示带警告图标的错误条目,标题取自资源文件中的Value Generator Error(见 Resources.resx)。
支持的命令一览
QueryHelper.cs 中的GeneratorDataList完整枚举了插件暴露的全部关键字、说明与示例,这是插件在#空查询时展示的建议列表来源。逐条对应如下(示例直接取自源码):
| 关键字 | 功能 | 示例(源码原文) |
|---|---|---|
uuid/guid | 生成随机 UUID(默认版本 4) | uuid或guid |
uuidv1 | 生成版本 1:基于时间的 UUID | uuidv1或uuid1 |
uuidv3 | 版本 3(MD5):基于命名空间和名称的 UUID | uuidv3 ns:<DNS, URL, OID, 或 X500> <your input> |
uuidv4 | 版本 4:随机生成的 UUID | uuidv4或uuid4 |
uuidv5 | 版本 5(SHA1):基于命名空间和名称的 UUID | uuidv5 ns:<DNS, URL, OID, 或 X500> <your input> |
uuidv7 | 版本 7:按时间排序的随机 UUID | uuidv7或uuid7 |
md5 | MD5 哈希 | md5 <your input> |
sha1 | SHA1 哈希 | sha1 <your input> |
sha256 | SHA256 哈希 | sha256 <your input> |
sha384 | SHA384 哈希 | sha384 <your input> |
sha512 | SHA512 哈希 | sha512 <your input> |
base64 | Base64 编码字符串 | base64 <your input> |
base64d | Base64 解码字符串 | base64d <your input> |
url | URL 编码 | url https://bing.com/?q=My Test query |
urld | URL 解码 | urld https://bing.com/?q=My+Test+query |
esc:data | 数据串转义(Uri.EscapeDataString) | esc:data C:\Program Files\PowerToys\PowerToys.exe |
uesc:data | 数据串反转义 | uesc:data C%3A%5CProgram%20Files%5CPowerToys%5CPowerToys.exe |
esc:hex | 单个字符的十六进制转义 | esc:hex z |
uesc:hex | 单个十六进制转义字符还原 | uesc:hex %7A |
几个使用细节值得注意:
- 所有命令前必须带
#前缀(如# md5 hello),否则不会路由到本插件; - 哈希、Base64 等命令的参数是取关键字之后的原始查询串(
RawUserQuery.Substring(commandIndex + command.Length)),因此参数中的空格会被完整保留并参与计算; guid是uuid的别名,输入guid、guidv1、guidv3等均按相同逻辑解析(截图示例即为# guidv3 ns:DNS www.microsoft.com)。
核心契约:IComputeRequest 接口
插件的抽象核心是 IComputeRequest.cs,每个具体生成器都必须实现该接口:
bool Compute():执行计算,必须填充IsSuccessful以及Result与ErrorMessage中的一个;string ResultToString():返回结果的可读文本,直接用作结果条目的标题(Title);string Description:用作结果条目的副标题(Subtitle),例如哈希请求的SHA256(原始字符串);byte[] Result:计算结果的原始字节。
Main.GetResult()(Main.cs)把request.Result存入ContextData、ResultToString()作为标题、Description作为副标题,并在点击动作里复制ResultToString()的输出。这个“解析 → 请求对象 → Compute → Result”的三段式结构,是理解各生成器实现的关键。
InputParser:查询解析规则与错误约定
InputParser.cs 唯一职责是解析用户查询,ParseInput()要么返回一个IComputeRequest实例,要么抛出FormatException或ArgumentException。原文档对此的错误语义约定非常重要,源码中也能逐条印证:
ArgumentException表示用户可修正的输入错误(如不支持的哈希函数、无效 GUID 版本、无效命名空间),其消息会直接展示给用户,且不写日志。例如 Main.cs 捕获ArgumentException后调用GetErrorResult(e.Message),生成带警告图标的错误条目;FormatException表示“查询可能变有效”或“彻底无效”(如还没输入内容、哈希命令后还没有要跟字符串),此时不向用户显示错误,只写一条 Debug 日志。从 Main.cs 可以看到,捕获FormatException后仅Log.Debug(...),若当前是通过#动作关键字进入,还会退化为模糊匹配关键字的建议列表(GetSuggestionFuzzyResults),所以输入# md时不会弹错误,而是继续给出md5等候选项。
具体解析分支(对照 InputParser.cs 源码):
md5:精确匹配,参数为空时抛FormatException("Empty hash request");sha*:前缀匹配后按command.Substring(3)分支为1/256/384/512,其余抛出FormatException,提示支持变体为 SHA1、SHA256、SHA384、SHA512;guid*/uuid*:版本默认 4;取command第 5 个字符起的内容(guid/uuid之后),若以v开头则去掉,再int.TryParse得到版本号,解析失败抛FormatException,提示支持的版本为 1、3、4、5、7。版本 3/5 时要求恰好两个参数(命名空间 + 名称),参数个数不对时抛ArgumentException并给出示例uuidv{version} ns:<DNS, URL, OID, or X500> <your input>;esc:/uesc:前缀分别进入转义与反转义分支,仅接受esc:data、esc:hex、uesc:data、uesc:hex四种精确关键字,其余形式抛FormatException。其中esc:hex要求输入恰好 1 个字符(超过 1 个字符抛ArgumentException,为空抛FormatException);- 其余未识别输入统一抛
FormatException($"Invalid Query: {query.RawUserQuery}")。
哈希生成器:HashRequest
HashRequest.cs 实现IComputeRequest,支持System.Security.Cryptography中的 MD5、SHA1、SHA256、SHA384、SHA512。其内部_algorithms字典(HashRequest.cs)静态缓存了各算法的HashAlgorithm实例,Compute()直接调用_algorithms[AlgorithmName].ComputeHash(DataToHash)。文档中给出的扩展提示与源码一致:若未来System.Security.Cryptography增加新的算法,只需向_algorithms字典注册,并让InputParser.ParseInput()能对该关键字返回HashRequest。
两个实现细节:
ResultToString()将结果字节逐字节格式化为大写十六进制(b.ToString("X2"))拼接,因此结果是大写Hex 字符串;Description动态生成算法名(原始输入),例如SHA256(hello),作为结果副标题展示;- 构造函数对
dataToHash做空引用检查;Compute()中若DataToHash为 null 会记录异常日志并置IsSuccessful = false,错误信息为Null data passed to hash request。
哈希输入统一按 UTF-8 编码为字节(Encoding.UTF8.GetBytes(content),见 InputParser.cs),即命令参数以什么字节序列进入算法是明确且可预期的。
Base64 编解码
Base64Request.cs 与 Base64DecodeRequest.cs 分别处理base64与base64d:
Base64Request的构造函数接收字节数组(由InputParser以Encoding.UTF8.GetBytes(content)传入),Compute()调用System.Convert.ToBase64String得到编码串;Base64DecodeRequest的构造函数接收字符串,Compute()调用System.Convert.FromBase64String得到解码后的字节数组,ResultToString()再按 UTF-8 还原为文本。
两者的共同模式是:Compute()内 try/catch 包裹,异常时通过Log.Exception记录、填充ErrorMessage并置IsSuccessful = false;解码失败(如非法 Base64 串)时结果标题会展示 .NET 的原始异常消息。
GUID 生成器:五个版本与命名空间
GUIDRequest.cs 实现IComputeRequest,通过 GUIDGenerator.cs 这个工具类实际生成或计算 GUID。各版本的底层来源与源码逐一对应:
- 版本 1(时间基准):
V1()调用 Win32 APIUuidCreateSequential(经NativeMethods.UuidCreateSequentialP/Invoke),返回码为RPC_S_OK或RPC_S_UUID_LOCAL_ONLY均视为成功,否则抛InvalidOperationException("Failed to create GUID version 1"); - 版本 4(随机):
V4()直接调用System.Guid.NewGuid(); - 版本 7(时间有序):
V7()调用 .NET 8 引入的System.Guid.CreateVersion7(); - 版本 3/5(命名空间 + 名称):
V3AndV5()(GUIDGenerator.cs)按 RFC 4122 手工实现——先把命名空间 GUID 的time_low、time_mid、time_hi_and_version三个字段转为网络字节序,拼接名称的 ASCII 字节后,版本 3 用MD5.HashData、版本 5 用SHA1.HashData取前 16 字节,再将版本号按time_hi_and_version &= 0x0FFF; | (version << 12)写入,并将第 9 字节高两位置为10(变体位)后构造Guid。源码中对 MD5/SHA1 的使用以#pragma warning disable CA5351/CA5350显式压制了“弱算法”告警——这是规范要求的,而非性能取舍。
命名空间参数规则(与文档一致):命名空间必须是一个合法 GUID,或是 GUIDGenerator.cs 中PredefinedNamespaces字典提供的 RFC 4122 附录 C 预定义命名空间别名:
| 别名 | 预定义 GUID |
|---|---|
ns:dns | 6ba7b810-9dad-11d1-80b4-00c04fd430c8 |
ns:url | 6ba7b811-9dad-11d1-80b4-00c04fd430c8 |
ns:oid | 6ba7b812-9dad-11d1-80b4-00c04fd430c8 |
ns:x500 | 6ba7b814-9dad-11d1-80b4-00c04fd430c8 |
GUIDRequest.cs 的构造函数做了三重校验:
- 版本必须是 1、3、4、5、7 之一(2 和 6 明确不支持,
Version is < 1 or > 7 or 2 or 6时抛ArgumentException); - 版本 3/5 时命名空间不能为 null,且先按小写字符串查
PredefinedNamespaces,查不到再尝试Guid.TryParse,两者都失败时抛ArgumentNullException,消息列出所有可用别名; - 版本 3/5 时名称(name)不能为 null。
Description属性按版本返回不同副标题,如Version 3 (SHA1 算法名): Namespace and name based GUID.、Version 7: Time-ordered randomly generated GUID等,这些正是结果列表第二行显示的内容(截图中的Version 3 (MDS): Namespace and name based GUID.即此输出)。
URI 转义与反转义
Generators/Uri目录下六个请求类分别对应六个命令,底层 API 与 InputParser.cs 的分发一一对应:
| 类文件 | 命令 | 底层 API | 说明 |
|---|---|---|---|
| UrlEncodeRequest.cs | url | HttpUtility.UrlEncode() | 整个 URL 编码,空格变+ |
| UrlDecodeRequest.cs | urld | HttpUtility.UrlDecode() | 整个 URL 解码 |
| DataEscapeRequest.cs | esc:data | System.Uri.EscapeDataString() | 数据串转义,空格变%20 |
| DataUnescapeRequest.cs | uesc:data | System.Uri.UnescapeDataString() | 数据串反转义 |
| HexEscapeRequest.cs | esc:hex | System.Uri.HexEscape() | 仅支持单字符输入,构造函数对长度不为 1 的输入抛ArgumentOutOfRangeException |
| HexUnescapeRequest.cs | uesc:hex | System.Uri.HexUnescape() | 只还原字符串开头第一个十六进制转义序列(先Uri.IsHexEncoding探测,再HexUnescape),用户输入其余部分被忽略 |
url与esc:data的差别在于HttpUtility.UrlEncode面向完整 URL(空格 →+),而Uri.EscapeDataString面向单个数据分量(空格 →%20);esc:hex/uesc:hex则是最细粒度的单字符%XX转义对。四个类的错误处理模式与 Base64 一致:异常时记日志、填ErrorMessage、Result保持 null,此时ResultToString()返回空串,结果条目因标题为空不会被加入(见 Main.cs 中对Title为空时直接返回空列表的处理)。
建议列表与模糊匹配:Main 层的两个辅助路径
除了主计算路径,Main.cs 还有两条展示路径,理解它们能解释插件的实际交互行为:
- 空查询建议:当
Search为空但ActionKeyword存在(即刚输入#)时,GetSuggestionResults()遍历 QueryHelper.GeneratorDataList 生成全部 18 条关键字建议,点击后通过_context.API.ChangeQuery($"{query.ActionKeyword} {generatorData.Keyword} ", true)直接把关键字写入查询框; - 模糊建议:解析出
FormatException且带ActionKeyword时,GetSuggestionFuzzyResults()用StringMatcher.FuzzySearch对输入做模糊打分,把得分大于 0 的关键字作为候选返回。
这正是“FormatException不直接报错”约定的产品意义:输入过程中的中间态(如# md)永远展示候选而非错误,只有ArgumentException这类确定的用户错误才呈现警告条目。
如何为插件新增一个生成器
原文档给出的三步扩展流程,与源码结构完全对应,可按此操作(在自己的 fork/本地环境中进行,本仓库只读):
- 在 Generators/ 目录下新建文件夹(与
Hashing、Base64、GUID、Uri并列),在其中实现IComputeRequest接口的请求类,Compute()负责填充IsSuccessful、Result或ErrorMessage; - 新生成器私有的工具类放在同一文件夹内,与其他生成器隔离——可参照
GUID文件夹中GUIDGenerator与GUIDRequest的分工:工具类负责纯计算,*Request类负责对接接口契约; - 修改 InputParser.ParseInput(),为新关键字增加分支并返回第 1 步创建的实例。同时注意遵守前文错误约定:用户可修正的错误抛
ArgumentException(会展示给用户),中间态/无效查询抛FormatException(只记日志)。若希望新命令出现在#建议列表与模糊匹配中,还应向 QueryHelper.GeneratorDataList 追加一条GeneratorData(关键字、描述、示例),并在 Resources.resx 中提供可本地化的描述资源。
小结
Value Generator 插件用一套极简的IComputeRequest契约 + 一个InputParser分发器,把哈希、Base64、GUID、URL 四类共 18 个命令收敛在数百行代码内:解析层严格区分“可修正错误”与“中间态”两种异常语义,计算层各请求类只关心填充Result/ErrorMessage,展示层统一由 Main.cs 负责标题、副标题与剪贴板复制。所有行为均可在源码中逐条验证,扩展新生成器也只需要遵循“新建 Generator 文件夹 → 实现接口 → 注册解析分支”三步约定。
【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考