首页
/ PowerShell 资源文件工程实践:.resx 资源体系、Start-ResGen 强类型绑定生成与 .txt 迁移指南

PowerShell 资源文件工程实践:.resx 资源体系、Start-ResGen 强类型绑定生成与 .txt 迁移指南

2026-09-06 13:34:40作者:姚月梅Lane

本文以 PowerShell 仓库的开发者文档 docs/dev-process/resx-files.md 为主体,系统讲解 PowerShell 代码库中 .resx 资源文件的组织方式、为什么需要自研的 Start-ResGen 工具为资源生成强类型 C# 绑定、日常编辑资源文件的正确姿势,以及如何用 Convert-TxtResourceToXml 把旧式 .txt 资源一次性迁移为 .resx。读完本文后,你能够独立完成资源字符串的修改、在遇到资源相关编译错误时正确触发代码生成,并理解生成产物的结构,避免在 Visual Studio 中误操作资源文件。

一、资源是什么:.resx 文件与 src/<项目>/resources 布局

PowerShell 中的 Resources 就是存放字符串值的 .resx 文件,主要用于错误消息等面向用户的文本。按照文档说明,它们统一放在各个项目目录下的 src\<project>\resources 文件夹中。

从当前仓库可以直观看到这一布局,例如:

  • src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.resx(同目录下还保留着 GetEventResources.txt,见后文迁移一节)
  • src/Microsoft.PowerShell.Security/resources/CertificateCommands.resxsrc/Microsoft.PowerShell.Security/resources/CmsCommands.resx

值得注意的是,资源目录中还包含按语言划分的本地化子目录,例如 Diagnostics 项目下就有 csdeesfritjakoplpt-BRrutrzh-Hanszh-Hant 等文件夹(见 src/Microsoft.PowerShell.Commands.Diagnostics/resources/),Microsoft.PowerShell.Security 下同样有 estr 等本地化 .resx 文件。这意味着新增或修改一个资源键时,主资源文件是"源头",各语言文件夹中存放的是对应的翻译覆盖文件。

.resx 本质上是一个简单的 XML 文件:<root> 节点下包含 XSD schema、resheader(reader/writer、版本等元数据)以及若干 <data name="..." xml:space="preserve"><value>...</value></data> 条目。正因为结构简单,文档给出的第一条编辑原则是:

  • 不要在 Visual Studio 中编辑 .resx 文件。VS 的资源设计器会试图替你生成 .cs 文件,从而产生一堆难以理解的错误;
  • 要编辑资源文件,请使用任意纯文本编辑器,因为资源文件就是简单的 XML,易于编辑。

二、为什么需要自研 Start-ResGen:dotnet CLI 的能力缺口

按文档的说明:目前 dotnet cli 并不支持生成 C# 绑定(即强类型资源文件),PowerShell 因此使用自研的 Start-ResGen 来生成这些绑定。

所谓"强类型资源文件",指的是把每个 .resx 变成一个 C# 类,其中每个资源键对应一个静态属性,代码中直接以 SomeResourceClass.SomeKey 的方式取值,而不是散落的字符串字面量。由于 dotnet 构建体系不提供这一生成能力,仓库自己实现了一个小工具项目 src/ResGen/ResGen.csproj——一个 net11.0 的命令行可执行程序,项目描述即为 "Generates C# typed bindings for .resx files"。

2.1 常规构建中:Start-PSBuild -ResGen

文档指出,Start-ResGen 通常在常规构建中被顺带调用:

Start-PSBuild -ResGen

在构建脚本 build.psm1 中可以看到对应实现:Start-PSBuild 定义了 -ResGen 开关参数(第 396 行),主流程中有一段"在新机器上需要跑 ResGen"的启发式判断(约第 664-669 行):

# handle ResGen
# Heuristic to run ResGen on the fresh machine
if ($ResGen -or -not (Test-Path "$PSScriptRoot/src/Microsoft.PowerShell.ConsoleHost/gen")) {
    Write-Log -message "Run ResGen (generating C# bindings for resx files)"
    Start-ResGen
}

也就是说:显式传了 -ResGen,或者检测到 src/Microsoft.PowerShell.ConsoleHost/gen 目录不存在(典型如全新克隆的机器),都会自动执行 ResGen。这也解释了文档的另一条建议——如果你看到与资源相关的编译错误,就显式调用一次 Start-ResGen

Start-ResGen

因为这类错误的常见原因就是强类型绑定尚未生成(gen 目录缺失)。

2.2 Start-ResGen 的实现:src/ResGen 工具

Start-ResGen 定义于 build.psm1,实现非常直接:把目录切到 src/ResGen,然后运行 dotnet run。真正的生成逻辑在 src/ResGen/Program.cs 中,从源码结构看,它的行为是:

  1. 遍历 src/ 下所有项目目录:以 Directory.EnumerateDirectories("..") 的方式枚举 src/*;对每个目录,若存在 resources 子目录,则进入资源生成流程。这与文档中"资源住在 src\<project>\resources"的约定完全对应。

  2. 生成输出到 gen/ 目录:为每个项目创建 <project>/gen 文件夹,并把每个 <project>/resources/*.resx 生成一个同名 <ClassName>.cs。生成的代码不提交进仓库——gen/ 目录是构建期产物,这正是上面"检测 ConsoleHost/gen 是否存在"作为触发条件的依据。

  3. 文件命名决定访问级别与命名空间

    • 类名取自 .resx 文件的主文件名。例如 GetEventResources.resx 会生成 GetEventResources 类;
    • 如果文件名以 public. 前缀开头(不区分大小写),则生成的类为 public,否则为 internal,并且类名会去掉该前缀;
    • 如果类名中包含点号,最后一个点之前的部分会被用作 namespace,点之后的部分作为类名(例如 Full.Name.Of.The.ClassFoo.resx → 命名空间 Full.Name.Of.The + 类 ClassFoo)。
  4. 每个资源键变成一个静态字符串属性:解析 .resx 的每个 <data> 节点后,按模板生成形如:

    /// <summary>
    ///   Looks up a localized string similar to {资源文本}
    /// </summary>
    internal static string 键名 {
        get {
            return ResourceManager.GetString("键名", resourceCulture);
        }
    }
    

    资源键中的空格会被替换为下划线(name.Replace(' ', '_'))。类内部通过一个缓存的 ResourceManager(基础名格式为 <项目名>.resources.<类名>)来查找本地化字符串,并提供 Culture 属性以覆盖当前线程的资源查找文化。

因此,修改 .resx 中某个字符串后,必须重跑一次 ResGen(Start-PSBuild -ResGen 或显式 Start-ResGen),gen/ 下的强类型类才会同步;跳过这一步就会出现文档所说的"资源相关编译错误"。

三、把旧式 .txt 资源文件转换为 .resx

文档指出:dotnet cli 也不支持内嵌传统(old-fashioned)的 .txt 资源文件,因此提供了一次性转换的辅助函数,将 .txt 资源转为 .resx

# example, converting all .txt resources under src\Microsoft.WSMan.Management\resources
Convert-TxtResourceToXml -Path src\Microsoft.WSMan.Management\resources

转换后的 .resx 文件会与 .txt 文件并排放置在同目录下。仓库中可以找到这一约定留下的真实痕迹:src/Microsoft.PowerShell.Commands.Diagnostics/resources/ 下同时存在 GetEventResources.txtGetEventResources.resx

build.psm1Convert-TxtResourceToXml 的实现看,其行为是:

  • 对传入路径下的每个 *.txt 文件,读取全文并用 ConvertFrom-StringData 解析——即 .txt 资源采用的是 PowerShell StringData 格式(键 = 值 形式的多行文本);
  • 把每个键值对渲染为标准的 <data name="键" xml:space="preserve"><value>值</value></data> XML 片段;
  • 将全部片段套用脚本内置的 resx 模板($script:RESX_TEMPLATE,包含 Microsoft ResX Schema 的 <xsd:schema> 与 reader/writer 头部)写出到 <同名>.resx
  • 该函数接受 [string[]]$Path,因此可以一次传入多个资源目录批量转换。

转换只是格式迁移这一步:转换之后,后续的资源修改就回到"用纯文本编辑器改 .resx,构建时由 ResGen 生成绑定"的标准流程。

四、补充:另一条 resx 生成路径——ETW 清单驱动的资源生成

Start-ResGen 这条"从 .resx 生成 C# 绑定"的主线外,仓库还存在一条方向相反的 resx 生成路径,值得了解以免混淆:tools/ResxGen/ResxGen.ps1(配套实现 tools/ResxGen/ResxGen.psm1)的作用是从 ETW 事件清单文件(.man 反向生成 .resx 和 C# 代码,用于 UNIX 构建场景。其调用示例(见脚本头部注释):

.\tools\ResxGen\ResxGen.ps1 `
  -Manifest .\src\PowerShell.Core.Instrumentation\PowerShell.Core.Instrumentation.man `
  -ResxPath .\src\System.Management.Automation\resources `
  -CodePath .\src\System.Management.Automation\CoreCLR

它会解析清单中的 stringTable 消息表与 event 事件表(tools/ResxGen/ResxGen.psm1 中的 ManifestEventMessageEventEntry 类),把 %1%99 的 FormatMessage 占位符转换为 String.Format 风格的 {0}{98},再产出两个文件:

  • <Name>.resx(默认 EventResource.resx):包含消息文本,写入 -ResxPath 指定目录;
  • <Name>.cs(默认 EventResource.cs):一个带 #if UNIX 包裹的静态类,GetMessage(int eventId, out int parameterCount) 通过 switch 把事件 ID 映射到资源键,写入 -CodePath 指定目录,默认命名空间为 System.Management.Automation.Tracing

这条路径服务于事件跟踪(ETW)本地化场景,与本文主线(手工维护的 .resx 资源及其强类型绑定)是两套独立机制:前者以 .man 清单为"源",后者以 .resx 为"源"。理解这一点有助于在改动跟踪事件或资源文件时选对工具。

五、实践要点速查

结合 docs/dev-process/resx-files.md 的原始指引与上述源码证据,日常开发可按以下清单执行:

场景 操作 依据
新增/修改资源字符串 用纯文本编辑器直接改 src/<项目>/resources/*.resx,不要用 Visual Studio 资源设计器 文档明确警告 VS 会生成 .cs 导致难懂的错误
常规构建 Start-PSBuild -ResGen,让 ResGen 随构建一并执行 build.psm1 中的自动触发逻辑
出现资源相关编译错误 显式运行 Start-ResGen 重新生成 gen/ 绑定 文档建议;gen/ 为构建期产物,缺失即报错
全新克隆机器首次构建 无需额外操作,构建检测到 src/Microsoft.PowerShell.ConsoleHost/gen 不存在会自动跑 ResGen build.psm1 的启发式判断
迁移遗留的 .txt 资源 Convert-TxtResourceToXml -Path src\<项目>\resources.resx 生成在 .txt build.psm1
修改需要对外暴露的资源类 public. 前缀命名 .resx 文件名,ResGen 会生成 public src/ResGen/Program.cs
修改 ETW 清单中的事件消息 运行 tools/ResxGen/ResxGen.ps1 重新生成 EventResource.resx/.cs 脚本注释中的示例命令

最后重申文档中最关键的两条纪律:资源文件即普通 XML,用纯文本编辑器维护;任何 .resx 改动之后都要确保 ResGen 重新运行——这两点覆盖了绝大多数资源相关构建问题的根因。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388