WSLC SDK 安装进度回调 WslcInstallCallback 详解:WSL 组件安装的进度上报协议
【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL
WslcInstallCallback 是 Windows Subsystem for Linux(WSL)WSLC SDK(C API)中用于上报组件安装进度的回调类型,由WslcInstallWithDependencies在安装虚拟机平台(Virtual Machine Platform)、WSL 运行时包等系统组件时反复调用,是开发者构建安装引导界面、进度条与失败恢复逻辑的核心接口。读完本文,你将掌握该回调的完整签名与参数语义、各组件进度模型差异,以及如何在 C/C++ 与 WinRT 应用中正确注册并消费安装进度事件。
回调类型定位:WSLC 安装流程中的进度通道
在 WSLC(WSL Containers)SDK 的 C API 体系中,组件安装由 WslcInstallWithDependencies 驱动,而本回调类型正是该函数接受的两个可选参数之一(另一个是context)。SDK 的头文件位于 wslcsdk.h,与它并列的还有会话崩溃转储、标准 I/O、进程退出、容器镜像下载等回调,完整清单见 Callback Types 索引。
typedef __callback void(CALLBACK* WslcInstallCallback)( _In_ WslcComponentFlags component, _In_ uint32_t progressSteps, _In_ uint32_t totalSteps, _In_opt_ PVOID context);该回调由 SDK 内部(安装逻辑)调用,由应用程序实现并提供给 SDK,属于典型的“回调(callback)”方向:安装引擎每完成一个进度步进,就通过该函数通知调用方。
参数详解
| Parameter | Type | 含义 |
|---|---|---|
component | WslcComponentFlags | 当前进度事件所属的组件,标识正在安装的子系统组件 |
progressSteps | uint32_t | 当前组件已完成的进度步数(从 0 开始递增) |
totalSteps | uint32_t | 当前组件安装所需的总步数,用于计算完成比例progressSteps / totalSteps |
context | PVOID | 可选。调用方自定义上下文指针,原样透传给回调,通常用于携带窗口句柄、进度条对象或状态结构体 |
__callback与CALLBACK均展开为__stdcall调用约定,_In_/_In_opt_是 SAL 注解,分别表示“必填输入”与“可选输入”。回调本身位于进程内同步执行(详见下文实现原理),因此在回调内部应避免执行耗时操作或可能阻塞安装流程的调用。
component 参数:安装的是哪个组件
component的类型为 WslcComponentFlags,在 wslcsdk.h 中定义为位标志枚举(支持按位或组合),其取值决定了回调语义:
| 标志 | 值 | 含义 |
|---|---|---|
WSLC_COMPONENT_FLAG_NONE | 0 | 无组件 |
WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORM | 1 | “虚拟机平台”可选功能提供的服务(其他可选功能也可能提供),安装此组件需要重启系统 |
WSLC_COMPONENT_FLAG_WSL_PACKAGE | 2 | 提供 WSLC 支持能力的 WSL 运行时包 |
WSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE | 4 | 表示 WSLC SDK 自身需要更新(该标志不会出现在安装回调中,见下文) |
SDK 为该枚举定义了DEFINE_ENUM_FLAG_OPERATORS(WslcComponentFlags),因此可以直接使用WI_IsFlagSet、WI_SetFlag等 WIL 标志位辅助宏对返回值进行测试。回调中应当以component区分当前进度属于哪个子流程,从而在 UI 上展示不同的文案(例如“正在启用虚拟机平台”“正在安装 WSL 运行时包”)。
进度模型:不同组件使用不同的步进刻度
progressSteps与totalSteps的组合构成一个简单的“已完成/总量”进度模型,但不同组件的刻度并不一致,这一点从 wslcsdk.cpp 的实现中可以精确确认:
- 虚拟机平台(
WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORM):安装开始前回调一次(component, 0, 1, context),安装完成后回调一次(component, 1, 1, context)。即该组件只有两个进度点:0/1 表示进行中,1/1 表示完成,UI 上可显示为不确定进度条或 0%/100% 两态。 - WSL 运行时包(
WSLC_COMPONENT_FLAG_WSL_PACKAGE):通过 Windows Update 流程安装,SDK 将内部更新进度归一化为0到100的刻度,即回调序列为(component, 0, 100)起步、(component, 100, 100)收尾,中间按更新引擎上报的实际进度递增。
因此,回调实现中应始终按totalSteps动态计算百分比(progressSteps * 100 / totalSteps),而不能硬编码 0~100 或 0/1 假设——两种组件并存时进度模型天然不同。
context 参数:携带调用方状态
context是调用WslcInstallWithDependencies时传入的任意指针,SDK 在每次回调时原样回传,不会解释其内容。典型用法是传入指向进度条控件、窗口句柄或状态结构的指针,避免使用全局变量:
typedef struct InstallUiState { HWND hwndProgress; int lastPercent; } InstallUiState; void CALLBACK OnInstallProgress( WslcComponentFlags component, uint32_t progressSteps, uint32_t totalSteps, PVOID context) { InstallUiState* state = (InstallUiState*)context; int percent = (int)((uint64_t)progressSteps * 100 / totalSteps); if (percent != state->lastPercent) { state->lastPercent = percent; // 更新 UI:SetProgress(percent),并根据 component 切换提示文案 } }如果无需携带状态,传NULL并在回调中忽略该参数即可(UNREFERENCED_PARAMETER(context);)。
与 WslcInstallWithDependencies 的完整配合示例
SDK 文档在 wslcinstallwithdependencies.md 中给出了可直接编译运行的完整示例,完整继承如下:
void CALLBACK OnInstallProgress( WslcComponentFlags component, uint32_t progressSteps, uint32_t totalSteps, PVOID context) { UNREFERENCED_PARAMETER(context); printf("component=%u %u/%u\n", (unsigned)component, progressSteps, totalSteps); } HRESULT hr = WslcInstallWithDependencies(OnInstallProgress, NULL);需要说明的是,SDK 头文件中该函数的完整声明还包含components与options两个前置参数(见 wslcsdk.h):
STDAPI WslcInstallWithDependencies( _In_ WslcComponentFlags components, _In_ WslcInstallOptions options, _In_opt_ WslcInstallCallback progressCallback, _In_opt_ PVOID context);其中options取 WslcInstallOptions 枚举:WSLC_INSTALL_OPTION_NONE = 0为普通安装,WSLC_INSTALL_OPTION_REPAIR = 1允许重新安装已存在的组件(修复模式)。
安装前的必要准备
回调只会针对本次调用实际安装的组件触发。因此,标准的调用流程是先用WslcGetMissingComponents查询缺失组件,再据此决定安装参数,参见 wslcgetmissingcomponents.md 与 端到端示例:
WslcComponentFlags missing = WSLC_COMPONENT_FLAG_NONE; HRESULT hr = WslcGetMissingComponents(&missing); if (FAILED(hr) || missing == WSLC_COMPONENT_FLAG_NONE) { return hr; // 所有组件已就绪,无需安装 } hr = WslcInstallWithDependencies(missing, WSLC_INSTALL_OPTION_NONE, OnInstallProgress, &myState);不传回调的场景
若调用方对进度不敏感(例如命令行工具静默安装),可同时传nullptr与nullptr,SDK 会跳过回调分支,安装照常进行;WinRT 桥接层的同步版本正是如此(见下文)。
源码级实现原理:回调在何处、以何种方式被触发
在 wslcsdk.cpp 的WslcInstallWithDependencies实现中,回调的触发遵循以下逻辑,可作为理解进度语义的权威依据:
- 参数校验:对
components中未知的位返回E_INVALIDARG;若包含WSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE,直接返回WSLC_E_SDK_UPDATE_NEEDED——SDK 无法自行更新正在使用的自身,调用方应升级 SDK 后重试。 - 空组件短路:
components == WSLC_COMPONENT_FLAG_NONE时立即返回S_OK,不触发任何回调,也不要求提升权限。 - 权限检查:安装组件需要管理员权限,若当前线程令牌未提升且非 LocalSystem,返回
ERROR_ELEVATION_REQUIRED。因此回调触发前应先处理提权(UAC)。 - 虚拟机平台阶段:安装前后各触发一次回调(
0/1与1/1),底层通过WslInstall::InstallOptionalComponent调用 DISM 启用c_optionalFeatureNameVmp可选功能;若返回ERROR_SUCCESS_REBOOT_REQUIRED,最终返回值为HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED),调用方可据此提示用户重启。 - WSL 运行时包阶段:构造 lambda 将内部进度映射为
0~100后回调,底层走WindowsUpdateContext::RunUpdateFlow(普通安装用EnsureProductRegistration,修复模式用ResetProductRegistration)驱动的 Windows Update 流程;若更新计数为 0(预览期包未发布等情况),回退到 GitHub 发布端点拉取预发布包,UpdatePackage(true, true, false)后回调 0 与 100 收尾。
可以看出,回调均发生在发起调用的线程上、同步执行。UI 应用若在主线程调用,应在回调内尽快返回(只做进度记录/消息投递),避免阻塞安装主流程。
WinRT 桥接层:托管/现代应用如何复用同一回调
WSLC SDK 的 WinRT 投影在 WslcService.cpp 中封装了该回调:静态InstallProgressCallback把原生WslcComponentFlags转换为winrt::Microsoft::WSL::Containers::Component,与progressSteps/totalSteps一起构造InstallProgress对象,再通过ProgressCallbackHelper::ReportProgress投递给IAsyncActionWithProgress<InstallProgress>的进度令牌。异步版本InstallWithDependenciesAsync在co_await winrt::resume_background()之后注册回调,同步版本则传nullptr回调(WslcService.cpp)。这意味着 C#/WinRT 开发者可以在不接触原生指针的情况下获得等价的进度事件流。
测试验证:回调协议的边界行为
仓库测试 WslcSdkTests.cpp 覆盖了该回调参与的几个关键契约,可作为集成调试时的行为参考:
InstallWithDependencies_NoComponents_Succeeds:传WSLC_COMPONENT_FLAG_NONE必须立即返回S_OK,且无需提权、不触发回调。InstallWithDependencies_SdkNeedsUpdate_ReturnsError:传WSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE必须返回WSLC_E_SDK_UPDATE_NEEDED。InstallWithDependencies_WslPackage_GhFallback404:通过注册表 URL 覆盖(Software\Microsoft\Windows\CurrentVersion\Lxss下的 GitHub 地址覆盖项)将 WSL 包回退下载端点替换为本地返回 404 的假服务器,验证下载失败时WslcInstallWithDependencies向上层暴露HTTP_E_STATUS_NOT_FOUND——即回调可能只推进到中途(如progressSteps < totalSteps)后安装整体失败,UI 层必须同时处理回调中断与 HRESULT 失败。
实践要点与注意事项
- 动态计算百分比:以
totalSteps为分母,适配不同组件的刻度差异(VMP 为 0/1,WSL 包为 0/100)。 - 区分组件与阶段:用
component切换 UI 文案;同一组件可能多次回调,用progressSteps == 0与progressSteps == totalSteps判断开始/结束。 - 保持回调轻量:同步回调中不要做 UI 重绘、磁盘 IO 或网络请求,仅记录进度或向消息循环投递更新。
- 处理提权与重启:调用前确保进程已提升;返回值可能为
ERROR_SUCCESS_REBOOT_REQUIRED,需提示用户重启完成虚拟机平台启用。 - 先查再装:结合
WslcGetMissingComponents避免对已安装组件的重复安装;修复场景才使用WSLC_INSTALL_OPTION_REPAIR。 - 不能安装 SDK 自身:若查询结果显示
WSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE,请更新调用方所使用的 SDK 版本,而不是将其传入安装流程。
围绕该回调的完整 API 族(组件标志、安装选项、缺失组件查询、端到端流程)均可从 C API 参考索引 进入,源码与测试路径为 wslcsdk.h、wslcsdk.cpp 与 WslcSdkTests.cpp,便于进一步深入。
【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考