
WSL C SDK 的 ProcessOutputMode 详解Discard、Stream 与 Event 三种进程输出模式的原理与实战【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSLProcessOutputMode是 WSLWindows Subsystem for LinuxC/WinRT SDKMicrosoft.WSL.Containers中决定容器内进程 stdout/stderr 去向的核心枚举。在WSLC-Neofetch、WSLC-CustomContainer等示例中它直接控制着你是丢弃输出按流读取还是回调推送。读完本文你将掌握三种模式Discard、Stream、Event的取值、行为差异、底层回调机制、调用约束与异常以及如何结合ProcessSettings、Process、ProcessOutputHandle和ProcessOutputHandler写出可运行的正确代码。一、ProcessOutputMode 的定义与取值ProcessOutputMode定义于 WinRT IDL 文件 wslcsdk.idl 中共三个枚举值枚举值底层数值语义Discard0既不产生 stdout/stderr 事件也不提供输出流Stream1可通过Process::GetOutputStream(...)获取输出流Event2stdout/stderr 通过回调投递由OutputReceived/ErrorReceived事件接收官方 API 参考文档 processoutputmode.md 给出的使用示例非常简洁procSettings.OutputMode(ProcessOutputMode::Event);OutputMode是ProcessSettings的一个可写属性见 wslcsdk.idl与WorkingDirectory、CommandLine、EnvironmentVariables并列必须在调用Process::Start()之前设置。二、三种模式的完整行为拆解1. Discard默认模式丢弃一切输出Discard是ProcessOutputMode的默认值。在该模式下进程的 stdout/stderr 不会被收集你既拿不到流对象也收不到输出事件。它适用于只管启动、不管输出的批处理场景——比如仅需执行命令并关注退出码时。从源码实现看ProcessOutputMode仅用于决定两条输出路径是否可用GetOutputStream要求Stream模式否则抛异常OutputReceived/ErrorReceived要求Event模式否则抛异常。因此Discard模式下两条路径均不可用这与文档no stdout/stderr events or output streams的描述完全一致。2. Stream流式模式拉取式读取Stream模式的核心能力是Process::GetOutputStream(ProcessOutputHandle)。它返回一个winrt::Windows::Storage::Streams::IInputStream由调用方主动读取。与之配套的枚举是ProcessOutputHandle定义于 wslcsdk.idl枚举值数值含义StandardOutput1标准输出stdoutStandardError2标准错误stderr在 Process.cpp 中GetOutputStream的实现会先校验模式再通过底层 C APIWslcGetProcessIOHandle取出管道句柄并包装成输入流if (m_outputMode ! ProcessOutputMode::Stream) { throw winrt::hresult_illegal_method_call(LGetOutputStream requires OutputMode::Stream); } wil::unique_handle handle; winrt::check_hresult(WslcGetProcessIOHandle(ToHandle(), static_castWslcProcessIOHandle(outputHandle), handle.put())); return winrt::makeIOHandleInputStream(std::move(handle));对应官方文档 process.md 中的流式示例ProcessSettings streamSettings; streamSettings.OutputMode(ProcessOutputMode::Stream); // ... set CommandLine ... auto streamProc container.CreateProcess(streamSettings); streamProc.Start(); auto stdoutStream streamProc.GetOutputStream(static_castProcessOutputHandle(1)); auto stderrStream streamProc.GetOutputStream(static_castProcessOutputHandle(2));Stream 模式的适用场景需要将子进程输出重定向到文件、管道或自定义缓冲区进行逐块、流式、可控的拉取式消费。3. Event事件模式推送式回调Event模式是三种模式中最异步友好的stdout/stderr 由底层回调推送SDK 将其转发为Process::OutputReceived与Process::ErrorReceived两个 C/WinRT 事件。事件处理器ProcessOutputHandler的定义见 processoutputhandler.mdOutputReceived和ErrorReceived携带一个参数内含原始输出字节包装层将 C 回调缓冲区转换为winrt::array_viewconst uint8_t后转发。一个典型的处理器实现process.OutputReceived([](auto const data) { std::string text(data.begin(), data.end()); printf(stdout: %s\n, text.c_str()); });底层回调机制在 Process.cpp 中ApplyCallbacksToSettings只有在m_outputMode ProcessOutputMode::Event时才把 C 回调注册进WslcProcessCallbacksif (m_outputMode ! ProcessOutputMode::Event) { return; } auto settingsPtr GetStructPointer(m_settings); WslcProcessCallbacks callbacks GetEventCallbacks(); winrt::check_hresult(WslcSetProcessSettingsCallbacks(settingsPtr, callbacks, this));回调本身是静态函数Process.cppOutputCallback根据ioHandle是WSLC_PROCESS_IO_HANDLE_STDOUT还是STDERR分别触发m_outputReceivedEvent或m_errorReceivedEvent并把原始字节构造为winrt::array_viewconst uint8_t传入void CALLBACK Process::OutputCallback(WslcProcessIOHandle ioHandle, _In_reads_bytes_(dataBytes) const BYTE* data, _In_ uint32_t dataBytes, _In_opt_ PVOID context) noexcept { auto process static_castProcess*(context); auto outputEvent (ioHandle WSLC_PROCESS_IO_HANDLE_STDOUT) ? process-m_outputReceivedEvent : process-m_errorReceivedEvent; winrt::array_viewconst uint8_t buffer{data, dataBytes}; outputEvent(buffer); }Event 模式与 Exited 事件的关系三种模式下Exited事件都会触发但触发路径不同见 process.md 行为说明Event 模式由退出回调ExitCallback直接触发ExitedProcess.cppStream / Discard 模式StartWaitingForExitAsync通过WslcGetProcessExitEvent获取进程退出事件句柄在winrt::resume_on_signal挂起等待退出事件后触发ExitedProcess.cpp。三、模式约束错误的调用会被明确拒绝三种模式的边界由 SDK 在 Process.cpp 中硬性校验违反即抛异常调用要求模式异常行为GetOutputStream(handle)Stream否则抛E_ILLEGAL_METHOD_CALLGetOutputStream requires OutputMode::StreamOutputReceived(handler)Event否则抛E_ILLEGAL_METHOD_CALLOutputReceived requires OutputMode::EventErrorReceived(handler)Event否则抛E_ILLEGAL_METHOD_CALLErrorReceived requires OutputMode::Event这些约束并非仅停留在文档层面测试 WslcSdkWinRTTests.cpp 中专门覆盖了负例与正例在Discard模式下注册OutputReceived会抛出E_ILLEGAL_METHOD_CALL在Discard模式下即使Start()之后调用GetOutputStream同样抛出在Event模式下注册/撤销事件处理器成功但调用GetOutputStream抛出在Stream模式下注册OutputReceived抛出。同时测试还验证了一个重要细节在Event模式下OutputReceived一旦注册stdout 管道句柄即被回调消费无法再通过GetOutputStream获取WslcSdkWinRTTests.cpp。四、实战完整的 Event 模式进程启动示例将 processoutputmode.md、processsettings.md 与 process.md 三份文档的示例组合即可得到完整的 Event 模式启动流程using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Microsoft::WSL::Containers; ProcessSettings procSettings; procSettings.WorkingDirectory(L/workspace); procSettings.OutputMode(ProcessOutputMode::Event); // 关键事件模式 // 命令行参数数组 auto cmd single_threaded_vectorhstring(); cmd.Append(L/bin/sh); cmd.Append(L-lc); cmd.Append(Lecho hello); procSettings.CommandLine(cmd); // 环境变量 auto env single_threaded_maphstring, hstring(); env.Insert(LDEMO, L1); procSettings.EnvironmentVariables(env); auto process container.CreateProcess(procSettings); // 注册事件处理器必须在 Start() 之前 process.OutputReceived([](auto const data) { std::string text(data.begin(), data.end()); printf(stdout: %s\n, text.c_str()); }); process.ErrorReceived([](auto const data) { std::string text(data.begin(), data.end()); fprintf(stderr, stderr: %s\n, text.c_str()); }); process.Exited([](int32_t exitCode) { printf(done: %d\n, exitCode); }); process.Start();要点回顾OutputMode必须在Process::Start()之前设置。从 ProcessSettings.cpp 可以看到一旦 settings 已应用于底层m_processSettings非空任何属性 setter 都会抛出E_ILLEGAL_STATE_CHANGECannot change value after options have been applied。Event模式的事件处理器建议在Start()之前注册避免错过早期输出。CommandLine不能为nullptr且Process::Start()要求其非空Process.cpp 会在命令行空时抛出E_INVALIDARG。五、模式选择建议场景推荐模式只需要退出码不关心输出Discard默认开销最小需要按块读取/重定向 stdout、stderr 到文件或管道StreamGetOutputStream需要实时响应输出、异步推送、避免轮询EventOutputReceived/ErrorReceived仓库中的官方示例 WSLC-Neofetch 采用Event模式将回调字节直接写入控制台是典型的实时输出消费范式processSettings.OutputMode(ProcessOutputMode::Event); // ... process.OutputReceived([](array_viewuint8_t const data) { WriteToConsole(stdout, data); });六、补充说明与延伸阅读除Container::CreateProcess(settings)外Session::OpenContainer(String nameOrId, ProcessOutputMode initProcessOutputMode)也接受该枚举用于指定已打开容器 init 进程的输出模式见 wslcsdk.idl相关正例/负例测试见 WslcSdkWinRTTests.cpp。Process::State()返回的ProcessStateUnknown/Running/Exited/Signalled可直接用于轮询判断进程是否结束可搭配Discard模式使用。想要进一步了解进程的创建、信号发送与输入流可阅读 process.md、processsettings.md 以及完整的 end-to-end-example.md。一个需要留意的限制三种模式互斥且固定——Stream与Event不能同时启用一旦选定对应的输出消费方式也随之确定。设计进程 IO 方案时应先明确拉取还是推送的消费模型再设置OutputMode并严格按上述约束调用对应 API即可避免踩中模式不匹配的异常。【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考