
WSL Containers C# SDK ProcessSettings 详解容器进程配置、输出模式与源码实现【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL本文围绕 WSLWindows Subsystem for Linux仓库中 WSLC SDK 的 C# API 参考文档 processsettings.md 展开完整讲解ProcessSettings类的四个成员属性、默认值、约束规则与典型用法并结合仓库中的 WinRT 定义文件与包装器实现源码剖析该设置类从 C# 对象到 C 语言结构体WslcProcessSettings的底层传递链路帮助开发者正确配置容器 init 进程与 exec 派生进程。ProcessSettings 的角色与两个使用入口ProcessSettings是一个 sealed 设置类用于在进程启动前描述该进程的运行参数其 C# 投影定义如下引自原文档public sealed class ProcessSettings { public string WorkingDirectory { get; set; } public IListstring CommandLine { get; set; } public IDictionarystring, string EnvironmentVariables { get; set; } public ProcessOutputMode OutputMode { get; set; } }从仓库的 WinRT 接口定义 wslcsdk.idl 可以看到其底层投影runtimeclass ProcessSettings拥有String WorkingDirectory、IVectorString CommandLine、IMapString, String EnvironmentVariables与ProcessOutputMode OutputMode四个属性。C# 端的IListstring与IDictionarystring, string分别对应 IDL 中的IVectorString与IMapString, String。在 SDK 中有两个入口会消费ProcessSettings对象容器 init 进程ContainerSettings.InitProcess属性类型为ProcessSettings见 containersettings.md 与 wslcsdk.idl 中ContainerSettings的ProcessSettings InitProcess;声明。该设置项是可选的配置后容器启动时才会产生Container.InitProcess对象。容器派生进程execContainer.CreateProcess(ProcessSettings newProcessSettings)用一份新的ProcessSettings在运行中的容器内创建二级进程见 container.md 与 wslcsdk.idl。成员属性说明属性类型含义备注WorkingDirectorystring进程启动时的工作目录可选仅当非空时才会下发到底层 C APICommandLineIListstring命令行参数数组argv 语义每个元素一个参数调用Process.Start()前必须非空EnvironmentVariablesIDictionarystring, string进程环境变量实现层会转换为KEYVALUE字符串序列下发OutputModeProcessOutputMode进程输出的消费方式默认值为Discard丢弃输出ProcessOutputMode枚举定义于 wslcsdk.idlenum ProcessOutputMode { Discard 0, // 丢弃 stdout/stderr Stream 1, // 通过 GetOutputStream(...) 以流式读取 Event 2, // 通过 OutputReceived / ErrorReceived 事件接收 };配套的ProcessOutputHandle枚举wslcsdk.idl区分流的目标StandardOutput 1与StandardError 2。默认值OutputMode 为 Discard可以结合源码确认默认行为WinRT 包装器实现 ProcessSettings.h 中m_outputMode的初始值被显式初始化为ProcessOutputMode::Discardwinrt::Microsoft::WSL::Containers::ProcessOutputMode m_outputMode{ winrt::Microsoft::WSL::Containers::ProcessOutputMode::Discard};因此如果开发者完全不设置OutputMode进程的输出stdout 与 stderr将被直接丢弃OutputReceived/ErrorReceived事件与GetOutputStream(...)都不会产生可用数据。这是排错时最常见的“进程没输出”原因。关键约束原文档 Notes 的完整继承与展开原文档给出四条 Notes这里结合实现逐条展开CommandLine必须非空才能调用Process.Start()。从 WinRT 包装器看ProcessSettings.cpp 的ToStructPointer()中只有当m_commandLine.Size() 0时才调用WslcSetProcessSettingsCmdLine下发命令行空数组不会报错但底层也不会获得任何 argv后续启动自然失败。init 进程由Container.Start()启动而不是Process.Start()。这一规则在 container.md 与 process.md 中均被强调Process.Start()只用于Container.CreateProcess(...)创建的二级进程init 进程的生命周期跟随容器本身。OutputMode.Event启用OutputReceived/ErrorReceived事件。事件回调签名为delegate void ProcessOutputHandler(UInt8[] data)wslcsdk.idl。OutputMode.Stream启用GetOutputStream(...)。返回Windows.Storage.Streams.IInputStream可配合DataReader分块读取示例见 process.md 的GetOutputStream一节。另外从 process.md 可以补充一点Exited事件delegate void ProcessExitHandler(Int32 exitCode)在所有输出模式下都可用因此即使选择Discard也可以只关心退出码。完整示例init 进程与 exec 派生进程原文档基础示例原文档给出的示例保持原样var processSettings new ProcessSettings { WorkingDirectory /workspace, CommandLine new Liststring { /bin/sh, -c, env | sort }, EnvironmentVariables new Dictionarystring, string { [DEMO] 1, [PATH] /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin }, OutputMode ProcessOutputMode.Event };该示例展示了四个属性的完整填法以/workspace为工作目录通过/bin/sh -c env | sort打印排序后的环境变量同时验证自定义变量DEMO1与显式指定的PATH是否生效——这是验证EnvironmentVariables传递链路的典型做法。组合示例ContainerSettings.InitProcessProcessSettings作为ContainerSettings.InitProcess时的用法与 containersettings.md 中的示例一致var init new ProcessSettings { CommandLine new Liststring { /bin/sh, -c, echo hello from init }, OutputMode ProcessOutputMode.Event }; var containerSettings new ContainerSettings(docker.io/library/alpine:latest) { Name demo-container, InitProcess init, EnableAutoRemove true };配置后container.Start()会启动容器并附带拉起 init 进程此时可通过container.InitProcess取得该Process对象并订阅事件注意不要对 init 进程再调用Start()。组合示例Container.CreateProcess 二级进程var execSettings new ProcessSettings { CommandLine new Liststring { /bin/sh, -c, echo secondary process }, OutputMode ProcessOutputMode.Event }; Process process container.CreateProcess(execSettings); process.OutputReceived data Console.Write(System.Text.Encoding.UTF8.GetString(data)); process.Exited code Console.WriteLine($Exit code: {code}); process.Start();若改用流式读取则设置OutputMode ProcessOutputMode.Stream并用Process.GetOutputStream(ProcessOutputHandle.StandardOutput)获取IInputStream完整DataReader用法见 process.md。源码级实现从 C# 对象到 WslcProcessSettings 结构体一次性设置与“已应用后锁定”机制WinRT 包装器 ProcessSettings.h 用一组成员缓存 C# 端属性值m_workingDirectorystd::string、m_commandLinesingle_threaded_vectorhstring、m_environmentVariablessingle_threaded_maphstring, hstring以及m_outputMode。所有属性的 setter 都包含同一套保护逻辑见 ProcessSettings.cppvoid ProcessSettings::WorkingDirectory(hstring const value) { if (m_processSettings) { throw hresult_illegal_state_change(LCannot change value after options have been applied); } m_workingDirectory winrt::to_string(value); }从源码结构看m_processSettingsstd::unique_ptrWslcProcessSettings一旦被创建就表示设置值已经“应用”到底层 C 结构体此后再修改任何属性都会抛出hresult_illegal_state_change。这解释了 API 的使用纪律先在Start()/CreateProcess()之前把ProcessSettings填完整之后只读。此外CommandLine与EnvironmentVariables的 setter 都做了空引用校验传入null集合会抛出E_POINTERProcessSettings.cpp。下发链路ToStructPointer 与 C API 调用真正把设置物化到底层的是ToStructPointer()ProcessSettings.cpp其调用顺序与条件如下首次调用时执行WslcInitProcessSettings(m_processSettings.get())初始化 C 结构体对应 C API 参考 wslcinitprocesssettings.md仅当WorkingDirectory非空时调用WslcSetProcessSettingsWorkingDirectory见 wslcsetprocesssettingsworkingdirectory.md仅当CommandLine非空时将IVectorhstring逐元素转为 UTF-8 字符串数组调用WslcSetProcessSettingsCmdLine(settings, argv, argc)见 wslcsetprocesssettingscmdline.md仅当EnvironmentVariables非空时把每个键值对拼成KEYVALUE形式再调用WslcSetProcessSettingsEnvVariables见 wslcsetprocesssettingsenvvariables.mdfor (auto const [key, value] : m_environmentVariables) { m_envStrings.Add(winrt::to_string(key) winrt::to_string(value)); } winrt::check_hresult(WslcSetProcessSettingsEnvVariables( m_processSettings.get(), m_envStrings.GetRawPointer(), size));值得注意的细节是环境变量的传递语义是字符串替换式的KEYVALUE列表与 Linuxexecve的envp一致而不是逐条setenv这与容器 init 环境的构造方式相符。StringArray保持裸指针有效的辅助结构C API 要求argv/envp是PCSTR*裸指针数组而元素本身又是动态字符串。包装器用StringArrayProcessSettings.h 定义、ProcessSettings.cpp 实现双份保存来解决生命周期问题struct StringArray { std::vectorstd::string m_strings; // 保存字符串本体 std::vectorPCSTR m_rawStrings; // 保存指向本体的裸指针 };Add(std::string s)先push_back字符串本体、再记录m_strings.back().c_str()作为裸指针从而保证GetRawPointer()返回的PCSTR*在整个结构体生命周期内始终有效依赖 vector 扩容前容量已由StringArray(size_t capacity)一次性 reserve。m_commandLineStrings与m_envStrings作为ProcessSettings的成员ProcessSettings.h与WslcProcessSettings同生命周期避免悬垂指针。OutputMode 不经过 C 结构体值得注意的是ToStructPointer()只下发了WorkingDirectory、CommandLine、EnvironmentVariables三项OutputMode并不写入WslcProcessSettings。从源码结构看OutputMode是 SDK 包装层用来决定“以事件还是以流暴露 I/O”的策略开关Event对应 IOCallback 一类的事件回调路径Stream对应可读取的IInputStreamDiscard则两者皆无。它影响的是输出通道形态而不是进程本身如何被创建。ProcessSettings 与 Process 的生命周期关系将 process.md 的约束与本文合并可得到一张清晰的对照表项init 进程ContainerSettings.InitProcess派生进程Container.CreateProcess启动方式Container.Start()自动拉起显式Process.Start()获取方式Container.InitProcess仅当配置了InitProcessCreateProcess的返回值OutputMode.Event订阅OutputReceived/ErrorReceived同左OutputMode.StreamGetOutputStream(ProcessOutputHandle.StandardOutput/StandardError)同左退出通知Exited事件所有模式可用同左输入GetInputStream()写 stdin同左Process还暴露PidUInt32、StateProcessStateUnknown/Running/Exited/Signalled见 wslcsdk.idl与ExitCode退出后有效可用于轮询或状态判断进程对象实现了IClosableDispose不再使用时应释放。常见陷阱与排错要点结合上述文档与源码事实使用ProcessSettings时容易出现以下问题忘记设置OutputMode默认Discard会导致“事件不触发、流读不到数据”先检查该属性是否设为Event或Stream。对 init 进程调用Process.Start()init 进程随Container.Start()启动重复启动不符合 API 契约。CommandLine为空Start()前必须保证非空注意 argv 语义——程序路径本身是第一个元素如/bin/sh选项与参数各自独立成元素而不是整句字符串。在设置已下发后继续改属性会抛出hresult_illegal_state_change“Cannot change value after options have been applied”正确做法是在启动前一次填好全部属性。依赖自定义PATH却未覆盖默认环境示例中显式给出PATH的原因就在于容器环境未必包含预期路径必要时像原文档示例那样显式声明。延伸阅读C# API 参考入口api-reference/csharp、Settings 类索引、端到端示例相关 C# 类文档Container、Process、ContainerSettingsC API 结构体与函数参考WslcProcessSettings 结构体、process-apis 目录实现源码ProcessSettings 包装器、ProcessSettings 头文件、WinRT 接口定义【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考