news 2026/9/10 19:53:23

WSL 容器 C API 端到端实战:用 WslcSDK 驱动容器完整生命周期

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WSL 容器 C API 端到端实战:用 WslcSDK 驱动容器完整生命周期

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 APIsWslcInitSessionSettingsWslcCreateSessionWslcTerminateSession会话的初始化、创建、终止与释放
Container APIsWslcInitContainerSettingsWslcCreateContainerWslcStartContainer容器的定义、创建、启动、检查、停止与删除
Process APIsWslcInitProcessSettingsWslcCreateContainerProcessWslcGetProcessExitCode容器内进程的配置、创建与状态查询
Image APIsWslcPullSessionImageWslcListSessionImagesWslcDeleteSessionImage容器镜像的拉取、导入、加载、打标签、推送、枚举与删除
Storage APIsWslcCreateSessionVhdVolumeWslcDeleteSessionVhdVolume基于 VHD 的会话卷存储管理
Install & Version APIsWslcGetMissingComponentsWslcGetVersionWslcInstallWithDependencies平台组件检查、安装与版本查询

值得注意的是,该 API 目前处于预览阶段。头文件开头的 PREVIEW NOTICE 明确声明:API 可能在未来版本中不经预告地发生破坏性变更,不建议在正式生产环境中依赖其稳定性(见 wslcsdk.h)。

二、生命周期全景:端到端示例的九个阶段

原文档 end-to-end-example.md 用一段完整main()展示了 WSL 容器的全生命周期,共九个阶段:

  1. 初始化会话设置(Session Settings)
  2. 创建会话(Create Session)
  3. 拉取镜像(Pull Image)
  4. 配置容器(Configure Container)
  5. 创建并启动容器(Create & Start Container)
  6. 检查容器状态 / 等待 init 进程退出(Inspect)
  7. 创建第二个进程(Create Second Process)
  8. 停止并删除容器(Stop & Delete Container)
  9. 释放句柄并终止会话(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 的说明,该函数接收两个关键参数:

  • namePCWSTR):会话名称。它既是显示名,也是机器级的会话唯一键——若同名会话已存在,创建会以ERROR_ALREADY_EXISTS失败;
  • storagePathPCWSTR):会话存储目录,路径不存在时会被自动创建。

需要特别警惕的是会话名称的安全语义:同一台机器上的所有用户都能看到会话的名称、创建者 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 需求结构体WslcVhdRequirementsnamesizeBytes会被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);

WslcInitProcessSettingsWslcProcessSettings结构体清零初始化,然后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(特权模式)。端口映射WslcContainerPortMappingwindowsAddress字段可覆盖默认绑定地址,同时接受 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); }

这套等待机制非常典型:

  1. WslcGetContainerInitProcess取出容器的 init 进程句柄WslcProcess
  2. WslcGetProcessExitEvent获取进程退出事件(HANDLE),交给WaitForSingleObject阻塞等待——示例设置了 30 秒超时;
  3. WslcGetProcessExitCode读取退出码;
  4. 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中注册onStdOutonStdErronExit,其中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;

逐行拆解这段"安全关停":

  1. 状态查询WslcGetContainerState返回WslcContainerState枚举,取值包括INVALIDCREATEDRUNNINGEXITEDDELETED(见 wslcsdk.h)。只有确认是RUNNING才发送停止信号,避免重复停止。
  2. 优雅停止WslcStopContainer(container, WSLC_SIGNAL_SIGTERM, 10, nullptr)发送SIGTERM并给出 10 秒宽限期;若需要立即终止可改用WSLC_SIGNAL_SIGKILL
  3. 删除容器WslcDeleteContainer的 flags 支持WSLC_DELETE_CONTAINER_FLAG_NONEWSLC_DELETE_CONTAINER_FLAG_FORCE(强制删除,即使容器仍在运行)。
  4. 释放句柄WslcReleaseContainer释放容器句柄。
  5. 终止并释放会话WslcTerminateSession终止会话(底层停止相关 VM),WslcReleaseSession释放会话句柄。

WslcSession是引用计数式的 COM 对象,WslcTerminateSessionWslcReleaseSession是两个独立职责:前者关停会话运行实体,后者归还句柄引用。若想让会话在崩溃时被感知,可注册WslcRegisterSessionCrashDumpCallback获取 Linux 侧崩溃转储回调,并通过WslcGetSessionTerminationEvent/WslcGetSessionTerminationReason区分正常关停(SHUTDOWN)与崩溃(CRASHED)。

十一、错误处理与内存管理规范

WSLC 全 API 采用HRESULT返回值,绝大多数带errorMessagePWSTR*)出参,其管理遵循三条铁律:

  1. 错误字符串必须释放errorMessageCoTaskMemAlloc分配,调用方有义务用CoTaskMemFree释放;
  2. NULL 检查兜底:失败时error可能为空指针,示例统一使用error ? error : L"unknown"的三元表达式兜底;
  3. 先释放再退出:任何失败分支都需按"容器 → 会话 → COM"的顺序完成清理,示例中 pull、create、start 三个失败分支都做了完整的反向回收。

SDK 定义了从0x80040601起始的专用错误码族WSLC_E_*(共 19 个,见 error-codes.md),常见的有:

错误码Hex场景
WSLC_E_IMAGE_NOT_FOUND0x80040601镜像不存在
WSLC_E_CONTAINER_PREFIX_AMBIGUOUS0x80040602容器 ID 前缀匹配到多个容器
WSLC_E_CONTAINER_NOT_FOUND0x80040603容器不存在
WSLC_E_CONTAINER_NOT_RUNNING0x80040605容器未在运行
WSLC_E_CONTAINER_IS_RUNNING0x80040606容器正在运行(删除/操作被拒)
WSLC_E_SDK_UPDATE_NEEDED0x8004060BSDK 需要更新

这些宏以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);WslcSignalWslcContainerNetworkingMode也通过显式switch完成翻译,未知取值直接抛出E_INVALIDARG(见 wslcsdk.cpp)。此外,WslcSessionSettingsWslcContainerSettingsWslcProcessSettings都是定长的不透明结构体(分别 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 19:53:22

LeetCode 496. Next Greater Element I 题解:Go 单调栈与哈希表实战

LeetCode 496. Next Greater Element I 题解&#xff1a;Go 单调栈与哈希表实战 【免费下载链接】LeetCode-Go ✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解 项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go …

作者头像 李华
网站建设 2026/9/10 19:53:20

SpringBoot+Vue城市公交调度系统设计与实现:从排班到实时监控

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 19:52:30

Python实现Linux抓包工具:从原理到实战

1. 为什么需要自己写抓包工具&#xff1f;在Linux环境下&#xff0c;虽然已经有Wireshark、tcpdump这样的专业抓包工具&#xff0c;但自己动手实现一个简易版本依然很有价值。我最初产生这个想法&#xff0c;是因为在一次服务器排障中遇到了特殊需求——需要实时过滤特定进程产…

作者头像 李华
网站建设 2026/9/10 19:48:46

Flutter跨端实战:为OpenHarmony打造数独生成器

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华