ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

WSL Containers SDK 的 ProcessOutputHandle 枚举:以 C 甄别容器进程的 stdout 与 stderr

WSL Containers SDK 的 ProcessOutputHandle 枚举:以 C 甄别容器进程的 stdout 与 stderr WSL Containers SDK 的 ProcessOutputHandle 枚举以 C# 甄别容器进程的 stdout 与 stderr【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSLProcessOutputHandle 是 WSLWindows Subsystem for Linux容器 SDKMicrosoft.WSL.Containers中用于显式指定进程输出通道的核心枚举它把 Linux 容器进程的标准输出stdout与标准错误stderr建模为两个可独立获取的句柄配合Process.GetOutputStream()即可在宿主侧以 WinRT 流的方式读取容器内进程的输出。本文将围绕该枚举的取值、与ProcessOutputMode的配合关系、C# 侧完整用法以及仓库源码级的底层实现逐一展开帮助你写出健壮、可复用的容器进程 I/O 处理代码。ProcessOutputHandle 枚举定义ProcessOutputHandle属于 WSL 容器 SDK 的 C#WinRT 投影API定义在 doc/docs/api-reference/csharp/enumerations/processoutputhandle.mdpublic enum ProcessOutputHandle { StandardOutput 1, StandardError 2 }语义要点如下该枚举只建模 stdout 与 stderr 两个输出方向标准输入stdin不在其中而是通过Process.GetInputStream()单独访问。枚举值采用 1 和 2 两个显式数值不包含 0。这是因为底层WslcProcessIOHandle原生枚举中 0 被保留给 stdin见下文源码实现对外只暴露输出方向的句柄更符合该 SDK 的职责划分。输出通道与输出模式的配合ProcessOutputModeProcessOutputHandle本身只是选择哪条输出通道的标识真正决定如何拿到输出的是配套的ProcessOutputMode枚举见 doc/docs/api-reference/csharp/enumerations/processoutputmode.mdpublic enum ProcessOutputMode { Discard 0, // 丢弃输出仅保留进程运行 Stream 1, // 允许通过 GetOutputStream(ProcessOutputHandle) 流式读取 Event 2 // 允许订阅 OutputReceived / ErrorReceived 事件 }两者在Process与ProcessSettings上的约束关系来自 doc/docs/api-reference/csharp/core-classes/process.md 与 doc/docs/api-reference/csharp/settings-classes/processsettings.md输出模式可用的输出读取方式说明Discard无进程照常运行但输出被丢弃StreamGetOutputStream(ProcessOutputHandle)以 WinRTIInputStream方式拉取 stdout/stderrEventOutputReceived/ErrorReceived事件以回调方式推送输出数据ProcessSettings.OutputMode在创建进程前配置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 };若模式与读取方式不匹配SDK 会直接抛出E_ILLEGAL_METHOD_CALLhresult_illegal_method_call例如在Discard/Event模式下调用GetOutputStream。这一点有源码与测试双重佐证详见下文。C# 实战分别读取 stdout 与 stderr流式读取OutputMode.Stream当OutputMode ProcessOutputMode.Stream时用Process.GetOutputStream(ProcessOutputHandle.StandardOutput)获取标准输出流用Process.GetOutputStream(ProcessOutputHandle.StandardError)获取标准错误流。返回类型为 WinRT 的IInputStream可直接配合DataReader使用using Windows.Storage.Streams; using IInputStream stdout process.GetOutputStream(ProcessOutputHandle.StandardOutput); using var reader new DataReader(stdout); await reader.LoadAsync(4096); string text reader.ReadString(reader.UnconsumedBufferLength); Console.WriteLine(text);stderr 的读取方式完全相同只需把枚举参数换成ProcessOutputHandle.StandardError。事件式读取OutputMode.Event当OutputMode ProcessOutputMode.Event时订阅事件并按需甄别数据来源using System.Text; process.OutputReceived data Console.Write(Encoding.UTF8.GetString(data)); process.ErrorReceived data Console.Error.Write(Encoding.UTF8.GetString(data));事件回调携带的是UInt8[]原始字节需按进程实际编码解码上例按 UTF-8。标准输入GetInputStream()stdin 不属于ProcessOutputHandle的管辖范围通过Process.GetInputStream()返回IOutputStream写入using Windows.Storage.Streams; using IOutputStream stdin process.GetInputStream(); using var writer new DataWriter(stdin); writer.WriteString(hello from C#\n); await writer.StoreAsync(); await writer.FlushAsync();值得注意底层实现中 stdin 对应的句柄值是 0WSLC_PROCESS_IO_HANDLE_STDIN 0这与ProcessOutputHandle从 1 开始编号的设计相互呼应——输出句柄从 1 起、输入句柄单独访问避免了两者在 API 层面混淆。容器 Init 进程与 Secondary 进程的差异使用输出句柄前必须清楚进程的启动方式Init 进程由Container.Start()启动不在你手上调用Start()。若其OutputMode为Event或StreamContainer.Start()会自动请求 native attach见 doc/docs/api-reference/csharp/core-classes/container.md 与 doc/docs/api-reference/csharp/known-gaps.md。Secondary 进程由Container.CreateProcess(...)创建需显式调用process.Start()后才会真正执行GetOutputStream才能取到有效流。从 doc/docs/api-reference/csharp/end-to-end-example.md 可以看到一个完整链路先以ProcessOutputMode.Event配置 init 进程再在container.Start()之前订阅OutputReceived与Exited从而在容器启动后拿到输出并等待退出码var initProcSettings new ProcessSettings { CommandLine new[] { /bin/echo, Hello from WSL Container! }, OutputMode ProcessOutputMode.Event }; var containerSettings new ContainerSettings(alpine:latest) { Name hello-container, InitProcess initProcSettings }; var container session.CreateContainer(containerSettings); var exited new TaskCompletionSourceint(TaskCreationOptions.RunContinuationsAsynchronously); container.InitProcess.OutputReceived data Console.Write(Encoding.UTF8.GetString(data)); container.InitProcess.Exited code exited.TrySetResult(code); container.Start(); var completed await Task.WhenAny(exited.Task, Task.Delay(TimeSpan.FromSeconds(30))); int exitCode completed exited.Task ? exited.Task.Result : -1; Console.WriteLine($Process exited with code: {exitCode});源码级实现从 C# 枚举到原生句柄WinRT IDL 中的声明ProcessOutputHandle与ProcessOutputMode首先在 IDL 中声明src/windows/WslcSDK/winrt/wslcsdk.idlenum ProcessOutputHandle { StandardOutput 1, StandardError 2, }; enum ProcessOutputMode { Discard 0, Stream 1, Event 2, };Processruntimeclass 中与输出句柄相关的成员同文件 wslcsdk.idlruntimeclass Process : Windows.Foundation.IClosable { ... Windows.Storage.Streams.IInputStream GetOutputStream(ProcessOutputHandle outputHandle); Windows.Storage.Streams.IOutputStream GetInputStream(); ... event ProcessOutputHandler OutputReceived; event ProcessOutputHandler ErrorReceived; event ProcessExitHandler Exited; };WinRT 投影实现模式校验与句柄获取在 src/windows/WslcSDK/winrt/Process.cpp 中GetOutputStream的实现清晰呈现了模式校验 原生句柄转换的逻辑winrt::Windows::Storage::Streams::IInputStream Process::GetOutputStream( winrt::Microsoft::WSL::Containers::ProcessOutputHandle const outputHandle) { 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)); } winrt::Windows::Storage::Streams::IOutputStream Process::GetInputStream() { wil::unique_handle handle; winrt::check_hresult(WslcGetProcessIOHandle(ToHandle(), WSLC_PROCESS_IO_HANDLE_STDIN, handle.put())); return winrt::makeIOHandleOutputStream(std::move(handle)); }对应地OutputReceived/ErrorReceived事件要求m_outputMode ProcessOutputMode::Event否则同样抛出hresult_illegal_method_call见 Process.cpp。底层原生枚举与句柄映射ProcessOutputHandle直接对映 SDK 原生枚举WslcProcessIOHandlesrc/windows/WslcSDK/wslcsdk.htypedef enum WslcProcessIOHandle { WSLC_PROCESS_IO_HANDLE_STDIN 0, WSLC_PROCESS_IO_HANDLE_STDOUT 1, WSLC_PROCESS_IO_HANDLE_STDERR 2 } WslcProcessIOHandle;可以看到StandardOutput 1与WSLC_PROCESS_IO_HANDLE_STDOUT 1、StandardError 2与WSLC_PROCESS_IO_HANDLE_STDERR 2完全一致——C# 层的枚举值是原生句柄号的直接透传。WslcGetProcessIOHandle则由 src/windows/WslcSDK/wslcsdk.cpp 导出实现。测试验证正反用例覆盖仓库的 SDK WinRT 测试 test/windows/WslcSdkWinRTTests.cpp 对ProcessOutputHandle给出了系统性的正反用例验证正常读取通过GetOutputStream(ProcessOutputHandle::StandardOutput)与StandardError分别读取进程输出并断言内容WslcSdkWinRTTests.cpp。模式不匹配即抛错在Discard模式下调用GetOutputStream会抛出ERROR_INVALID_STATEWslcSdkWinRTTests.cpp。非法调用校验多个用例验证在非Stream模式下调用GetOutputStream(StandardOutput/StandardError)抛出E_ILLEGAL_METHOD_CALLWslcSdkWinRTTests.cpp、#L1257、#L1364-L1367。这些测试同时印证了上文的两个关键结论一是ProcessOutputHandle只覆盖 stdout/stderr 两条输出通道二是输出模式必须与读取 API 严格匹配否则 SDK 会以异常形式快速失败而不是静默返回错误数据。常见问题与最佳实践为什么没有StandardInput成员因为 stdin 是输入而非输出SDK 有意将输入通道独立为GetInputStream()与ProcessOutputHandle的输出职责分离使用时不要尝试在枚举中寻找 stdin。Discard模式下还能拿到退出码吗可以。Process.ExitCode在进程退出后始终有效Exited事件对所有输出模式均可用见 doc/docs/api-reference/csharp/core-classes/process.md输出模式的取舍只影响输出数据的获取方式。何时选 Stream、何时选 Event需要按需拉取如对接自有的读取循环、控制背压时选StreamGetOutputStream希望以回调方式即时消费、代码更简洁时选EventOutputReceived/ErrorReceived完全不需要输出时选Discard以节省资源。尽量分别订阅/读取 stdout 与 stderr借助ProcessOutputHandle区分两条通道可在宿主侧把错误信息单独归档或染色显示避免将程序输出与诊断信息混为一谈若只要合并输出可分别读取后在应用层拼接。小结ProcessOutputHandle是 WSL 容器 SDK 中体积小、职责明确的枚举StandardOutput 1、StandardError 2只覆盖输出通道stdin 交由Process.GetInputStream()处理。它的实际价值体现在与ProcessOutputMode的组合使用中——Stream模式配合GetOutputStream(ProcessOutputHandle)拉取流、Event模式配合OutputReceived/ErrorReceived消费回调模式不匹配时 SDK 会立即抛错。其枚举值直接映射底层WslcProcessIOHandle原生句柄号且有完整的 WinRT 测试用例护航值得在容器进程 I/O 编程中放心使用。【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表