WSL ContainerSettings API 详解:用 WSLC C++/WinRT 配置 WSL 容器的完整指南

WSL ContainerSettings API 详解:用 WSLC C++/WinRT 配置 WSL 容器的完整指南 WSL ContainerSettings API 详解用 WSLC C/WinRT 配置 WSL 容器的完整指南【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL导读ContainerSettings是 WSLWindows Subsystem for LinuxWSLC 容器 SDK 中的核心设置类用于在创建容器之前集中描述容器的镜像、名称、网络、端口映射、卷挂载等全部运行参数。本文以 ContainerSettings 官方 API 文档 为主线结合仓库中 WinRT 封装实现 与 C ABI 头文件 的源码级证据逐一讲解每个属性的语义、取值约束、校验规则与底层转换逻辑。读完本文你将能够独立编写一段可运行的 C/WinRT 代码通过 WSLC SDK 创建并启动一个带自定义网络、端口映射和持久化数据卷的 WSL 容器同时理解设置对象一旦物化materialize便不可再修改这一关键生命周期约束。一、ContainerSettings 在 WSLC 中的定位1.1 设置对象家族的共同约束在 Settings Classes 索引文档 中明确说明设置对象在 wrapper 物化底层 C 结构体之后将变得不可变immutable。也就是说ContainerSettings及其兄弟类SessionSettings、ProcessSettings、VhdOptions都遵循同一套先配置、后冻结的设计哲学——所有配置工作必须在对象被提交给 SDK 底层即生成对应 C 结构体指针之前完成。从 WinRT 封装实现 中可以看到每个 setter 的第一行都会做同一个检查if (m_containerSettings) { throw winrt::hresult_illegal_state_change(LCannot change ... after container has been initialized); }其中m_containerSettings是std::unique_ptrWslcContainerSettings只有在首次调用ToStructPointer()时才会被创建。因此任何属性在设置对象物化后再被修改都会抛出hresult_illegal_state_change值0x8000000EHRESULT_FROM_WIN32(ERROR_CANNOT_COPY) 相邻语义——实际是非法状态变更。这是使用本类时最容易踩的坑务必记住所有属性必须在将设置传给CreateContainer之前配置完毕。1.2 设置对象与 C ABI 的关系ContainerSettings是 WinRT 层的托管封装其底层对应的是 wslcsdk.h 中定义的 C 结构体#define WSLC_CONTAINER_OPTIONS_SIZE 104 #define WSLC_CONTAINER_OPTIONS_ALIGNMENT 8 typedef struct WslcContainerSettings { __declspec(align(WSLC_CONTAINER_OPTIONS_ALIGNMENT)) BYTE _opaque[WSLC_CONTAINER_OPTIONS_SIZE]; } WslcContainerSettings;这是一个不透明opaque结构体调用方不能直接访问内部字段只能通过WslcInitContainerSettings、WslcSetContainerSettingsName等一组 C API 来读写。WinRT 层的ContainerSettings正是把这些 C API 包装成面向对象的属性最终通过ToStructPointer()一次性把 WinRT 属性值物化到 C 结构体中再传给WslcCreateContainer。从 Container.cpp 可以看到容器创建的实际调用链Container::Container(WslcSession session, winrt::Microsoft::WSL::Containers::ContainerSettings const settings) { ... auto hr WslcCreateContainer(session, GetStructPointer(settings), m_container.put(), errorMessage.put());即ContainerSettings→ToStructPointer()→WslcContainerSettings*→WslcCreateContainer。二、构造函数与镜像名ImageName2.1 构造签名ContainerSettings的构造函数要求必须传入镜像名ContainerSettings(hstring imageName)该构造函数是唯一的入口且imageName必须非空。在 ContainerSettings.cpp 的实现中ContainerSettings::ContainerSettings(hstring const imageName) : m_imageName(winrt::to_string(imageName)) { if (imageName.empty()) { throw winrt::hresult_invalid_argument(LImage name cannot be empty); } }2.2 属性语义与运行时校验getterImageName()返回当前镜像名字符串。setterImageName(value)允许在物化前修改镜像名但有两个硬性校验见 ContainerSettings.cppvoid ContainerSettings::ImageName(hstring const value) { if (m_containerSettings) { throw winrt::hresult_illegal_state_change(LCannot change image name after container has been initialized); } if (value.empty()) { throw winrt::hresult_invalid_argument(LImage name cannot be empty); } m_imageName winrt::to_string(value); }实操要点镜像名通常形如demo-image:latest、ubuntu:22.04即名称:标签格式空字符串在任何情况下都会抛出hresult_invalid_argument物化后修改会抛出hresult_illegal_state_change。三、核心属性逐项解析下表汇总了 ContainerSettings 官方文档 列出的全部属性以及它们对应的 C 层 API依据 wslcsdk.h属性类型C 层对应 API说明ImageNamehstringWslcInitContainerSettings镜像名构造必需NamehstringWslcSetContainerSettingsName容器名称InitProcessProcessSettingsWslcSetContainerSettingsInitProcess容器启动的初始进程NetworkingModeIReferenceContainerNetworkingModeWslcSetContainerSettingsNetworkingMode网络模式仅None/BridgedHostNamehstringWslcSetContainerSettingsHostName容器内主机名DomainNamehstringWslcSetContainerSettingsDomainName容器内域名EnableAutoRemoveboolWslcSetContainerSettingsFlagsWSLC_CONTAINER_FLAG_AUTO_REMOVE容器退出后自动删除EnableGpuboolWslcSetContainerSettingsFlagsWSLC_CONTAINER_FLAG_ENABLE_GPU启用 GPU 直通PrivilegedboolWslcSetContainerSettingsFlagsWSLC_CONTAINER_FLAG_PRIVILEGED特权模式PortMappingsIVectorContainerPortMappingWslcSetContainerSettingsPortMappings端口映射列表VolumesIVectorContainerVolumeWslcSetContainerSettingsVolumes目录/文件绑定挂载列表NamedVolumesIVectorContainerNamedVolumeWslcSetContainerSettingsNamedVolumes命名会话卷VHD列表3.1 Name容器名称hstring Name(); void Name(hstring const value);设置容器的显示名称。对应 C APIWslcSetContainerSettingsName(containerSettings, name)PCSTR。在 ContainerSettings.cpp 中Name的 setter 同样受物化后不可修改约束但没有非空校验——空字符串会被原样接受只是物化时if (!m_name.empty())判断会跳过设置因此空名等价于使用 SDK 默认命名。3.2 InitProcess初始进程ProcessSettings InitProcess(); void InitProcess(ProcessSettings const value);指定容器内启动的第一个进程init 进程类型为 ProcessSettings。这是保证容器活着的关键——从仓库示例看通常用一个长驻进程充当 initauto initProcess ProcessSettings{ /* CommandLine {/bin/sleep, infinity} */ }; containerSettings.InitProcess(initProcess);在 Container.cpp 中物化后会把这个进程设置转换为implementation::Process对象if (settings.InitProcess()) { m_initProcess winrt::make_selfimplementation::Process(settings.InitProcess()); }源码依据WSLC-CustomContainer 示例 和 WSLC-NextCloud 示例 都使用了CommandLine new Liststring { /bin/sleep, infinity }作为 init 进程——容器以此保持存活之后再用container.CreateProcess(...)执行真正的业务进程。3.3 NetworkingMode网络模式重点约束IReferenceContainerNetworkingMode NetworkingMode(); void NetworkingMode(IReferenceContainerNetworkingMode const value);网络模式使用winrt::Windows::Foundation::IReferenceContainerNetworkingMode可空引用包装只允许None和Bridged两个值。从 wslcsdk.idl 中可见枚举定义enum ContainerNetworkingMode { None 0, Bridged 1, };在 ContainerSettings.cpp 中setter 有显式的取值范围校验if (value value.Value() ! ContainerNetworkingMode::None value.Value() ! ContainerNetworkingMode::Bridged) { throw winrt::hresult_invalid_argument(LInvalid networking mode); }注意官方文档注释写明 NoneandBridgedonly因此即便 IDL 枚举将来扩展出其他值当前 SDK 版本以仓库源码为准也只接受这两个。实操要点使用IReferenceT时不能直接赋枚举值需要用box_value(...).asIReferenceContainerNetworkingMode()进行装箱转换containerSettings.NetworkingMode( winrt::box_value(ContainerNetworkingMode::Bridged) .aswinrt::Windows::Foundation::IReferenceContainerNetworkingMode());3.4 HostName 与 DomainNamehstring HostName(); void HostName(hstring const value); hstring DomainName(); void DomainName(hstring const value);HostName设置容器内部的主机名如demo-host对应WslcSetContainerSettingsHostNameDomainName设置容器内部的域名如localdomain对应WslcSetContainerSettingsDomainName。两者均为可选字符串物化时仅当非空才写入 C 结构体见 ContainerSettings.cpp 的if (!m_hostName.empty())/if (!m_domainName.empty())分支。3.5 三个布尔开关EnableAutoRemove / EnableGpu / Privileged这三个布尔属性在内部合并为一个WslcContainerFlags位标志字段见 ContainerSettings.h 私有成员WslcContainerFlags m_containerFlags。标志定义在 wslcsdk.htypedef enum WslcContainerFlags { WSLC_CONTAINER_FLAG_NONE 0x00000000, WSLC_CONTAINER_FLAG_AUTO_REMOVE 0x00000001, WSLC_CONTAINER_FLAG_ENABLE_GPU 0x00000002, WSLC_CONTAINER_FLAG_PRIVILEGED 0x00000004, } WslcContainerFlags;底层通过 Windows SDK 的WI_IsFlagSet/WI_UpdateFlag宏读写见 ContainerSettings.cpp属性标志位含义EnableAutoRemoveWSLC_CONTAINER_FLAG_AUTO_REMOVE(0x1)容器停止后自动从系统中移除避免残留EnableGpuWSLC_CONTAINER_FLAG_ENABLE_GPU(0x2)向容器暴露宿主 GPU用于 CUDA/ML 等场景PrivilegedWSLC_CONTAINER_FLAG_PRIVILEGED(0x4)以特权模式运行容器内拥有更完整的内核能力物化时这三个标志通过一次调用整体写入winrt::check_hresult(WslcSetContainerSettingsFlags(m_containerSettings.get(), m_containerFlags));实操要点EnableAutoRemove在示例中普遍置为true如 WSLC-CustomContainer 示例因为临时运行的工具容器不需要保留数据类容器则应谨慎——若自动删除开启且未正确挂载数据卷容器数据会随容器一起消失。3.6 PortMappings端口映射集合IVectorContainerPortMapping PortMappings(); void PortMappings(IVectorContainerPortMapping const value);端口映射集合元素类型ContainerPortMapping定义于 wslcsdk.idlenum PortProtocol { TCP 0, UDP 1, }; runtimeclass ContainerPortMapping { ContainerPortMapping(UInt16 windowsPort, UInt16 containerPort, PortProtocol protocol); UInt16 WindowsPort; UInt16 ContainerPort; PortProtocol Protocol; Windows.Networking.HostName WindowsAddress; };其 C 层结构体 wslcsdk.htypedef struct WslcContainerPortMapping { _In_ uint16_t windowsPort; // Port on Windows host _In_ uint16_t containerPort; // Port inside container _In_ WslcPortProtocol protocol; // TCP or UDP _In_opt_ struct sockaddr_storage* windowsAddress; // accepts ipv4/6 } WslcContainerPortMapping;映射规则windowsPort是 Windows 宿主机端口containerPort是容器内部端口WindowsAddress可选用于覆盖默认绑定地址支持 IPv4/IPv6。物化时集合被转换为 C 结构体数组后调用winrt::check_hresult(WslcSetContainerSettingsPortMappings( m_containerSettings.get(), m_portMappingsStructs.data(), static_castuint32_t(m_portMappingsStructs.size())));实操示例来自官方文档见 containersettings.mdauto ports single_threaded_vectorContainerPortMapping(); ports.Append(ContainerPortMapping{ 8080, 80, PortProtocol::TCP }); containerSettings.PortMappings(ports);即Windows 的 8080 端口 → 容器内 80 端口TCP。示例项目 WSLC-NextCloud/Program.cs 正是用这条映射把 Nextcloud 暴露在http://localhost:8080。3.7 Volumes绑定挂载集合IVectorContainerVolume Volumes(); void Volumes(IVectorContainerVolume const value);元素类型ContainerVolumeruntimeclass ContainerVolume { ContainerVolume(String windowsPath, String containerPath, Boolean readOnly); String WindowsPath; String ContainerPath; Boolean ReadOnly; };C 层结构体typedef struct WslcContainerVolume { _In_z_ PCWSTR windowsPath; // Windows 宿主路径 _In_z_ PCSTR containerPath; // 容器内绝对路径 _In_ BOOL readOnly; } WslcContainerVolume;注意windowsPath是PCWSTR宽字符containerPath是PCSTRUTF-8 窄字符——跨体系路径编码在此汇合。WinRT 层通过GetStruct(volume)完成转换。实操示例auto volumes single_threaded_vectorContainerVolume(); volumes.Append(ContainerVolume{ LC:\\src, L/src, false }); containerSettings.Volumes(volumes);即把 Windows 目录C:\src以读写方式挂载到容器内/src。第三个参数readOnly为true时只读挂载。3.8 NamedVolumes命名会话卷集合IVectorContainerNamedVolume NamedVolumes(); void NamedVolumes(IVectorContainerNamedVolume const value);命名卷用于挂载由 Session 创建的 VHD 会话卷VHD 卷由WslcCreateSessionVhdVolume创建见 wslcsdk.h 注释适合需要持久化存储又不依赖 Windows 路径语义的场景runtimeclass ContainerNamedVolume { ContainerNamedVolume(String name, String containerPath, Boolean readOnly); String Name; String ContainerPath; Boolean ReadOnly; };C 层结构体typedef struct WslcContainerNamedVolume { _In_z_ PCSTR name; // Name of the session volume (from WslcVhdRequirements.name) _In_z_ PCSTR containerPath; // Absolute path inside the container _In_ BOOL readOnly; } WslcContainerNamedVolume;实操示例auto namedVolumes single_threaded_vectorContainerNamedVolume(); namedVolumes.Append(ContainerNamedVolume{ Lcache, L/cache, false }); containerSettings.NamedVolumes(namedVolumes);即把名为cache的会话卷以读写方式挂载到容器内/cache。物化时调用WslcSetContainerSettingsNamedVolumes依据 ContainerSettings.cpp。四、集合属性的安全约束官方文档强调两条重要约束见 containersettings.md 的 Important notes二者在源码中均有对应实现4.1 集合 setter 拒绝 nullptr三个集合属性的 setter 都会做空指针校验传入nullptr会抛出hresult_error(E_POINTER, ...)void ContainerSettings::PortMappings(IVectorContainerPortMapping const value) { if (m_containerSettings) { throw winrt::hresult_illegal_state_change(LCannot change port mappings after container has been initialized); } if (!value) { throw winrt::hresult_error(E_POINTER, LValue cannot be null); } m_portMappings value; }Volumes与NamedVolumes的 setter 遵循完全相同的模式见 ContainerSettings.cpp。实操要点因为 setter 拒绝nullptr而内部默认值就是空集合single_threaded_vectorT()见 ContainerSettings.h 的成员初始化所以不配置端口映射/卷时什么都不用做——直接不调用对应 setter 即可属性保持为空集合。4.2 物化时拒绝集合内的 null 元素在ToStructPointer()物化过程中会把集合逐个转换为 C 结构体一旦发现元素为 null 立即抛错for (auto const portMapping : m_portMappings) { if (!portMapping) { throw winrt::hresult_error(E_POINTER, LPort mappings collection contains a null element); } m_portMappingsStructs.push_back(GetStruct(portMapping)); }VolumesVolumes collection contains a null element与NamedVolumesNamed volumes collection contains a null element同理。这一校验只发生在物化阶段也就是说如果你向集合中Append了一个nullptrsetter 不会立刻报错而是在CreateContainer触发ToStructPointer()时才失败——调试时留意这个时序差异。五、完整可运行示例以下代码完整覆盖官方文档示例见 containersettings.md并补充了注释与校验说明#include winrt/Windows.Foundation.Collections.h #include winrt/Microsoft.WSL.Containers.h using namespace winrt; using namespace winrt::Microsoft::WSL::Containers; using namespace winrt::Windows::Foundation::Collections; ContainerSettings containerSettings{ Ldemo-image:latest }; // 基本标识 containerSettings.Name(Ldemo-container); containerSettings.HostName(Ldemo-host); containerSettings.DomainName(Llocaldomain); // 网络模式仅支持 None / Bridged且需要装箱为 IReference containerSettings.NetworkingMode( winrt::box_value(ContainerNetworkingMode::Bridged) .aswinrt::Windows::Foundation::IReferenceContainerNetworkingMode()); // 布尔开关 containerSettings.EnableAutoRemove(false); containerSettings.EnableGpu(false); containerSettings.Privileged(false); // 端口映射Windows 8080 - 容器 80 (TCP) auto ports single_threaded_vectorContainerPortMapping(); ports.Append(ContainerPortMapping{ 8080, 80, PortProtocol::TCP }); containerSettings.PortMappings(ports); // 绑定挂载C:\src - /src读写 auto volumes single_threaded_vectorContainerVolume(); volumes.Append(ContainerVolume{ LC:\\src, L/src, false }); containerSettings.Volumes(volumes); // 命名卷cache - /cache读写 auto namedVolumes single_threaded_vectorContainerNamedVolume(); namedVolumes.Append(ContainerNamedVolume{ Lcache, L/cache, false }); containerSettings.NamedVolumes(namedVolumes); // 物化并创建容器此后任何 setter 调用都会抛 hresult_illegal_state_change // WslcCreateContainer(session, GetStructPointer(containerSettings), ...);完整端到端示例仓库提供了两个可直接参考的完整程序——WSLC-CustomContainer/Program.cs最小化自定义容器含InitProcessEnableAutoRemove与 WSLC-NextCloud/Program.cs真实服务部署含Bridged网络、8080→80 端口映射、数据卷挂载。其中 NextCloud 示例还给出了一个重要的工程经验数据卷只挂载/var/www/html/data而非整个 webroot因为 Nextcloud 初始化时会写入数千个 PHP 文件整体通过 9P 挂载会非常慢——这印证了Volumes粒度选择对性能的直接影响。六、设置对象生命周期与常见错误速查6.1 生命周期总结构造ContainerSettings(imageName)镜像名非空配置任意次数调用 setter 设置属性集合 setter 拒绝 nullptr物化ToStructPointer()首次被调用由WslcCreateContainer内部触发生成WslcContainerSettings*此间校验集合内 null 元素冻结物化之后任何 setter 均抛出hresult_illegal_state_change消费C 层WslcCreateContainer(session, settings, ...)用物化结果创建容器句柄。6.2 常见错误对照错误场景抛出异常触发阶段ContainerSettings(L)或 setter 传入空镜像名hresult_invalid_argumentImage name cannot be empty构造 / setter物化后调用任意 setterhresult_illegal_state_changesetterNetworkingMode传入非None/Bridged值hresult_invalid_argumentInvalid networking modesetter集合 setter 传入nullptrhresult_error(E_POINTER)Value cannot be nullsetter集合中包含 null 元素hresult_error(E_POINTER)...contains a null element物化ToStructPointer七、相关文档与源码索引官方 API 文档ContainerSettings、Settings Classes 总览兄弟设置类ProcessSettings、SessionSettings、VhdOptionsWinRT 封装实现ContainerSettings.cpp、ContainerSettings.h、wslcsdk.idlC ABI 定义wslcsdk.h容器创建调用链Container.cpp完整示例WSLC-CustomContainer/Program.cs、WSLC-NextCloud/Program.cs以上源码文件均位于当前 WSL 仓库内读者可结合本文对照阅读深入理解ContainerSettings从 WinRT 属性到 C ABI 结构体的完整转换路径以及配置 → 物化 → 冻结 → 创建的生命周期模型。【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考