PowerShell 二进制模块实战:用 .NET Standard 2.0 和 dotnet CLI 构建跨平台 Cmdlet
本文基于 PowerShell 仓库中的 docs/cmdlet-example/command-line-simple-example.md 指南,完整演示如何使用 .NET Core 命令行工具(dotnet CLI)从零创建一个基于 .NET Standard 2.0 类库的 PowerShell 二进制模块(Binary Module)。读完本文,你将掌握:如何用 dotnet new classlib 搭建工程、引用 PowerShell Standard Library 包、编写 C# Cmdlet 并构建、导入和调用;同时理解 .NET Standard 2.0 程序集为何能同时被 PowerShell Core 与 Windows PowerShell 加载,以及在没有 .NET Framework 4.7.1 的 Windows 系统上出现 netstandard.dll 缺失错误时的完整修复步骤。
为什么选择 .NET Standard 2.0
PowerShell 存在两个实现:PowerShell Core(.NET Core 之上,跨平台)和 Windows PowerShell(.NET Framework 之上,3.0 及以上版本)。如果二进制模块的目标框架是 .NET Standard 2.0 类库,那么同一份程序集可以同时导入这两种实现——你不必为它们分别构建和分发不同的程序集。
这一设计在仓库源码中有直接印证:Cmdlet 的基类 PSCmdlet 就定义在 System.Management.Automation 核心引擎源码 中,外部二进制模块通过引用该程序集的 API(或 PowerShell Standard Library 中对应的 API 子集)来派生 PSCmdlet/Cmdlet。仓库同时提供了 Visual Studio 图形界面方式构建同一 Cmdlet 的对照指南,与本文的 CLI 方式互为补充。
前提条件
-
PowerShell Core 和/或 Windows PowerShell
本例可在 PowerShell Core 支持的任何操作系统上进行。仓库 README.md 中的 "Get PowerShell" 部分列出了各平台的支持情况与安装方式。
说明:在 Windows 10 周年更新(Anniversary Update)及以上版本上,可以通过 WSL(Windows Subsystem for Linux)控制台构建模块。若要导入和使用该模块,需要为你的 Linux 发行版安装对应版本的 PowerShell Core。可以运行
lsb_release -a查看当前 WSL 的发行版信息。 -
.NET Core 2.x SDK
为你的操作系统下载并安装 .NET Core 2.x SDK。在 Linux 上建议使用包管理器安装,并按你的发行版(如 RHEL、Debian 等)选择对应的安装说明。
注:本指南以 .NET Core 2.x SDK 为适用前提;当前仓库自身构建使用更新版本的 .NET SDK(见仓库根目录的 global.json,其中锁定了具体的 SDK 版本),但本示例面向的是编写第三方二进制模块的开发者,使用 2.x SDK 即可复现全部步骤。
创建 .NET Standard 2.0 二进制模块
第 1 步:确认 dotnet CLI 版本
dotnet --version
输出应为 2.0.0 或更高。如果返回主版本号为 1,说明尚未安装 .NET Core 2.x SDK,请安装后重启 Shell 再重新执行。
第 2 步:创建 classlib 启动项目
dotnet new classlib --name MyModule
classlib(类库)项目模板的默认目标框架就是 .NET Standard 2.0,正好满足跨 PowerShell 实现的要求。
第 3 步:添加 global.json 锁定 SDK 版本
cd MyModule
dotnet new globaljson --sdk-version 2.0.0
global.json 的作用是显式声明项目所需的 .NET Core SDK 版本(本例为 2.0.0)。这在机器上安装了多个 SDK 版本时尤其重要,可以防止 dotnet 工具自动挑选到不兼容的高版本。
这一做法与仓库自身的实践一致:PowerShell 主仓库根目录就有一份 global.json 文件来锁定 SDK 版本,从源码结构看这是 .NET 项目应对多 SDK 共存的标准手段。
第 4 步:添加 PowerShell Standard Library 包
dotnet add package PowerShellStandard.Library --version 3.0.0-preview-01
PowerShellStandard.Library 包提供 System.Management.Automation 程序集的精简 API 面(即 PowerShell Standard 3.0 定义的子集)。注意:随着该库发布新版本,应把命令中的版本号更新为最新版。
该包 API 面的约束在仓库的测试中也有验证痕迹,例如 StandardLibraryTypes.Tests.ps1 就测试了标准库类型在会话中的行为,可以从中了解 Standard Library 类型系统与引擎的交互。
第 5 步:编写 Cmdlet 源码
用编辑器打开 Class1.cs,将原有代码替换为以下代码:
using System;
using System.Management.Automation;
namespace MyModule
{
[Cmdlet(VerbsCommunications.Write, "TimestampedMessage")]
public class WriteTimestampedMessageCommand : PSCmdlet
{
[Parameter(Position=1)]
public string Message { get; set; } = string.Empty;
protected override void EndProcessing()
{
string timestamp = DateTime.Now.ToString("u");
this.WriteObject($"[{timestamp}] - {this.Message}");
base.EndProcessing();
}
}
}
源码要点解析:
[Cmdlet(VerbsCommunications.Write, "TimestampedMessage")]:CmdletAttribute将普通 C# 类注册为 Cmdlet。动词取自 PowerShell 批准动词集VerbsCommunications.Write,与名词TimestampedMessage组合后,命令名为Write-TimestampedMessage。命名必须遵循 "动词-名词" 规范,仓库引擎源码 CommandBase.cs 中可以看到 Cmdlet 元数据(CommandMetadata)就是围绕这一约定构建的。public class WriteTimestampedMessageCommand : PSCmdlet:继承引擎提供的基类PSCmdlet(定义于 MshCmdlet.cs),它封装了参数绑定、事务支持等基础设施;如果不需要这些能力,也可以像 Visual Studio 版示例 那样直接继承更轻量的Cmdlet。[Parameter(Position=1)]:声明Message属性为位置参数,因此调用时可以直接按位置传值(如Write-TimestampedMessage "Test message.")。EndProcessing():Cmdlet 生命周期中处理完所有输入后的收尾阶段。本例没有逐条输入,因此在EndProcessing中一次性输出带时间戳的消息。仓库引擎内大量内置命令同样覆写该方法做收尾输出,例如 GetCommandCommand.cs、Modules/NewModuleCommand.cs。DateTime.Now.ToString("u"):"u"为通用排序格式(UTC,形如2016-02-01 12:34:56Z),保证时间戳格式稳定可排序。this.WriteObject(...):向输出流写出对象,即 Cmdlet 的标准输出方式。
第 6 步:构建项目
dotnet build
构建产物位于 bin/Debug/netstandard2.0/MyModule.dll,其程序集目标为 netstandard2.0。
第 7 步:导入模块并调用命令
注意:前面的步骤在 Linux 上也可以换用 Bash 等其他 Shell 完成;到这一步请确保运行的是 PowerShell Core。
cd 'bin/Debug/netstandard2.0'
Import-Module ./MyModule.dll
Write-TimestampedMessage "Test message."
导入后,Write-TimestampedMessage 命令即出现在当前会话中,执行后会输出一行带 UTC 时间戳的消息。
在 Windows PowerShell 中使用 .NET Standard 2.0 模块
一个作为 .NET Standard 2.0 类库编译的 .NET 程序集,理论上可以加载到 .NET Core 2.x 应用(如 PowerShell Core)和 .NET Framework 4.6.1 及以上应用(如 Windows PowerShell)中,从而实现"一份程序集,两个宿主"。
但有一个前提:宿主应用需要已经针对 .NET Standard 2.0 编译、或声明了对 .NET Standard 库的支持。这样构建系统才能提供必要的绑定重定向(binding redirects)以及 facade/shim 程序集,让 .NET Standard 2.0 库在运行中的应用上下文里找到所需的 .NET Framework 类型。
好消息:.NET Framework 4.7.1 与 Windows 10 秋季创造者更新(Fall Creators Update)已修复这一问题——这些系统上无需修改或重新编译,.NET Standard 2.0 二进制模块即可直接在 Windows PowerShell 中工作。
坏消息:在未升级到 .NET Framework 4.7.1 的 Windows 系统上(如 Windows 10 1703 或更低版本),模块会出错。
复现错误:netstandard.dll 缺失
-
将
MyModule.dll拷贝到一台 Windows 机器的某个文件夹。 -
导入模块:
Import-Module .\MyModule.dll注意:此时导入本身不会报错。
-
执行命令:
Write-TimestampedMessage "Test message."在没有 .NET Framework 4.7.1 的 Windows 10 CU(1703 或更低版本)上会得到如下错误:
Write-TimestampedMessage : Could not load file or assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified. At line:1 char:1 + Write-TimestampedMessage "Test message." + ~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], FileNotFoundException + FullyQualifiedErrorId : System.IO.FileNotFoundException
如果你这边命令执行成功了,说明你的系统已更新到 .NET Framework 4.7.1。否则,这个错误意味着 MyModule.dll 找不到 Windows PowerShell 所用 .NET Framework 版本对应的 netstandard.dll 实现程序集。
修复方案:补齐 netstandard.dll 实现程序集
如果你已安装(或准备安装)Windows 版的 .NET Core SDK,可以在以下目录中找到面向 .NET 4.6.1 的 netstandard.dll 实现程序集:
C:\Program Files\dotnet\sdk\<version-number>\Microsoft\Microsoft.NET.Build.Extensions\net461\lib
其中 <version-number> 取决于你安装的 SDK 版本。把该目录下的 netstandard.dll 复制到 MyModule.dll 所在目录,Write-TimestampedMessage 即可正常工作。完整操作步骤:
-
安装 Windows 版 .NET Core SDK(如尚未安装)。
-
启动一个新的 Windows PowerShell 控制台。注意:二进制程序集一旦加载进 PowerShell 就无法卸载,重启 PowerShell 是重新加载
MyModule.dll的必要条件。 -
将 .NET 4.6.1 的
netstandard.dll实现程序集复制到模块所在目录:cd 'path-to-where-you-copied-module.dll' Copy-Item 'C:\Program Files\dotnet\sdk\<version-number>\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\netstandard.dll' . -
导入模块并执行命令:
Import-Module .\MyModule.dll Write-TimestampedMessage "Test message."此时命令应当成功。如果仍失败,请重启 Windows PowerShell,确保会话中没有之前加载的旧版本程序集,然后重复本步骤。
如果模块还依赖其他第三方库,可能需要做更多类似工作;本方案已在使用 System.Xml 和 System.Web 类型时成功验证过。
小结
通过上述步骤,我们完成了一个用 .NET Standard 2.0 类库构建的 PowerShell 二进制模块,它的运行环境覆盖:
- PowerShell Core:在所有受支持的操作系统上均可运行,且可以在 Linux、macOS 和 Windows 上通过 .NET Core 2.x SDK 命令行工具构建;
- Windows PowerShell:在已更新到 .NET Framework 4.7.1 的系统(包括自带该版本的 Windows 10 秋季创造者更新)上可直接运行;在更低版本的 Windows 上,可通过复制
netstandard.dll实现程序集修复。
如果需要图形化开发体验,仓库提供了平行的 Visual Studio 版本指南 visual-studio-simple-example.md(同样基于 .NET Standard 2.0 + PowerShellStandard.Library,并附各步骤截图)。对于希望直接引用完整 PowerShell API 而非 Standard 子集的场景,仓库的 Microsoft.PowerShell.SDK 元项目文档说明了如何将 PowerShell 子项目打包为 SDK 供外部引用。选择哪条路线,取决于你需要的 API 面和需要兼容的 PowerShell 版本。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00