首页
/ WSL 容器 API 的 C 投影:Microsoft.WSL.Containers 下 CsWinRT 类型映射完全指南

WSL 容器 API 的 C 投影:Microsoft.WSL.Containers 下 CsWinRT 类型映射完全指南

2026-09-09 12:07:50作者:乔或婵

本文是 WSL 容器 API C# 参考系列中的类型映射专篇,聚焦 Microsoft.WSL.Containers 命名空间下 WinRT 类型 → C# 投影类型 的完整对应关系。它适用于所有通过 C# 调用 WSL 容器 SDK(Session → Container → Process 三层模型)进行 Windows 桌面/服务端应用开发的读者;读完本文,你将能准确读懂 C# API 参考 中每个方法的签名,正确选用集合类型、可空值类型与异步进度类型,避免在事件回调、异步等待与属性赋值时出现类型不匹配或投影语义误解。

背景:C# 投影从何而来

WSL 容器 SDK 的公共 C# 表面并非手写的一套独立 API,而是对 WinRT 层实现的镜像投影。仓库中定义 WinRT 投影的权威来源是 wslcsdk.idl,其命名空间即为 Microsoft.WSL.Containers;而 Projection.csWinRTActivation.cs 构成 C# 侧的激活与投影包装。正如 Overview 所述:“The public C# surface mirrors the WinRT surface implemented by the winrt_*.h / winrt_*.cpp wrappers.”

因此,理解 C# API 的正确姿势是:先看 IDL 中该 WinRT 类型的声明,再套用本文的映射表翻译成 C# 类型。CsWinRT(C#/WinRT 投影生成器)负责在编译期把 IDL 中的 WinRT 类型自动投影为对应的 .NET 类型,这正是本文映射表存在的底层机制。

核心映射表(完整清单)

以下 14 项映射是 C# 投影中最常遇到的对应关系,也是本文的骨架:

WinRT 类型 C# 投影类型
hstring string
Windows.Foundation.Uri System.Uri
Windows.Foundation.TimeSpan System.TimeSpan
IReference<uint32_t> uint?
IReference<TimeSpan> TimeSpan?
IReference<ContainerNetworkingMode> ContainerNetworkingMode?
IVector<T> IList<T>
IVectorView<T> IReadOnlyList<T>
IMap<string, string> IDictionary<string, string>
Windows.Foundation.DateTime DateTimeOffset
com_array<uint8_t> 事件负载 byte[]
Windows.Networking.HostName Windows.Networking.HostName
IAsyncActionWithProgress<T> 可等待的 WinRT 异步操作(awaitable WinRT async operation)

下面逐类展开,说明每一行的投影语义,并给出在 wslcsdk.idl 中的真实使用位置。

字符串与基础类型:hstringstring

WinRT 的 hstring(不可变字符串)在 C# 中投影为 string,这是最基础也最常用的映射。在 IDL 中几乎每个 runtimeclass 的属性都用到它,例如:

  • SessionSettings.NameSessionSettings.StoragePath(idl 第 54-55 行);
  • ProcessSettings.WorkingDirectoryContainerSettings.ImageNameContainerSettings.NameHostNameDomainName
  • ImageInfo.NamePullImageOptions.UriPushImageOptions.ImageTagImageOptions 三个属性。

在 C# 侧这些字段全部以 string 出现,可直接与 Console.WriteLinePath API、字符串插值等互操作,无需任何转换。

可空值类型:IReference<T>T?

WinRT 没有“可空值类型”语法,可空性通过 Windows.Foundation.IReference<T> 表达;CsWinRT 将 IReference<T> 投影为 C# 的可空值类型 T?。这通常用于可选配置项——未赋值即为 null,赋值后为对应值。映射表里出现的三种 IReference 用法都可以在 IDL 中找到:

IDL 声明(wslcsdk.idl) C# 投影
Windows.Foundation.IReference<UInt32> CpuCount;(SessionSettings,第 57 行) uint?
Windows.Foundation.IReference<UInt32> MemorySizeInMB;(SessionSettings,第 58 行) uint?
Windows.Foundation.IReference<Windows.Foundation.TimeSpan> Timeout;(SessionSettings,第 59 行) TimeSpan?
Windows.Foundation.IReference<ContainerNetworkingMode> NetworkingMode;(ContainerSettings,第 164 行) ContainerNetworkingMode?

典型用法是 SessionContainerSettings 的初始化:

var sessionSettings = new SessionSettings("MyApp", @"C:\WslcData")
{
    CpuCount = 4,          // uint?
    MemorySizeInMB = 4096, // uint?
    Timeout = TimeSpan.FromMinutes(5) // TimeSpan?
};

var containerSettings = new ContainerSettings("alpine:latest")
{
    Name = "hello-container",
    NetworkingMode = ContainerNetworkingMode.Bridged // ContainerNetworkingMode?
};

枚举可空类型(如 ContainerNetworkingMode?)在业务上很有价值:null 表示“使用默认网络模式”,显式赋值则覆盖默认行为。从 idl 第 99-103 行 可以看到 ContainerNetworkingMode 枚举实际只有 None = 0Bridged = 1 两个成员,C# 中按 ContainerNetworkingMode? 使用即可。

集合类型:IVector / IVectorView / IMap

WinRT 集合接口在 C# 中投影为对应的 .NET 集合接口,三者语义差异在于可变性:

WinRT 类型 C# 投影 语义
IVector<T> IList<T> 可读可写、支持按索引访问
IVectorView<T> IReadOnlyList<T> 只读快照
IMap<string, string> IDictionary<string, string> 键值对字典

IVector → IList(可变集合)

IDL 中 ContainerSettingsProcessSettings 大量使用 IVector

  • IVector<ContainerPortMapping> PortMappings;(第 170 行)
  • IVector<ContainerVolume> Volumes;(第 171 行)
  • IVector<ContainerNamedVolume> NamedVolumes;(第 172 行)
  • IVector<String> CommandLine;(ProcessSettings,第 219 行)

C# 侧使用 IList<T> 初始化:

var settings = new ProcessSettings
{
    CommandLine = new[] { "/bin/echo", "Hello from WSL Container!" },
    EnvironmentVariables = new Dictionary<string, string> { { "MY_VAR", "value" } }
};

注意 CommandLine 被投影为 IList<string>,因此数组字面量 new[] {...} 可以直接赋值(数组实现了 IList<T>)。

IVectorView → IReadOnlyList(只读快照)

IDL 中返回查询结果的 API 均使用 IVectorView<T>,表示调用方拿到的是只读快照:

  • IVectorView<ImageInfo> GetImages();(Session,第 92 行)
  • IVectorView<Component> GetMissingComponents();(WslcService,第 283 行)

C# 中对应 IReadOnlyList<T>,典型消费方式:

var missing = WslcService.GetMissingComponents(); // IReadOnlyList<Component>
if (missing.Count > 0) { /* 提示运行 wsl --install */ }

foreach (var image in session.GetImages()) // IReadOnlyList<ImageInfo>
{
    Console.WriteLine(image.Name);
}

IMap → IDictionary

ProcessSettings.EnvironmentVariables 在 IDL 中声明为 IMap<String, String>(第 220 行),C# 投影为 IDictionary<string, string>,可像普通字典一样赋值、遍历与读写:

settings.EnvironmentVariables = new Dictionary<string, string>
{
    { "TERM", "xterm-256color" },
    { "LANG", "C.UTF-8" }
};

时间与日期:TimeSpan / DateTime

WinRT 类型 C# 投影 说明
Windows.Foundation.TimeSpan System.TimeSpan 时长
Windows.Foundation.DateTime DateTimeOffset 时间点(时刻)

TimeSpan → System.TimeSpan

IDL 中 TimeSpan 出现于:

  • SessionSettings.TimeoutIReference<TimeSpan>,第 59 行);
  • Container.Stop(Signal signal, Windows.Foundation.TimeSpan timeout)(第 188 行)。

C# 中直接用 TimeSpan 表达时长,例如 end-to-end-example 中的停止容器:

container.Stop(Signal.SIGTERM, TimeSpan.FromSeconds(10));

以及等待 init 进程退出的 30 秒超时:

var completed = await Task.WhenAny(exited.Task, Task.Delay(TimeSpan.FromSeconds(30)));

DateTime → DateTimeOffset

Windows.Foundation.DateTime 投影为 DateTimeOffset(而非 DateTime),因为 WinRT 的 DateTime 自带 UTC 语义与 100ns 精度。IDL 中用于:

  • ProcessCrashInformation.TimestampWindows.Foundation.DateTime,第 32 行);
  • ImageInfo.CreatedTimestamp(第 364 行)。

C# 中可作为 DateTimeOffset 直接格式化输出,例如崩溃信息处理:

session.ProcessCrashed += info =>
    Console.WriteLine($"[{info.Timestamp:O}] {info.ProcessName} ({info.Pid}) crashed");

字节数组事件负载:com_array<uint8_t>byte[]

ProcessOutputHandler 委托在 IDL 中声明为 delegate void ProcessOutputHandler(UInt8[] data);(第 232 行)。IDL 里的 UInt8[] 属于 ABI 层的 com_array<uint8_t>(COM 风格数组),CsWinRT 将其投影为 C# 的 byte[]。这直接决定了事件回调的签名:

public delegate void ProcessOutputHandler(byte[] data);

事件负载是原始字节,而非字符串,因此输出编码需要调用方自行处理(通常是 UTF-8)。delegates-and-events 文档给出了标准写法:

using System.Text;

container.InitProcess.OutputReceived += data => Console.Write(Encoding.UTF8.GetString(data));
container.InitProcess.ErrorReceived += data => Console.Error.Write(Encoding.UTF8.GetString(data));

这也是 known-gaps 中所说的“原始原生句柄被包装,不直接暴露”的一个体现:C API 的 WslcGetProcessIOHandle 在 C# 中不直接出现,取而代之的是 OutputReceived/ErrorReceived 事件与 WinRT 流。

Uri 与 HostName:投影为同名/框架类型

WinRT 类型 C# 投影 IDL 位置
Windows.Foundation.Uri System.Uri Session.Authenticate(Windows.Foundation.Uri serverAddress, ...),第 90 行
Windows.Networking.HostName Windows.Networking.HostName(保持同名) ContainerPortMapping.WindowsAddress,第 119 行

Windows.Foundation.Uri 投影为框架类型 System.Uri,因此 Authenticate 的调用非常自然:

string token = session.Authenticate(
    new Uri("https://registry.example.com"),
    "user1",
    "password");

Windows.Networking.HostName 在 WinRT 与 .NET 中本就同名同命名空间,投影后类型不变,属于“透传”映射:

var mapping = new ContainerPortMapping(8080, 80, PortProtocol.TCP)
{
    WindowsAddress = new Windows.Networking.HostName("127.0.0.1")
};

异步操作:IAsyncActionWithProgress<T> 与进度回调

IDL 中所有耗时操作都以 Windows.Foundation.IAsyncActionWithProgress<T> 返回,例如:

  • IAsyncActionWithProgress<ImageProgress> PullImageAsync(PullImageOptions options);(第 75 行)
  • IAsyncActionWithProgress<ImageProgress> PushImageAsync(...)(第 81 行)
  • IAsyncActionWithProgress<ImageProgress> ImportImageAsync(...) / LoadImageAsync(...)(第 78、80 行)
  • IAsyncActionWithProgress<InstallProgress> InstallWithDependenciesAsync(InstallOptions options);(第 286 行)

该类型投影为可等待的 WinRT 异步操作:既可以直接 await,又暴露 Progress 事件用于接收进度。Session 文档中的标准用法:

var pullOp = session.PullImageAsync(new PullImageOptions("docker.io/library/alpine:latest"));
pullOp.Progress = (op, progress) =>
    Console.WriteLine($"Pull: {progress.Status} {progress.CurrentBytes}/{progress.TotalBytes}");
await pullOp;

ImageProgress 由 IDL 第 326-332 行定义:IdStatusImageProgressStatus 枚举,取值覆盖 Pulling/Waiting/Downloading/Verifying/Extracting/Complete)、CurrentBytesTotalBytesProgress 事件在 await 之前订阅即可捕获整个下载过程,await 之后进度已经结束。

需要特别指出:C# 投影将同步方法(PullImage/PushImage/ImportImage/LoadImage/InstallWithDependencies)与异步方法(*Async)同时保留(参见 wslcsdk.idl 第 74-82 行),UI 线程场景应优先使用 *Async 版本,避免阻塞。

事件与委托:WinRT 委托在 C# 中的落地

类型映射不仅作用于属性和方法参数,也作用于事件。IDL 中声明的四个委托(第 24、35、232、233 行)投影为普通 C# 委托,事件则按标准 C# 事件消费:

public delegate void SessionTerminationHandler(SessionTerminationReason reason);
public delegate void ProcessCrashHandler(ProcessCrashInformation information);
public delegate void ProcessOutputHandler(byte[] data);
public delegate void ProcessExitHandler(int exitCode);

典型订阅示例(来自 delegates-and-events):

session.Terminated += reason => Console.WriteLine($"Session ended: {reason}");
session.ProcessCrashed += info => Console.WriteLine($"Process crashed: {info.ProcessName} ({info.Pid})");
container.InitProcess.OutputReceived += data => Console.Write(Encoding.UTF8.GetString(data));
container.InitProcess.ErrorReceived += data => Console.Error.Write(Encoding.UTF8.GetString(data));
container.InitProcess.Exited += code => Console.WriteLine($"Init exited: {code}");

注意 ProcessExitHandler 的负载是 int(对应 IDL 的 Int32),而 ProcessOutputHandler 的负载是 byte[](对应 com_array<uint8_t>),两者不要混淆。

投影边界:不在 C# 中出现的类型

映射表之外,理解哪些 C API 能力没有被投影同样重要,这能避免写出无法编译或语义错误的代码。Known Gaps 完整列出了四类差异:

C API 功能 C# 状态
WslcImportSessionImage / WslcLoadSessionImage 接收原始 HANDLE + 字节数的重载 不投影;C# 只暴露基于文件路径的 ImportImage/ImportImageAsync/LoadImage/LoadImageAsync
原始原生句柄(WslcGetSessionTerminationEventWslcGetProcessExitEventWslcGetProcessIOHandle 包装而非暴露;改用 C# 事件与 WinRT 流
WslcProcessCallbacks 注册面 包装为事件;使用 OutputReceivedErrorReceivedExited
WslcContainerStartFlags 不直接暴露Container.Start() 在 init 进程采用 ProcessOutputMode.EventProcessOutputMode.Stream 时自动设置 ATTACH 标志

例如“事件负载为 byte[]”的映射(第 5 节)正是“句柄被包装”这一缺口的 C# 侧表象。

实战要点小结

  1. 可空配置项CpuCountMemorySizeInMBTimeoutNetworkingMode 在 C# 中都是可空类型,未赋值为 null,判断“是否显式设置”直接用 hasValue 语义,不要与 0 混淆。
  2. 只读快照GetImages()GetMissingComponents() 返回 IReadOnlyList<T>,适合遍历与 LINQ 查询,但不适合原地修改。
  3. 字节 vs 字符串:所有进程输出事件负载均为 byte[],务必按已知编码(通常 UTF-8)解码。
  4. 异步进度:在 await 之前订阅 Progress 才能观察到完整进度序列。
  5. 类型翻译法:遇到任何 C# 签名不确定时,回查 wslcsdk.idl 中对应成员的 WinRT 声明,再按本文映射表翻译即可。

将以上映射规则套入 End-to-End Example 的完整生命周期示例(检查前置组件 → 打印版本 → 创建会话 → 拉取镜像 → 配置 init 进程 → 创建并启动容器 → 等待退出 → 清理),即可写出类型正确、进度可控、事件完整的 C# WSL 容器应用。

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

项目优选

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