WSL C SDK 镜像标记指南:深入解析 WslcTagSessionImage 的用法、参数与底层实现
【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL
导读
WslcTagSessionImage是 Windows Subsystem for Linux(WSL)C SDK(WslcSDK)中负责为容器镜像创建新标签(tag)的核心 API。在 WSL 容器工作流中,拉取镜像(WslcPullSessionImage)之后、推送镜像(WslcPushSessionImage)之前,通常需要先用它给镜像打上目标仓库前缀与语义化标签,才能在本地镜像库中定位并发布镜像。本文将围绕该 API 的签名、参数、选项结构体、返回值与错误处理展开,并结合本仓库的 C 层实现(wslcsdk.cpp)与单元测试(WslcSdkTests.cpp)剖析其底层调用链,最终给出可直接复制的完整 C 示例代码,帮助你快速掌握在 C/C++ 项目中为 WSL 容器镜像打标签的实战方法。
API 签名与参数说明
WslcTagSessionImage的完整函数签名如下(声明见 wslcsdk.h):
STDAPI WslcTagSessionImage( _In_ WslcSession session, _In_ const WslcTagImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage);| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
session | WslcSession | in | 目标 WSL 容器会话句柄,由WslcCreateSession创建 |
options | const WslcTagImageOptions* | in | 标记配置,指定源镜像名/ID、目标仓库与目标标签 |
errorMessage | PWSTR* | out, optional | 失败时返回的本地化错误信息(UTF-16 字符串),可为NULL |
返回值:HRESULT。S_OK表示标记成功;失败时返回对应的错误码,并通过errorMessage提供可读错误描述。
参数语义
- session:必须是有效会话。在 wslcsdk.cpp 的实现中,函数首先通过
CheckAndGetInternalType(session)解析句柄,若底层会话对象为空,立即返回HRESULT_FROM_WIN32(ERROR_INVALID_STATE)——这也提醒调用方:任何图像管理 API 都必须先成功创建会话,不能对无效会话调用。 - options:指向
WslcTagImageOptions结构体的指针。实现中会校验其三个字段image、repo、tag均非空,任何一个为NULL都返回E_INVALIDARG(详见后文错误处理)。 - errorMessage:可选输出参数。若传入非空指针,SDK 会通过内部
ErrorInfoWrapper捕获底层错误并将可读信息写入该缓冲区;调用方应使用CoTaskMemFree释放。
选项结构体 WslcTagImageOptions
标记行为完全由WslcTagImageOptions结构体驱动(定义见 wslctagimageoptions.md):
typedef struct WslcTagImageOptions { _In_z_ PCSTR image; // Source image name or ID. _In_z_ PCSTR repo; // Target repository name. _In_z_ PCSTR tag; // Target tag name. } WslcTagImageOptions;| 字段 | 类型 | 说明 |
|---|---|---|
image | PCSTR | 源镜像名称或 ID,例如docker.io/library/alpine:latest |
repo | PCSTR | 目标仓库名称,例如demo/alpine或带注册表地址的localhost:5000/demo/alpine |
tag | PCSTR | 目标标签名称,例如stable |
三个字段组合后形成的新镜像引用即repo:tag。注意:
- 使用
{ 0 }初始化结构体是一个好习惯,能保证未赋值的字段为空指针,从而让 SDK 的参数校验(E_INVALIDARG)在字段缺失时立即生效,而不是携带未定义内存内容继续执行。 - 字段均为 UTF-8 编码的 C 字符串(
PCSTR),而errorMessage是 UTF-16(PWSTR),混合使用时要留意字符集差异。
最小可运行示例
文档给出的经典示例可直接编译运行(前提是已链接wslcsdk.lib并完成 COM 初始化与会话创建):
WslcTagImageOptions tagOptions = { 0 }; tagOptions.image = "docker.io/library/alpine:latest"; tagOptions.repo = "demo/alpine"; tagOptions.tag = "stable"; HRESULT hr = WslcTagSessionImage(session, &tagOptions, NULL); if (FAILED(hr)) { // 处理失败,可读取 errorMessage 获取详情 }在完整生命周期中的位置
WslcTagSessionImage通常与镜像拉取、列表、推送、删除 API 配合使用。完整的生命周期示例见 end-to-end-example.md,其典型顺序为:
- 调用
WslcInitSessionSettings/WslcCreateSession创建会话; - 调用
WslcPullSessionImage拉取基础镜像; - 调用
WslcTagSessionImage为镜像添加目标仓库前缀与新标签(便于后续推送); - 调用
WslcPushSessionImage推送到注册表; - 调用
WslcListSessionImages校验结果,调用WslcDeleteSessionImage清理不再需要的标签; - 最后
WslcTerminateSession/WslcReleaseSession释放资源。
错误处理与参数校验(源码级)
在 wslcsdk.cpp 中,WslcTagSessionImage的实现按顺序执行如下校验:
STDAPI WslcTagSessionImage(_In_ WslcSession session, _In_ const WslcTagImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage) try { ErrorInfoWrapper errorInfoWrapper{errorMessage}; auto internalType = CheckAndGetInternalType(session); RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session); RETURN_HR_IF_NULL(E_POINTER, options); RETURN_HR_IF_NULL(E_INVALIDARG, options->image); RETURN_HR_IF_NULL(E_INVALIDARG, options->repo); RETURN_HR_IF_NULL(E_INVALIDARG, options->tag); WSLCCompatTagImageOptions runtimeOptions{}; runtimeOptions.Image = options->image; runtimeOptions.Repo = options->repo; runtimeOptions.Tag = options->tag; return errorInfoWrapper.CaptureResult(internalType->session->TagImage(&runtimeOptions)); } CATCH_RETURN();校验顺序与返回码对应关系如下:
| 条件 | 返回码 | 含义 |
|---|---|---|
| 会话无效或未启动 | HRESULT_FROM_WIN32(ERROR_INVALID_STATE) | 底层session为空 |
options为NULL | E_POINTER | 选项指针为空 |
options->image为NULL | E_INVALIDARG | 源镜像未指定 |
options->repo为NULL | E_INVALIDARG | 目标仓库未指定 |
options->tag为NULL | E_INVALIDARG | 目标标签未指定 |
校验通过后,函数将公开结构体转换为内部运行时结构体WSLCCompatTagImageOptions,再委托给会话对象的TagImage方法执行实际标记操作,最后通过errorInfoWrapper.CaptureResult把底层错误连同可读消息一并返回给调用方。所有 C 层异常都会由CATCH_RETURN()统一转换为HRESULT,避免 C++ 异常泄漏到 C 调用边界。
测试用例佐证
上述错误码行为与 WslcSdkTests.cpp 中的TagImage测试方法完全一致:
WSLC_TEST_METHOD(TagImage) { // Positive: tag an existing image. { WslcTagImageOptions opts{}; opts.image = "debian:latest"; opts.repo = "debian"; opts.tag = "sdk-test-tag"; VERIFY_SUCCEEDED(WslcTagSessionImage(m_defaultSession, &opts, nullptr)); // Verify the tag is present. VERIFY_IS_TRUE(HasImage("debian:sdk-test-tag")); // Cleanup: delete the tag. WslcDeleteSessionImage(m_defaultSession, "debian:sdk-test-tag", nullptr); } // Negative: null options must fail. VERIFY_ARE_EQUAL(WslcTagSessionImage(m_defaultSession, nullptr, nullptr), E_POINTER); // Negative: null fields must fail. { WslcTagImageOptions opts{}; opts.image = nullptr; opts.repo = "debian"; opts.tag = "test"; VERIFY_ARE_EQUAL(WslcTagSessionImage(m_defaultSession, &opts, nullptr), E_INVALIDARG); } // ... repo / tag 为 NULL 的用例同理 }测试验证了三个关键事实:
- 标记成功后,新标签立即在镜像列表中可见(
HasImage("debian:sdk-test-tag")为真); options为NULL返回E_POINTER;image、repo、tag任一字段为NULL均返回E_INVALIDARG。
典型实战场景:标记后推送镜像
WslcTagSessionImage最常见的用途是配合本地注册表完成"拉取 → 标记 → 推送"。在 WslcSdkTests.cpp 的PushImageToRegistry辅助方法中可以看到完整模式:
// Tags and pushes an image to a local registry via the SDK APIs. void PushImageToRegistry(const std::string& repo, const std::string& tag, const std::string& registryAddress, const std::string& registryAuth) { auto imageName = std::format("{}:{}", repo, tag); auto registryImage = std::format("{}/{}:{}", registryAddress, repo, tag); auto registryRepo = std::format("{}/{}", registryAddress, repo); VERIFY_IS_TRUE(HasImage(imageName)); // Tag the image with the registry address so it can be pushed. WslcTagImageOptions tagOptions{}; tagOptions.image = imageName.c_str(); tagOptions.repo = registryRepo.c_str(); tagOptions.tag = tag.c_str(); VERIFY_SUCCEEDED(WslcTagSessionImage(m_defaultSession, &tagOptions, nullptr)); // Ensures the registry-prefixed tag is removed after the push. auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { LOG_IF_FAILED(WslcDeleteSessionImage(m_defaultSession, registryImage.c_str(), nullptr)); }); WslcPushImageOptions pushOptions{}; pushOptions.image = registryImage.c_str(); pushOptions.registryAuth = registryAuth.c_str(); VERIFY_SUCCEEDED(WslcPushSessionImage(m_defaultSession, &pushOptions, nullptr)); }这里的关键点:
- 本地镜像名为
repo:tag(如debian:latest),要推送到注册表,必须先用WslcTagSessionImage生成带注册表地址的镜像名localhost:5000/debian:latest(即registryRepo = registryAddress/repo),否则推送 API 无法定位远端仓库; - 推送完成后立即用
WslcDeleteSessionImage删除注册表前缀标签,保持本地镜像库干净; WslcPushSessionImage需要registryAuth字段提供注册表认证信息(如 Docker 的 Base64 认证串),参见 wslcpushsessionimage.md。
底层调用链:从 C API 到 WinRT 封装
WslcTagSessionImage并不直接操作镜像存储,而是将工作委托给会话对象的TagImage方法。该 API 同时被 WinRT 层复用:在 Session.cpp 中,WinRT 的Session::TagImage方法在完成空指针检查与EnsureStarted()(确保会话已启动)后,直接调用 C 层WslcTagSessionImage:
void Session::TagImage(winrt::Microsoft::WSL::Containers::TagImageOptions const& options) { if (!options) { throw winrt::hresult_error(E_POINTER, L"Tag image options cannot be null"); } EnsureStarted(); wil::unique_cotaskmem_string errorMessage; auto hr = WslcTagSessionImage(ToHandle(), GetStructPointer(options), errorMessage.put()); THROW_MSG_IF_FAILED(hr, errorMessage); }由此可以推断出完整的调用链:
WslcTagSessionImage (C API) └─> session->TagImage(&runtimeOptions) // C++ 内部实现 └─> WinRT Session::TagImage // WinRT 封装(可选路径) └─> WslcTagSessionImage // 复用同一 C 入口该函数通过 wslcsdk.def 导出,是 wslcsdk.dll 对外公开的稳定 ABI 接口之一;同时它也作为 WinRTMicrosoft.WSL.Containers命名空间的底层实现存在(选项结构体的 WinRT 版本见 TagImageOptions.cpp)。也就是说,无论是纯 C 调用方还是 WinRT/C# 调用方,最终都收敛到同一个标记实现,行为完全一致。
常见问题与最佳实践
- 忘记初始化结构体:务必用
{ 0 }初始化WslcTagImageOptions,否则未赋值的字段可能包含垃圾值,导致校验行为不可预期。 - 标签命名:标签应遵循 OCI/Docker 标签规范(字母、数字、
_、.、-,且以字母或数字开头结尾),虽然 SDK 本身依赖底层运行时校验,但提前遵循规范可以避免推送阶段失败。 - 标记后验证:调用
WslcListSessionImages确认新标签出现在镜像列表中,测试代码中的HasImage即为此模式。 - 释放错误信息:当
errorMessage输出非空时,使用CoTaskMemFree释放;这也是 WslcListSessionImages 等 API 的共同约定。 - 会话生命周期:标记操作必须在会话存活期间执行;会话终止后句柄失效,再次调用会得到
ERROR_INVALID_STATE。 - C 调用方需初始化 COM:与 SDK 中其他 API 一致,使用前应调用
CoInitializeEx,结束时CoUninitialize(参见 end-to-end-example.md)。
相关 API 一览
WslcTagSessionImage属于图像管理 API 家族,完整的成员列表见 image-apis/index.md:
| API | 功能 |
|---|---|
WslcPullSessionImage | 从注册表拉取镜像到会话 |
WslcImportSessionImage/WslcImportSessionImageFromFile | 导入镜像(含从文件导入) |
WslcLoadSessionImage/WslcLoadSessionImageFromFile | 加载镜像(含从文件加载) |
WslcListSessionImages | 列出会话中的镜像 |
WslcTagSessionImage | 为镜像创建新标签(本文主题) |
WslcDeleteSessionImage | 删除镜像或某个标签 |
WslcPushSessionImage | 推送镜像到注册表 |
这些 API 共用一个WslcSession会话句柄(句柄类型见 handle-types.md),构成了 WSL 容器镜像从拉取、标记、推送到清理的完整闭环。结合 end-to-end-example.md 与 WslcSdkTests.cpp 中的测试代码,你可以在自己的 C/C++ 项目中安全地复刻这一工作流。
【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考