WSL 容器镜像进度回调 WslcContainerImageProgressCallback:从 SDK 签名到底层实现全解析
导读
WslcContainerImageProgressCallback 是 WSL(Windows Subsystem for Linux)容器 SDK(WSLC)中用于接收容器镜像拉取(Pull)/推送(Push)进度通知的回调函数类型。在 WslcPullSessionImage、WslcPushSessionImage 等镜像管理 API 中,调用方通过该回调实时获取每个镜像层的下载/解压状态与字节进度,从而驱动进度条、日志等 UI 展示。读完本文,你将掌握该回调的类型签名、进度消息结构与状态枚举的完整定义,学会在 C 代码中正确挂接回调并解析进度数据,并了解 SDK 底层如何把引擎字符串状态转换为枚举、如何通过 IWSLCCompatProgressCallback 适配层将进度转发到你的函数。
一、回调类型签名与参数说明
该回调是 SDK 公开 C API 的一部分,完整定义位于 wslcsdk.h,同时也是官方 API 参考文档 wslccontainerimageprogresscallback.md 的主题:
typedef HRESULT(CALLBACK* WslcContainerImageProgressCallback)(const WslcImageProgressMessage* progress, PVOID context);
| 参数 | 类型 | 说明 |
|---|---|---|
progress |
const WslcImageProgressMessage* |
单次进度事件的完整消息,包含层 ID、状态与字节进度,见下文"进度消息结构" |
context |
PVOID |
调用方自定义上下文指针,原样透传,通常指向一个用户状态结构 |
- 返回值为
HRESULT。在 SDK 底层实现中,回调的返回值会被直接作为进度分发的返回值返回给上层(详见"五、底层实现"),因此建议始终返回S_OK,除非你确实需要中断进度分发。 - 参数
context与镜像操作选项结构中的progressCallbackContext字段一一对应,SDK 不会解析或修改它,仅做透传,可用于携带回调状态(如累计下载量、是否已收到完成事件等)。
二、进度消息结构 WslcImageProgressMessage
回调收到的 progress 参数指向如下结构(官方文档见 wslcimageprogressmessage.md,源码定义见 wslcsdk.h):
typedef struct WslcImageProgressMessage
{
_Out_ PCSTR id; // layer ID or digest
_Out_ WslcImageProgressStatus status; // "Downloading", "Extracting", etc.
_Out_ WslcImageProgressDetail detail;
} WslcImageProgressMessage;
| 字段 | 类型 | 含义 |
|---|---|---|
id |
PCSTR |
镜像层 ID(layer ID)或摘要(digest),用于区分不同层的事件 |
status |
WslcImageProgressStatus |
该层当前所处的进度阶段(枚举值,见下节) |
detail |
WslcImageProgressDetail |
字节级进度详情 |
其中 WslcImageProgressDetail 是一个仅含两个 uint64_t 字段的小结构(wslcsdk.h):
typedef struct WslcImageProgressDetail
{
_Out_ uint64_t currentBytes; // bytes downloaded so far
_Out_ uint64_t totalBytes; // total bytes expected
} WslcImageProgressDetail;
currentBytes:当前已传输(下载/上传)的字节数;totalBytes:预期总字节数。注意某些阶段(如 Waiting)可能尚未获知总量,渲染进度条时应处理totalBytes == 0的边界情况(可显示为"传输中/未知总量")。
三、进度状态枚举 WslcImageProgressStatus
status 字段的取值来自枚举 WslcImageProgressStatus,完整定义见 wslcsdk.h:
typedef enum WslcImageProgressStatus
{
WSLC_IMAGE_PROGRESS_STATUS_UNKNOWN = 0, // 未知/未识别状态
WSLC_IMAGE_PROGRESS_STATUS_PULLING = 1, // "Pulling fs layer"
WSLC_IMAGE_PROGRESS_STATUS_WAITING = 2, // "Waiting"
WSLC_IMAGE_PROGRESS_STATUS_DOWNLOADING = 3, // "Downloading"
WSLC_IMAGE_PROGRESS_STATUS_VERIFYING = 4, // "Verifying Checksum"
WSLC_IMAGE_PROGRESS_STATUS_EXTRACTING = 5, // "Extracting"
WSLC_IMAGE_PROGRESS_STATUS_COMPLETE = 6 // "Pull complete"
} WlcImageProgressStatus;
从枚举注释可以看出,这些枚举值与镜像引擎(Docker/Moby 风格)输出的状态字符串一一对应,一个典型镜像层的生命周期大致为:
Pulling fs layer → Waiting → Downloading → Verifying Checksum → Extracting → Pull complete
在实际渲染进度 UI 时,WSLC_IMAGE_PROGRESS_STATUS_COMPLETE 是判断某一层完成的可靠信号;而 WSLC_IMAGE_PROGRESS_STATUS_UNKNOWN 表示 SDK 未能将引擎字符串映射为已知状态(见第五节"状态映射"),业务代码可将其当作通用进度处理。
四、在镜像操作选项中使用回调
该回调并非独立注册,而是作为镜像操作选项结构的一个字段传入。以拉取镜像为例,选项结构 WslcPullImageOptions(wslcsdk.h)定义如下:
typedef struct WslcPullImageOptions
{
_In_z_ PCSTR uri;
WslcContainerImageProgressCallback progressCallback;
PVOID progressCallbackContext;
_In_opt_z_ PCSTR registryAuth;
} WslcPullImageOptions;
uri:要拉取的镜像引用,如docker.io/library/alpine:latest;progressCallback:进度回调函数指针,可置NULL表示不接收进度;progressCallbackContext:透传给回调的自定义上下文;registryAuth:可选,私有仓库的认证头(registry auth header)。
推送镜像的选项结构 WslcPushImageOptions 同样包含 progressCallback 与 progressCallbackContext 两个字段。SDK 侧通过 ProgressCallback::CreateIf(options) 判断:仅当选项非空且 progressCallback 非 NULL 时才创建内部进度转发对象(见 ProgressCallback.h)。
五、完整示例:拉取镜像并打印进度
官方 API 文档 wslcpullsessionimage.md 给出了可直接编译的完整示例,回调写法如下:
HRESULT CALLBACK OnImageProgress(const WslcImageProgressMessage* progress, PVOID context)
{
UNREFERENCED_PARAMETER(context);
printf("%s %llu/%llu\n",
progress->id,
(unsigned long long)progress->detail.currentBytes,
(unsigned long long)progress->detail.totalBytes);
return S_OK;
}
WslcPullImageOptions pullOptions = { 0 };
pullOptions.uri = "docker.io/library/alpine:latest";
pullOptions.progressCallback = OnImageProgress;
pullOptions.progressCallbackContext = NULL;
pullOptions.registryAuth = NULL;
HRESULT hr = WslcPullSessionImage(session, &pullOptions, NULL);
在此基础上,可以进一步利用 progress->status 枚举做状态感知的 UI:
HRESULT CALLBACK OnImageProgress(const WslcImageProgressMessage* progress, PVOID context)
{
const char* stage = "?";
switch (progress->status)
{
case WSLC_IMAGE_PROGRESS_STATUS_PULLING: stage = "pulling layer"; break;
case WSLC_IMAGE_PROGRESS_STATUS_WAITING: stage = "waiting"; break;
case WSLC_IMAGE_PROGRESS_STATUS_DOWNLOADING: stage = "downloading"; break;
case WSLC_IMAGE_PROGRESS_STATUS_VERIFYING: stage = "verifying"; break;
case WSLC_IMAGE_PROGRESS_STATUS_EXTRACTING: stage = "extracting"; break;
case WSLC_IMAGE_PROGRESS_STATUS_COMPLETE: stage = "complete"; break;
default: stage = "unknown"; break;
}
printf("[%s] %s: %llu / %llu bytes\n",
progress->id, stage,
(unsigned long long)progress->detail.currentBytes,
(unsigned long long)progress->detail.totalBytes);
return S_OK;
}
六、底层实现:SDK 如何把进度转发给你的回调
要理解该回调的真实调用时机与数据来源,需要阅读 SDK 的进度适配层实现 ProgressCallback.cpp:
-
COM 适配层:
ProgressCallback类实现了内部接口IWSLCCompatProgressCallback(见 ProgressCallback.h),构造函数保存用户回调m_callback与上下文m_context,SDK 镜像操作(如拉取/推送)内部通过该 COM 接口统一接收引擎进度事件。 -
数据组装:
OnProgress(LPCSTR Status, LPCSTR Id, ULONGLONG Current, ULONGLONG Total)将引擎上报的原始参数组装为WslcImageProgressMessage:message.id = Id(层 ID 或 digest);message.status = ConvertStatus(Status)(引擎字符串 → 枚举);message.detail.currentBytes = Current、message.detail.totalBytes = Total;
然后调用用户回调:
return m_callback(&message, m_context);(见 ProgressCallback.cpp)。 -
状态字符串 → 枚举映射:
ConvertStatus通过精确字符串匹配与前缀匹配把引擎输出(如"Pulling from "、"Pulling fs layer"、"Waiting"、"Downloading"、"Verifying Checksum"、"Extracting"、"Pull complete"等)转换为上文的枚举值(ProgressCallback.cpp)。无法识别时记录调试日志并返回WSLC_IMAGE_PROGRESS_STATUS_UNKNOWN。源码注释中也明确提到该字符串映射方案"较为脆弱"(fragile),且引擎字符串本身未做本地化,因此业务代码不应假设所有引擎输出都能被枚举覆盖,对UNKNOWN状态要有兜底渲染。
七、WinRT 侧封装与测试验证
- WinRT 封装:SDK 同时提供 WinRT 版本
Microsoft.WSL.Containers.ImageProgress,其属性Id、Status、CurrentBytes、TotalBytes与 C 结构的id、status、detail.currentBytes、detail.totalBytes一一对应(见 ImageProgress.h),C#/WinRT 调用方可直接绑定到 XAML 进度控件。 - 测试验证:SDK 单元测试
ImageProgressCallback(WslcSdkTests.cpp)实测了回调行为:测试在本地 registry 上先推送再拉取hello-world:latest镜像,回调中记录invoked = true,并断言回调确实被调用(VERIFY_IS_TRUE(ctx.invoked)),且在拉取场景下收到过非UNKNOWN的已知状态(VERIFY_IS_TRUE(ctx.sawKnownStatus))。这从测试侧印证了:只要在选项结构中设置了progressCallback,拉取/推送镜像过程中该回调就会被可靠触发,且context指针可用于在多次回调之间携带状态(测试中的ProgressContext即为一例)。
八、使用注意事项小结
- 回调一定是在镜像操作(拉取/推送)过程中被同步触发,实现应保持轻量,避免在回调内做耗时操作阻塞操作流程。
- 多个层会各自上报进度:
progress->id区分不同层,同一层会经历 Pulling → Waiting → Downloading → Verifying → Extracting → Complete 多个阶段事件,UI 应按id聚合展示。 - 处理
totalBytes未知的情况:Waiting、Verifying 等阶段可能尚无总量信息,进度条需有"不确定进度"形态。 - 对
WSLC_IMAGE_PROGRESS_STATUS_UNKNOWN兜底:引擎字符串与枚举的映射由 ProgressCallback.cpp 中的固定规则决定,未来引擎输出变化时可能落入 UNKNOWN。 context是唯一允许携带用户状态的地方:与progressCallbackContext配套使用,SDK 不解析、不释放该指针,生命周期由调用方管理。
相关资源索引
- 回调类型官方参考:doc/docs/api-reference/c/callback-types/wslccontainerimageprogresscallback.md
- 进度消息结构:doc/docs/api-reference/c/structures/wslcimageprogressmessage.md
- 拉取镜像 API 与完整示例:doc/docs/api-reference/c/image-apis/wslcpullsessionimage.md
- 枚举与结构源码定义:src/windows/WslcSDK/wslcsdk.h
- 底层转发实现:src/windows/WslcSDK/ProgressCallback.cpp、src/windows/WslcSDK/ProgressCallback.h
- WinRT 封装:src/windows/WslcSDK/winrt/ImageProgress.h
- 单元测试:test/windows/WslcSdkTests.cpp
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 StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00