WSL 容器 C API 端到端实战:用 WslcSDK 驱动容器完整生命周期
【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL
WSL 容器(WSLC)在 Windows Subsystem for Linux 项目中提供了一套面向开发者的 C API——WslcSDK,让原生 C/C++ 程序能够以编程方式创建会话、拉取镜像、启动容器并管理其中的 Linux 进程。本文以仓库中的 End-to-End Example 为骨架,逐阶段拆解一个完整生命周期示例,并结合 wslcsdk.h 头文件、wslcsdk.cpp 实现与仓库内示例代码,带你掌握从环境检查、会话管理、镜像拉取到容器启停清理的完整开发链路,读完即可独立编写自己的第一个 WSL 容器控制程序。
一、WslcSDK C API 概览
WSL 容器 API 对外暴露为一套扁平 C 接口,相关定义全部集中在公共头文件 wslcsdk.h 中,编译时链接wslcsdk.lib、运行时依赖wslcsdk.dll。整套 API 按职责分为以下几组(详见 C API 参考索引):
| API 分组 | 典型接口 | 职责 |
|---|---|---|
| Session APIs | WslcInitSessionSettings、WslcCreateSession、WslcTerminateSession | 会话的初始化、创建、终止与释放 |
| Container APIs | WslcInitContainerSettings、WslcCreateContainer、WslcStartContainer | 容器的定义、创建、启动、检查、停止与删除 |
| Process APIs | WslcInitProcessSettings、WslcCreateContainerProcess、WslcGetProcessExitCode | 容器内进程的配置、创建与状态查询 |
| Image APIs | WslcPullSessionImage、WslcListSessionImages、WslcDeleteSessionImage | 容器镜像的拉取、导入、加载、打标签、推送、枚举与删除 |
| Storage APIs | WslcCreateSessionVhdVolume、WslcDeleteSessionVhdVolume | 基于 VHD 的会话卷存储管理 |
| Install & Version APIs | WslcGetMissingComponents、WslcGetVersion、WslcInstallWithDependencies | 平台组件检查、安装与版本查询 |
值得注意的是,该 API 目前处于预览阶段。头文件开头的 PREVIEW NOTICE 明确声明:API 可能在未来版本中不经预告地发生破坏性变更,不建议在正式生产环境中依赖其稳定性(见 wslcsdk.h)。
二、生命周期全景:端到端示例的九个阶段
原文档 end-to-end-example.md 用一段完整main()展示了 WSL 容器的全生命周期,共九个阶段:
- 初始化会话设置(Session Settings)
- 创建会话(Create Session)
- 拉取镜像(Pull Image)
- 配置容器(Configure Container)
- 创建并启动容器(Create & Start Container)
- 检查容器状态 / 等待 init 进程退出(Inspect)
- 创建第二个进程(Create Second Process)
- 停止并删除容器(Stop & Delete Container)
- 释放句柄并终止会话(Release & Terminate)
下文将按这九个阶段逐层深入,每一阶段都同时给出"怎么用"与"为什么",并在最后附上完整可编译源码。
三、环境准备与前置检查
任何 WSLC 程序的第一步都是初始化 COM 运行库。示例中调用:
CoInitializeEx(nullptr, COINIT_MULTITHREADED);由于WslcSDK内部依赖 COM 对象与CoTaskMemAlloc分配的错误消息,所有使用 WslcSDK 的线程都必须先初始化 COM,程序退出前再以CoUninitialize()收尾。
随后进行两项前置检查:
WslcComponentFlags missing = WSLC_COMPONENT_FLAG_NONE; hr = WslcGetMissingComponents(&missing); if (FAILED(hr) || missing != WSLC_COMPONENT_FLAG_NONE) { printf("WSL components are missing. Run: wsl --install\n"); CoUninitialize(); return 1; } WslcVersion ver = {}; WslcGetVersion(&ver); printf("WSL version: %u.%u.%u\n", ver.major, ver.minor, ver.revision);WslcGetMissingComponents返回位标志(WslcComponentFlags,定义见 wslcsdk.h),表示当前机器缺失的组件:
WSLC_COMPONENT_FLAG_NONE (0):无缺失,一切就绪;WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORM (1):缺少"虚拟机平台"可选功能,安装该组件需要重启;WSLC_COMPONENT_FLAG_WSL_PACKAGE (2):WSL 运行时包版本过低,无法提供 WSLC 支持;WSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE (4):WSLC SDK 自身需要更新。
若missing非零,示例直接提示用户运行wsl --install并退出。WslcGetVersion则把 SDK 版本写入WslcVersion结构体(major/minor/revision三个字段,见 wslcsdk.h),用于向用户展示或做版本判断。除检查外,SDK 还提供WslcInstallWithDependencies以编程方式补齐缺失组件(支持WSLC_INSTALL_OPTION_REPAIR重装模式),并可通过WslcInstallCallback回调获得安装进度。
四、会话的初始化与创建
会话(Session)是 WSLC 一切操作的顶层容器:镜像、容器、卷都隶属于某个会话。第一步是用WslcInitSessionSettings填写会话设置:
std::filesystem::path storagePath = std::filesystem::current_path(); WslcSessionSettings sessionSettings; hr = WslcInitSessionSettings(L"MyApp", storagePath.c_str(), &sessionSettings); if (FAILED(hr)) return 1;按 wslcinitsessionsettings.md 的说明,该函数接收两个关键参数:
name(PCWSTR):会话名称。它既是显示名,也是机器级的会话唯一键——若同名会话已存在,创建会以ERROR_ALREADY_EXISTS失败;storagePath(PCWSTR):会话存储目录,路径不存在时会被自动创建。
需要特别警惕的是会话名称的安全语义:同一台机器上的所有用户都能看到会话的名称、创建者 SID 和创建进程 PID。因此切勿把凭据或其他敏感信息放进会话名中。
初始化之后可以按需定制资源配额。示例中为会话分配了 4 个 CPU 与 4 GB 内存:
WslcSetSessionSettingsCpuCount(&sessionSettings, 4); WslcSetSessionSettingsMemory(&sessionSettings, 4096);会话的可选设置远不止这两项(全部见 wslcsdk.h):
| 接口 | 含义 |
|---|---|
WslcSetSessionSettingsCpuCount | 会话可用的 CPU 数量(uint32_t) |
WslcSetSessionSettingsMemory | 会话内存上限,单位 MB |
WslcSetSessionSettingsTimeout | 会话超时,单位毫秒 |
WslcSetSessionSettingsVhd | 指定会话主卷 VHD 需求(WslcVhdRequirements:名称、大小、类型) |
WslcSetSessionSettingsFeatureFlags | 会话特性位,如WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU |
其中 VHD 需求结构体WslcVhdRequirements的name与sizeBytes会被WslcSetSessionSettingsVhd使用,而flags/uid/gid字段仅由存储 APIWslcCreateSessionVhdVolume解释,传给WslcSetSessionSettingsVhd的非零标志会以E_INVALIDARG被拒绝(见 wslcsdk.h)。VHD 类型WslcVhdType支持WSLC_VHD_TYPE_DYNAMIC(动态扩展,默认)与WSLC_VHD_TYPE_FIXED(固定分配)。
设置就绪后创建会话:
WslcSession session = nullptr; hr = WslcCreateSession(&sessionSettings, &session, &error); if (FAILED(hr)) { wprintf(L"Session creation failed: %s\n", error ? error : L"unknown"); CoTaskMemFree(error); CoUninitialize(); return 1; }WslcCreateSession输出一个不透明的WslcSession句柄(由DECLARE_HANDLE声明,见 wslcsdk.h)。出错时可通过第三个参数拿到人类可读的错误消息字符串——它由CoTaskMemAlloc分配,调用方必须用CoTaskMemFree释放,这是整个 SDK 统一的资源约定。示例对每处失败分支都做了"打印错误 → 释放 error → 退出"的处理,这是 WSLC 编程的推荐范式。
五、拉取容器镜像
会话创建后,示例从 Docker Hub 拉取 Alpine Linux 镜像:
WslcPullImageOptions pullOpts = {}; pullOpts.uri = "docker.io/library/alpine:latest"; hr = WslcPullSessionImage(session, &pullOpts, &error); if (FAILED(hr)) { wprintf(L"Pull failed: %s\n", error ? error : L"unknown"); CoTaskMemFree(error); WslcTerminateSession(session); WslcReleaseSession(session); CoUninitialize(); return 1; }WslcPullImageOptions结构体(见 wslcsdk.h)包含四个字段:
uri:镜像地址,示例使用docker.io/library/alpine:latest,即 Docker Hub 上的官方 Alpine 镜像;progressCallback/progressCallbackContext:拉取进度回调及其上下文;registryAuth:私有仓库认证串(Base64 编码的X-Registry-Auth头值),公共镜像可留空。
进度回调以WslcImageProgressMessage为入参,其status字段完整覆盖了拉取的各个阶段:PULLING(拉取层)、WAITING(等待)、DOWNLOADING(下载中)、VERIFYING(校验)、EXTRACTING(解压)、COMPLETE(完成),detail中携带currentBytes/totalBytes便于绘制下载进度条(见 wslcsdk.h)。关于进度回调的完整用法,可参考 wslcpullsessionimage.md 中的示例。
镜像 API 还支持离线场景:WslcImportSessionImage/WslcImportSessionImageFromFile(从句柄或文件导入)、WslcLoadSessionImage/WslcLoadSessionImageFromFile(从文件加载)、WslcTagSessionImage(打标签)、WslcPushSessionImage(推送)、WslcListSessionImages(枚举)与WslcDeleteSessionImage(删除)。访问私有仓库时,可用WslcSessionAuthenticate先换取身份令牌:服务端返回 token 时产出{"identitytoken": ...},否则回退为{"username": ..., "password": ...},两者都编码为 Base64 JSON,可直接作为registryAuth使用(见 wslcsdk.h)。
六、配置 init 进程与容器
镜像拉取完成后,示例配置容器的 init 进程(容器启动后第一个执行的进程):
WslcProcessSettings initProcSettings; WslcInitProcessSettings(&initProcSettings); PCSTR argv[] = { "/bin/echo", "Hello from WSL Container!" }; WslcSetProcessSettingsCmdLine(&initProcSettings, argv, 2);WslcInitProcessSettings将WslcProcessSettings结构体清零初始化,然后WslcSetProcessSettingsCmdLine用标准 C 风格的argv数组(PCSTR const*+ 参数个数size_t)指定命令行。进程设置还可通过 process-apis 下的一组接口进一步定制:
| 接口 | 含义 |
|---|---|
WslcSetProcessSettingsWorkingDirectory | 进程工作目录(容器内绝对路径) |
WslcSetProcessSettingsCmdLine | 命令行参数数组 |
WslcSetProcessSettingsEnvVariables | 环境变量key=value数组 |
WslcSetProcessSettingsCallbacks | 注册 stdout/stderr 数据回调与进程退出回调 |
接着配置容器本体:
WslcContainerSettings containerSettings; WslcInitContainerSettings("alpine:latest", &containerSettings); WslcSetContainerSettingsName(&containerSettings, "hello-container"); WslcSetContainerSettingsInitProcess(&containerSettings, &initProcSettings);WslcInitContainerSettings以镜像名("alpine:latest",与拉取时的镜像对应)初始化WslcContainerSettings。可选的容器设置接口相当丰富(见 wslcsdk.h):
| 接口 | 含义 |
|---|---|
WslcSetContainerSettingsName | 容器名称 |
WslcSetContainerSettingsInitProcess | 指定 init 进程设置 |
WslcSetContainerSettingsNetworkingMode | 网络模式:WSLC_CONTAINER_NETWORKING_MODE_NONE(隔离)或WSLC_CONTAINER_NETWORKING_MODE_BRIDGED(桥接,底层映射为"bridge") |
WslcSetContainerSettingsHostName/WslcSetContainerSettingsDomainName | 容器主机名与域名 |
WslcSetContainerSettingsFlags | 容器标志(见下) |
WslcSetContainerSettingsPortMappings | 端口映射数组(Windows 端口 ↔ 容器端口,TCP/UDP,可指定绑定地址) |
WslcSetContainerSettingsVolumes | 绑定卷数组(Windows 路径 ↔ 容器路径,可只读) |
WslcSetContainerSettingsNamedVolumes | 命名卷数组(引用会话 VHD 卷) |
容器标志WslcContainerFlags支持WSLC_CONTAINER_FLAG_AUTO_REMOVE(停止后自动删除)、WSLC_CONTAINER_FLAG_ENABLE_GPU(启用 GPU)与WSLC_CONTAINER_FLAG_PRIVILEGED(特权模式)。端口映射WslcContainerPortMapping的windowsAddress字段可覆盖默认绑定地址,同时接受 IPv4/IPv6 的sockaddr_storage。
七、创建并启动容器
配置完成后创建容器句柄:
WslcContainer container = nullptr; hr = WslcCreateContainer(session, &containerSettings, &container, &error);WslcCreateContainer接收会话句柄与容器设置,输出WslcContainer句柄。随后启动:
hr = WslcStartContainer(container, WSLC_CONTAINER_START_FLAG_NONE, &error);WslcStartContainer的第二个参数是启动标志(见 wslcsdk.h):
WSLC_CONTAINER_START_FLAG_NONE (0):普通启动;WSLC_CONTAINER_START_FLAG_ATTACH (1):附加模式,配合WslcSetContainerInitProcessIOCallbacks可将 init 进程的 stdout/stderr 直接接到 Windows 侧。
启动失败时,示例执行了一套完整的回滚清理:
WslcDeleteContainer(container, WSLC_DELETE_CONTAINER_FLAG_FORCE, nullptr); WslcReleaseContainer(container); WslcTerminateSession(session); WslcReleaseSession(session); CoUninitialize();这套"失败即全量回收"的顺序值得注意:先删容器、再释放容器句柄、再终止并释放会话、最后反初始化 COM,保证任何路径下都不会泄漏句柄。
八、等待 init 进程退出并读取退出码
原文档概述中的第 6 步是"检查容器",对应示例代码中获取 init 进程句柄并等待其退出的逻辑(如需获取真正的结构化检查数据,可调用WslcInspectContainer,其返回的 ANSI 字符串同样由CoTaskMemAlloc分配、需调用方CoTaskMemFree释放,见 wslcsdk.h):
WslcProcess initProc = nullptr; hr = WslcGetContainerInitProcess(container, &initProc); if (SUCCEEDED(hr)) { HANDLE exitEvent = nullptr; if (SUCCEEDED(WslcGetProcessExitEvent(initProc, &exitEvent))) { WaitForSingleObject(exitEvent, 30000); // 30-second timeout } INT32 exitCode = 0; if (SUCCEEDED(WslcGetProcessExitCode(initProc, &exitCode))) { printf("Process exited with code: %d\n", exitCode); } WslcReleaseProcess(initProc); }这套等待机制非常典型:
WslcGetContainerInitProcess取出容器的 init 进程句柄WslcProcess;WslcGetProcessExitEvent获取进程退出事件(HANDLE),交给WaitForSingleObject阻塞等待——示例设置了 30 秒超时;WslcGetProcessExitCode读取退出码;WslcReleaseProcess释放进程句柄。
进程状态还可通过WslcGetProcessState查询(RUNNING/EXITED/SIGNALLED/UNKNOWN),或通过WslcGetProcessPid拿到容器内 Linux PID,通过WslcGetProcessIOHandle拿到 stdin/stdout/stderr 句柄做手动 IO 重定向。此外WslcSignalProcess可向进程发送 POSIX 信号,信号枚举WslcSignal覆盖了SIGHUP(1)、SIGINT(2)、SIGQUIT(3)、SIGKILL(9)、SIGTERM(15)(见 wslcsdk.h)。
九、在运行中的容器里创建第二个进程
原文档概述的第 7 步是"创建第二个进程"。在 WSLC 中,向已运行的容器再投递进程的接口是WslcCreateContainerProcess(见 wslcsdk.h):
STDAPI WslcCreateContainerProcess( _In_ WslcContainer container, _In_ WslcProcessSettings* newProcessSettings, _Out_ WslcProcess* newProcess, _Outptr_opt_result_z_ PWSTR* errorMessage);即"容器内执行(exec)"语义:传入一份新的WslcProcessSettings,得到新的WslcProcess句柄。仓库中的 WSLC-HelloWorld 示例完整演示了这一用法——容器 init 进程是/bin/sleep 60(保持容器存活),随后用WslcCreateContainerProcess创建/bin/echo进程,并通过回调方式把输出流回 Windows 控制台(见 helloworld.c)。
该示例同时展示了进程回调的正确姿势:在WslcProcessCallbacks中注册onStdOut、onStdErr与onExit,其中onExit在进程退出且 IO 全部刷出后触发,可避免退出与 IO 缓冲刷新之间的竞态。头文件明确提示:使用回调会占用 IO 句柄,之后无法再通过WslcGetProcessIOHandle获取句柄(见 wslcsdk.h)。
十、停止、删除与全量释放
示例收尾阶段先查询容器状态,若仍在运行则发送信号停止:
WslcContainerState containerState = WSLC_CONTAINER_STATE_INVALID; if (SUCCEEDED(WslcGetContainerState(container, &containerState)) && containerState == WSLC_CONTAINER_STATE_RUNNING) { WslcStopContainer(container, WSLC_SIGNAL_SIGTERM, 10, nullptr); } WslcDeleteContainer(container, WSLC_DELETE_CONTAINER_FLAG_NONE, nullptr); WslcReleaseContainer(container); WslcTerminateSession(session); WslcReleaseSession(session); CoUninitialize(); return 0;逐行拆解这段"安全关停":
- 状态查询:
WslcGetContainerState返回WslcContainerState枚举,取值包括INVALID、CREATED、RUNNING、EXITED、DELETED(见 wslcsdk.h)。只有确认是RUNNING才发送停止信号,避免重复停止。 - 优雅停止:
WslcStopContainer(container, WSLC_SIGNAL_SIGTERM, 10, nullptr)发送SIGTERM并给出 10 秒宽限期;若需要立即终止可改用WSLC_SIGNAL_SIGKILL。 - 删除容器:
WslcDeleteContainer的 flags 支持WSLC_DELETE_CONTAINER_FLAG_NONE与WSLC_DELETE_CONTAINER_FLAG_FORCE(强制删除,即使容器仍在运行)。 - 释放句柄:
WslcReleaseContainer释放容器句柄。 - 终止并释放会话:
WslcTerminateSession终止会话(底层停止相关 VM),WslcReleaseSession释放会话句柄。
WslcSession是引用计数式的 COM 对象,WslcTerminateSession与WslcReleaseSession是两个独立职责:前者关停会话运行实体,后者归还句柄引用。若想让会话在崩溃时被感知,可注册WslcRegisterSessionCrashDumpCallback获取 Linux 侧崩溃转储回调,并通过WslcGetSessionTerminationEvent/WslcGetSessionTerminationReason区分正常关停(SHUTDOWN)与崩溃(CRASHED)。
十一、错误处理与内存管理规范
WSLC 全 API 采用HRESULT返回值,绝大多数带errorMessage(PWSTR*)出参,其管理遵循三条铁律:
- 错误字符串必须释放:
errorMessage由CoTaskMemAlloc分配,调用方有义务用CoTaskMemFree释放; - NULL 检查兜底:失败时
error可能为空指针,示例统一使用error ? error : L"unknown"的三元表达式兜底; - 先释放再退出:任何失败分支都需按"容器 → 会话 → COM"的顺序完成清理,示例中 pull、create、start 三个失败分支都做了完整的反向回收。
SDK 定义了从0x80040601起始的专用错误码族WSLC_E_*(共 19 个,见 error-codes.md),常见的有:
| 错误码 | Hex | 场景 |
|---|---|---|
WSLC_E_IMAGE_NOT_FOUND | 0x80040601 | 镜像不存在 |
WSLC_E_CONTAINER_PREFIX_AMBIGUOUS | 0x80040602 | 容器 ID 前缀匹配到多个容器 |
WSLC_E_CONTAINER_NOT_FOUND | 0x80040603 | 容器不存在 |
WSLC_E_CONTAINER_NOT_RUNNING | 0x80040605 | 容器未在运行 |
WSLC_E_CONTAINER_IS_RUNNING | 0x80040606 | 容器正在运行(删除/操作被拒) |
WSLC_E_SDK_UPDATE_NEEDED | 0x8004060B | SDK 需要更新 |
这些宏以WSLC_E_BASE (0x0600)为起点,经MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, ...)生成(见 wslcsdk.h),与头文件和 IDL 文件保持同步更新。
在底层实现上(wslcsdk.cpp),SDK 通过一组FlagsTraits模板与static_assert把公开的Wslc*Flags枚举翻译为内部WSLC*Flags运行时值,确保公开值与运行时值永不失配(见 wslcsdk.cpp);WslcSignal与WslcContainerNetworkingMode也通过显式switch完成翻译,未知取值直接抛出E_INVALIDARG(见 wslcsdk.cpp)。此外,WslcSessionSettings、WslcContainerSettings、WslcProcessSettings都是定长的不透明结构体(分别 72 / 104 / 72 字节,8 字节对齐,见 constants.md),应用层只能通过 setter 接口修改,内部实现因此可自由演进而不破坏二进制兼容——这也解释了为什么所有设置都必须先用WslcInit*Settings初始化再逐个 setter 修改。
十二、完整端到端示例源码
以下为原文档 end-to-end-example.md 的完整示例,覆盖从 COM 初始化到会话释放的全部九个阶段:
#include <winsock2.h> #include <windows.h> #include <stdio.h> #include <objbase.h> #include <filesystem> #include "wslcsdk.h" #pragma comment(lib, "ole32.lib") #pragma comment(lib, "wslcsdk.lib") int main() { // Initialize COM CoInitializeEx(nullptr, COINIT_MULTITHREADED); HRESULT hr; PWSTR error = nullptr; // 0. Check prerequisites WslcComponentFlags missing = WSLC_COMPONENT_FLAG_NONE; hr = WslcGetMissingComponents(&missing); if (FAILED(hr) || missing != WSLC_COMPONENT_FLAG_NONE) { printf("WSL components are missing. Run: wsl --install\n"); CoUninitialize(); return 1; } WslcVersion ver = {}; WslcGetVersion(&ver); printf("WSL version: %u.%u.%u\n", ver.major, ver.minor, ver.revision); // 1. Initialize and create a session std::filesystem::path storagePath = std::filesystem::current_path(); WslcSessionSettings sessionSettings; hr = WslcInitSessionSettings(L"MyApp", storagePath.c_str(), &sessionSettings); if (FAILED(hr)) return 1; // Optionally customize resources WslcSetSessionSettingsCpuCount(&sessionSettings, 4); WslcSetSessionSettingsMemory(&sessionSettings, 4096); WslcSession session = nullptr; hr = WslcCreateSession(&sessionSettings, &session, &error); if (FAILED(hr)) { wprintf(L"Session creation failed: %s\n", error ? error : L"unknown"); CoTaskMemFree(error); CoUninitialize(); return 1; } // 2. Pull an image WslcPullImageOptions pullOpts = {}; pullOpts.uri = "docker.io/library/alpine:latest"; hr = WslcPullSessionImage(session, &pullOpts, &error); if (FAILED(hr)) { wprintf(L"Pull failed: %s\n", error ? error : L"unknown"); CoTaskMemFree(error); WslcTerminateSession(session); WslcReleaseSession(session); CoUninitialize(); return 1; } // 3. Configure an init process WslcProcessSettings initProcSettings; WslcInitProcessSettings(&initProcSettings); PCSTR argv[] = { "/bin/echo", "Hello from WSL Container!" }; WslcSetProcessSettingsCmdLine(&initProcSettings, argv, 2); // 4. Configure and create a container WslcContainerSettings containerSettings; WslcInitContainerSettings("alpine:latest", &containerSettings); WslcSetContainerSettingsName(&containerSettings, "hello-container"); WslcSetContainerSettingsInitProcess(&containerSettings, &initProcSettings); WslcContainer container = nullptr; hr = WslcCreateContainer(session, &containerSettings, &container, &error); if (FAILED(hr)) { wprintf(L"Container creation failed: %s\n", error ? error : L"unknown"); CoTaskMemFree(error); WslcTerminateSession(session); WslcReleaseSession(session); CoUninitialize(); return 1; } // 5. Start the container hr = WslcStartContainer(container, WSLC_CONTAINER_START_FLAG_NONE, &error); if (FAILED(hr)) { wprintf(L"Start failed: %s\n", error ? error : L"unknown"); CoTaskMemFree(error); WslcDeleteContainer(container, WSLC_DELETE_CONTAINER_FLAG_FORCE, nullptr); WslcReleaseContainer(container); WslcTerminateSession(session); WslcReleaseSession(session); CoUninitialize(); return 1; } // 6. Wait for the init process to exit WslcProcess initProc = nullptr; hr = WslcGetContainerInitProcess(container, &initProc); if (SUCCEEDED(hr)) { HANDLE exitEvent = nullptr; if (SUCCEEDED(WslcGetProcessExitEvent(initProc, &exitEvent))) { WaitForSingleObject(exitEvent, 30000); // 30-second timeout } INT32 exitCode = 0; if (SUCCEEDED(WslcGetProcessExitCode(initProc, &exitCode))) { printf("Process exited with code: %d\n", exitCode); } WslcReleaseProcess(initProc); } // 7. Clean up WslcContainerState containerState = WSLC_CONTAINER_STATE_INVALID; if (SUCCEEDED(WslcGetContainerState(container, &containerState)) && containerState == WSLC_CONTAINER_STATE_RUNNING) { WslcStopContainer(container, WSLC_SIGNAL_SIGTERM, 10, nullptr); } WslcDeleteContainer(container, WSLC_DELETE_CONTAINER_FLAG_NONE, nullptr); WslcReleaseContainer(container); WslcTerminateSession(session); WslcReleaseSession(session); CoUninitialize(); return 0; }注意:/bin/echo执行完毕后 init 进程会立即退出,容器状态转为EXITED,因此清理阶段的WslcGetContainerState检查通常不会命中RUNNING分支——该检查的真正价值在于"通用化",当 init 进程是/bin/sleep这类长驻进程时,确保容器能被优雅关停。
十三、从示例走向真实工程
端到端示例解决了"生命周期怎么走"的问题,而仓库内还有两处可直接借鉴的进阶素材:
- WSLC-HelloWorld:完整可编译的最小示例(helloworld.c)。它用
WslcSetProcessSettingsCallbacks以回调方式把容器 stdout/stderr 实时转发到 Windows 控制台,用WSLC_CONTAINER_FLAG_AUTO_REMOVE让容器停止后自动清理,并在wmain中演示了完整的goto cleanup错误回收范式; - WSLC-Neofetch:基于 C++ 的进阶示例,演示在容器内运行
neofetch并把终端输出回显到 Windows 侧的完整工程化写法。
若你的宿主程序不是 C/C++,WslcSDK 还提供了 C# 与 WinRT 封装(见 WslcSDK/csharp 与 WslcSDK/winrt),接口语义与本例的 C 接口一一对应。编写生产级程序时,建议结合 session-apis、container-apis、process-apis、image-apis、storage-apis、enumerations 与 error-codes 各参考页逐项核对签名与语义,并留意 not-yet-implemented-apis.md 中尚未实现的接口清单,避免在预览期依赖尚未落地的能力。
【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考