首页
/ WSL 官方 C SDK 的 InstallProgress 数据类:依赖安装进度回传机制详解

WSL 官方 C SDK 的 InstallProgress 数据类:依赖安装进度回传机制详解

2026-09-09 23:05:35作者:齐添朝

导读

InstallProgress 是 WSL(Windows Subsystem for Linux)官方 C# SDK(Microsoft.WSL.Containers)中专门用于依赖组件安装进度回传的数据类。当应用调用 WslcService.InstallWithDependenciesAsync 安装 WSL 所依赖的"虚拟机平台"与"WSL 包"等组件时,SDK 会以 InstallProgress 为载荷(payload),通过异步进度通道把当前组件、已完成步骤数与总步骤数实时回报给调用方。读完本文,你将掌握 InstallProgress 的属性语义、它在 WslcService 安装流程中的底层调用链、如何用 Progress 回调编写可复制的进度条代码,以及它对 UI 展示和用户引导的实际价值。

一、什么是 InstallProgress:面向依赖安装的进度载荷

在 WSL 的 C# SDK 文档中,InstallProgress 被明确定义为 "Progress payload for dependency installation"(依赖安装的进度载荷)。它只服务于依赖组件安装场景,与镜像拉取/导入/推送场景的 ImageProgress 分工明确:前者汇报"装系统组件装到哪一步了",后者汇报"容器镜像数据传输了多少字节"。

InstallProgress 是一个 sealed 类,实例由 SDK 内部创建并只读暴露,调用方只能读取属性而不能修改:

public sealed class InstallProgress
{
    public Component Component { get; }
    public uint Progress { get; }
    public uint Total { get; }
}

三个属性分别回答三个问题:

属性 类型 含义
Component Component(枚举) 当前正在安装的依赖组件,见 Component 枚举
Progress uint 当前组件已完成/已报告的进度步骤数
Total uint 当前组件总进度步骤数

其中 Component 枚举在仓库中的定义如下(component.md):

public enum Component
{
    VirtualMachinePlatform = 1,
    WslPackage = 2,
    SdkNeedsUpdate = 4
}

从枚举取值可以看出它本质是位标志(bit flags)语义:VirtualMachinePlatform(虚拟机平台,WSL2 运行所需的 Hyper-V 基础组件)、WslPackage(WSL 应用包本体)、SdkNeedsUpdate(SDK 需要更新,属于检测结果而非可安装的组件)。

二、InstallProgress 在安装流程中的位置:与 WslcService 的关系

InstallProgress 不是独立使用的类,它是服务级 API WslcService 的进度回调载荷。WslcService 是 WSL C# SDK 中"服务级操作"的静态入口,其中与安装相关的方法有:

public static class WslcService
{
    public static IReadOnlyList<Component> GetMissingComponents();
    public static ServiceVersion GetVersion();
    public static void InstallWithDependencies();
    public static IAsyncActionWithProgress<InstallProgress> InstallWithDependenciesAsync();
}

典型使用方式是三步曲:先用 GetMissingComponents() 探测缺哪些组件,再调用 InstallWithDependenciesAsync() 补齐,最后在进度回调中消费 InstallProgress

IReadOnlyList<Component> missing = WslcService.GetMissingComponents();
if (missing.Count == 0)
{
    Console.WriteLine("All required components are installed.");
}
else
{
    Console.WriteLine($"Missing: {string.Join(", ", missing)}");
}

var install = WslcService.InstallWithDependenciesAsync();
install.Progress = (op, progress) =>
    Console.WriteLine($"install: {progress.Component} {progress.Progress}/{progress.Total}");
await install;

这一组合正是 InstallProgress 存在的意义:InstallWithDependenciesAsync 返回 IAsyncActionWithProgress<InstallProgress>,每一次进度上报都会携带一个只读的 InstallProgress 实例。

三、属性与语义详解:Component、Progress、Total

3.1 Component:当前安装的组件

Component 表明本次进度事件对应哪一个依赖组件。当一次安装涉及多个组件(例如同时缺 VirtualMachinePlatformWslPackage)时,SDK 会按顺序逐个安装,每个组件都有自己的进度区间,因此每次回调中 Component 都可能切换,调用方应据此区分进度条归属于哪个阶段。

3.2 Progress / Total:完成步骤与总步骤

ProgressTotal 是当前组件的"步骤计数"。例如 Progress = 2, Total = 10 表示当前组件已完成 2/10 步,进度百分比可计算为 (double)progress.Progress / progress.Total。由于二者均为 uint(32 位无符号整数),直接做除法前建议先转成 double 再乘 100,避免整数截断。

3.3 文档给出的最小可运行示例

原文档提供了一个简洁的打印示例,可直接放入任何 C# 工程中使用:

void PrintInstallProgress(InstallProgress progress) =>
    Console.WriteLine($"{progress.Component}: {progress.Progress}/{progress.Total}");

结合 Component 的枚举名,输出形如 WslPackage: 4/10,便于日志审计与调试。

四、底层实现剖析:从 IDL 到原生回调

InstallProgress 的"文档 C# 外观"背后,是仓库中完整的 WinRT 实现链。下面按层拆解其真实构造过程。

4.1 IDL 定义:SDK 的类型契约

WinRT 运行时类的最终契约定义在 wslcsdk.idl

runtimeclass InstallProgress
{
    Component Component { get; };
    UInt32 Progress { get; };
    UInt32 Total { get; };
};

可以看到与 C# 文档一一对应:三个只读属性,类型为 ComponentUInt32。该 IDL 还同时定义了 Component 枚举(取值 1/2/4)、InstallOptionsComponents + Repair)以及 WslcService 的静态方法签名。

4.2 WinRT 实现类:只读不可变封装

实现位于 InstallProgress.hInstallProgress.cpp。它继承 InstallProgressT<InstallProgress>,通过构造函数注入三个值并存入私有成员,属性 getter 只读返回:

InstallProgress::InstallProgress(winrt::Microsoft::WSL::Containers::Component component, uint32_t progress, uint32_t total) :
    m_component(component), m_progress(progress), m_total(total)
{
}

也就是说,InstallProgress不可变的快照对象——每次进度上报都会由 SDK 新建一个实例,调用方持有的旧实例不会受后续进度影响。这一设计对 UI 线程更新非常友好:你可以安全地把每个 InstallProgress 实例缓存或传递,而无需担心数据被后续事件改写。

4.3 关键调用链:原生回调包装成 WinRT 进度

真正把原生安装进度"翻译"成 InstallProgress 的逻辑在 WslcService.cpp

void CALLBACK InstallProgressCallback(WslcComponentFlags component, uint32_t progressSteps, uint32_t totalSteps, PVOID context) noexcept
{
    try
    {
        auto installProgress = winrt::make<implementation::InstallProgress>(
            static_cast<winrt::Microsoft::WSL::Containers::Component>(component), progressSteps, totalSteps);
        ProgressCallbackHelper<decltype(installProgress)>::ReportProgress(context, installProgress);
    }
    CATCH_LOG();
}
  • WslcComponentFlags 原生位标志被 static_cast 转换为 WinRT 的 Component 枚举;
  • progressSteps / totalSteps 直接映射为 Progress / Total
  • 新构造的 InstallProgress 通过 ProgressCallbackHelper::ReportProgress 上报给调用方挂接的 Progress 委托。

该回调最终由 WslcInstallWithDependencies(原生 C API)触发,InstallWithDependenciesAsync 的整体流程如下:

IAsyncActionWithProgress<winrt::Microsoft::WSL::Containers::InstallProgress> WslcService::InstallWithDependenciesAsync(
    winrt::Microsoft::WSL::Containers::InstallOptions options)
{
    auto components = GetComponentsForInstall(options);
    auto wslcOptions = GetOptionsForInstall(options);

    co_await winrt::resume_background();

    auto context = ProgressCallbackHelper<winrt::Microsoft::WSL::Containers::InstallProgress>{co_await winrt::get_progress_token()};
    winrt::check_hresult(WslcInstallWithDependencies(components, wslcOptions, InstallProgressCallback, &context));
}

值得注意的实现细节:

  • co_await winrt::resume_background() 表明安装工作在线程池后台执行,不阻塞 UI;
  • co_await winrt::get_progress_token() 负责取回调用方挂接的 Progress 委托;
  • winrt::check_hresult 会抛出失败 HRESULT,例如组件列表传入 SdkNeedsUpdate 时会得到 WSLC_E_SDK_UPDATE_NEEDED(见下文的测试用例)。

五、如何消费进度:实用 C# 代码模板

以下模板可直接用于真实应用,包含缺失检测、带进度安装、结果校验三段完整逻辑:

using Microsoft.WSL.Containers;

// 1. 先探测缺失组件
var missing = WslcService.GetMissingComponents();
if (missing.Count == 0)
{
    Console.WriteLine("All required components are installed.");
    return;
}
Console.WriteLine($"Missing: {string.Join(", ", missing)}");

// 2. 异步安装并消费 InstallProgress
var install = WslcService.InstallWithDependenciesAsync();
install.Progress = (op, progress) =>
{
    double percent = progress.Total == 0
        ? 0
        : (double)progress.Progress / progress.Total * 100;
    Console.WriteLine(
        $"[{progress.Component}] {progress.Progress}/{progress.Total} ({percent:F1}%)");
};

await install;

// 3. 安装完成后复检
var after = WslcService.GetMissingComponents();
Console.WriteLine(after.Count == 0 ? "Install complete." : $"Still missing: {string.Join(", ", after)}");

对 GUI 应用(如 WinUI / WPF / WinForms),推荐在 Progress 回调中用 DispatcherSynchronizationContext 将 UI 更新切回 UI 线程,并据 progress.Component 切换进度条的分段标签,例如:

install.Progress = async (op, progress) =>
{
    await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        ProgressText.Text = $"{progress.Component}: {progress.Progress}/{progress.Total}";
        ProgressBar.Maximum = progress.Total;
        ProgressBar.Value = progress.Progress;
    });
};

六、测试与边界行为:仓库给出的行为证据

仓库测试 WslcSdkWinRTTests.cppInstallProgress 相关的服务行为提供了可验证的证据:

WSLC_TEST_METHOD(InstallWithDependencies)
{
    // Pass null options to auto-detect and install any missing components (same behavior as the old no-arg call).
    WSLCSDK::WslcService::InstallWithDependenciesAsync(nullptr).get();
    VERIFY_ARE_EQUAL(WSLCSDK::WslcService::GetMissingComponents().Size(), 0u);
}

据此可以总结出几条重要的边界行为:

  1. InstallOptionsnull 是合法的:SDK 会自动探测并安装所有缺失组件,与旧版无参调用行为一致;
  2. InstallOptions 默认值:默认构造的 InstallOptionsComponentsnullRepairfalse(对应 InstallOptions.hm_repair = false 的初始值);
  3. Repair = true,SDK 会转为修复模式,把 WSLC_INSTALL_OPTION_REPAIR 选项透传给原生层(见 WslcService.cpp);
  4. SdkNeedsUpdate 放进安装组件列表会抛异常InstallWithDependenciesAsync 会抛出 WSLC_E_SDK_UPDATE_NEEDED,因为 SDK 更新不应由安装流程直接触发:
    VERIFY_THROWS_HR(WSLCSDK::WslcService::InstallWithDependenciesAsync(options).get(), WSLC_E_SDK_UPDATE_NEEDED);
    
  5. 安装前组件检测是完备的GetMissingComponentsWslcService.cpp 中逐位检查原生标志,把 WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORMWSLC_COMPONENT_FLAG_WSL_PACKAGEWSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE 分别映射为对应枚举值。

七、常见问题与使用建议

Q1:InstallProgressImageProgress 有什么区别? InstallProgress 面向"系统依赖组件安装"(虚拟机平台、WSL 包),步骤以组件内步数计;ImageProgress 面向"容器镜像 pull/import/load/push",以字节与 ImageProgressStatus 状态表达。二者用途不同,切勿混用。

Q2:为什么我的进度回调里 Component 会变化? 当一次安装需要多个组件时,SDK 按组件逐个安装并上报,Component 会随阶段切换。处理时建议以 progress.Component 为 key 维护每个组件的独立进度。

Q3:Progress == Total 是否代表安装完成? 代表"当前组件"的步骤走完,但整个安装是否结束应以 awaitIAsyncActionWithProgress 完成为准;若存在多个组件,还需等待后续组件事件。

Q4:可以在 UI 线程直接调用 InstallWithDependenciesAsync 吗? 可以安全调用——SDK 内部会 co_await resume_background() 切到后台执行,但进度回调的线程上下文需要自行处理 UI 切换(见第五节模板)。

Q5:想静默安装不显示进度? 直接调用同步版本 WslcService.InstallWithDependencies()(底层传 nullptr 回调即可),或对异步版本不挂 Progress 委托。

延伸阅读

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
927
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.89 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
602
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
396
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
526