
使用 WslcSetProcessSettingsCallbacks 为 WSL 容器进程注册 I/O 与退出回调【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL导读WslcSetProcessSettingsCallbacks是 WSLWindows Subsystem for LinuxC API 中负责为待启动进程绑定标准输出/标准错误数据回调与进程退出回调的入口函数。当你在 Windows 侧通过 WSLC SDK 启动一个 Linux 容器内进程时可通过本函数把 stdout/stderr 字节流与退出码直接推到你的 C 代码中而无需自行轮询句柄或管理 I/O 线程。读完本文你将掌握该 API 的完整签名与参数语义、回调结构体WslcProcessCallbacks的字段约束、它与其他进程设置 API 的调用顺序以及它在 WinRT 封装层中Event 输出模式下的实际使用方式。API 签名与参数语义STDAPI WslcSetProcessSettingsCallbacks( _In_ WslcProcessSettings* processSettings, _In_ const WslcProcessCallbacks* callbacks, _In_opt_ PVOID context);参数类型方向说明processSettingsWslcProcessSettings*in由 WslcInitProcessSettings 初始化并持有的进程设置对象回调会写入该对象内部callbacksconst WslcProcessCallbacks*in指向回调集合结构的指针结构体内容会被拷贝进processSettings可传入NULL以清空已注册回调contextPVOIDin, optional透传给每个回调的用户自定义上下文指针可传NULL返回值HRESULT成功返回S_OK。从 wslcsdk.cpp 的实现 可以看出几个值得注意的约束参数校验若callbacks NULL context ! NULL返回E_INVALIDARG即不能只给上下文而不给回调结构体约束源码中有static_assert(std::is_trivial_vWslcProcessCallbacks, WslcProcessCallbacks must be trivial.)要求WslcProcessCallbacks是平凡类型trivial type因此它内部只能是指针成员不能包含需要构造/析构的 C 对象拷贝语义传入的回调集合被整体浅拷贝到内部ioCallbacks字段随后callbackContext被单独设置为context清空语义传入callbacks NULL时内部回调集合被清零*internalCallbacks {}可用于在启动前撤销已注册的回调。回调结构体 WslcProcessCallbacks 详解该结构体定义于 wslcsdk.htypedef __callback void(CALLBACK* WslcStdIOCallback)( WslcProcessIOHandle ioHandle, _In_reads_bytes_(dataBytes) const BYTE* data, _In_ uint32_t dataBytes, _In_opt_ PVOID context); typedef __callback void(CALLBACK* WslcProcessExitCallback)( INT32 exitCode, _In_opt_ PVOID context); typedef struct WslcProcessCallbacks { WslcStdIOCallback onStdOut; WslcStdIOCallback onStdErr; WslcProcessExitCallback onExit; } WslcProcessCallbacks;三个回调字段字段类型触发时机onStdOutWslcStdIOCallback进程产生标准输出数据时onStdErrWslcStdIOCallback进程产生标准错误数据时onExitWslcProcessExitCallback进程退出且剩余 I/O 已冲刷完毕后I/O 回调的约束重要头文件注释给出了明确的契约这是使用本 API 最容易踩坑的地方data缓冲区由 WSLC 所有仅在回调执行期间有效。回调返回后 WSLC 会立即释放或复用该缓冲区因此绝不能在回调中 free、修改或长期保留该指针需要保留数据必须在返回前拷贝例如写入自己的环形缓冲区。缓冲区是原始字节序列不以\0结尾dataBytes才是有效长度。回调必须快速返回长时间阻塞操作会阻塞 WSLC 内部的 I/O 处理线程拖慢整个进程的输出管道。只有 STDOUT 与 STDERR 会收到 I/O 回调STDIN 不产生数据回调stdin 数据通过 WslcGetProcessIOHandle 获取写句柄后写入。退出回调的意义头文件明确建议如果使用了 I/O 回调也应该使用onExit回调以消除进程退出与I/O 缓冲区冲刷之间的竞态——一旦onExit被调用任何 I/O 回调都不会再被触发此时可以安全地释放依赖输出数据的资源。回调与会话退出事件的配合在 WinRT 封装层中Process.cpp 的ApplyCallbacksToSettings展示了回调只与Event输出模式配套使用当输出模式为Stream/Discard时走的是 WslcGetProcessExitEvent 事件等待路径StartWaitingForExitAsync只有Event模式才通过WslcSetProcessSettingsCallbacks注册回调。这正是 I/O 回调与事件等待两种退出通知机制的分工。与 WslcGetProcessIOHandle 的互斥关系原文档的 Header note 明确指出使用回调会消费进程的 I/O 句柄此后不能再通过WslcGetProcessIOHandle获取句柄。头文件中的注释同样强调了这一点Using any callbacks will consume the IO handles, preventing acquisition through WslcGetProcessIOHandle。这意味着你必须在两种 I/O 消费方式中二选一方式使用 API适用场景回调模式WslcSetProcessSettingsCallbacks需要按数据块流式消费 stdout/stderr、或希望由 WSLC 管理读线程句柄模式WslcGetProcessIOHandle需要自行 ReadFile、与既有句柄管理/select/WaitForMultipleObjects 逻辑集成两种模式不要混用否则获取句柄会失败。完整使用示例前置初始化与配置进程设置回调需要挂在WslcProcessSettings上因此完整流程是先初始化设置对象、再配置命令行/环境变量/工作目录、最后注册回调WslcProcessSettings processSettings; HRESULT hr WslcInitProcessSettings(processSettings); if (FAILED(hr)) { /* 处理错误 */ } // 配置命令行为 /bin/sh -c echo ready PCSTR const argv[] { /bin/sh, -c, echo ready }; hr WslcSetProcessSettingsCmdLine(processSettings, argv, _countof(argv)); // 配置环境变量可选 PCSTR const key_value[] { HOME/root, DEMO_FLAG1 }; hr WslcSetProcessSettingsEnvVariables(processSettings, key_value, _countof(key_value)); // 配置工作目录可选 hr WslcSetProcessSettingsWorkingDirectory(processSettings, /work);以上三个配置 API 与本文的WslcSetProcessSettingsCallbacks一起构成了完整的进程设置流程详见 Process APIs 索引。注意三个配置 API 的_In_reads_(argc)注释表明argv/key_value数组按argc个元素读取。核心注册回调沿用原文档示例将 stdout 与 stderr 都转发到标准输出并在退出时打印退出码void CALLBACK OnStdOut(WslcProcessIOHandle ioHandle, const BYTE* data, uint32_t dataBytes, PVOID context) { UNREFERENCED_PARAMETER(ioHandle); UNREFERENCED_PARAMETER(context); fwrite(data, 1, dataBytes, stdout); } void CALLBACK OnExit(INT32 exitCode, PVOID context) { UNREFERENCED_PARAMETER(context); printf(exit%ld\n, (long)exitCode); } WslcProcessCallbacks callbacks { 0 }; callbacks.onStdOut OnStdOut; callbacks.onStdErr OnStdOut; // stderr 也走同一个打印回调 callbacks.onExit OnExit; HRESULT hr WslcSetProcessSettingsCallbacks(processSettings, callbacks, NULL);注意WslcProcessCallbacks callbacks { 0 }将三个字段全部清零保证未设置的回调为NULLWSLC 内部对NULL字段视为不订阅该事件。后续启动进程与查询状态注册回调后正常启动进程之后可通过 WslcGetProcessStateWSLC_PROCESS_STATE_RUNNING/WSLC_PROCESS_STATE_EXITED/WSLC_PROCESS_STATE_SIGNALLED、WslcGetProcessExitCode 与 WslcGetProcessExitEvent 等 API 管理生命周期结束时调用 WslcReleaseProcess 释放资源。清空回调若在启动前需要撤销已注册的回调传NULL即可hr WslcSetProcessSettingsCallbacks(processSettings, NULL, NULL);注意此时context也必须为NULL否则返回E_INVALIDARG。源码级原理剖析底层实现wslcsdk.cppwslcsdk.cpp 中WslcSetProcessSettingsCallbacks的实现核心只有几步通过CheckAndGetInternalType(processSettings)取得内部实现对象句柄非法时返回错误校验callbacks与context的参数组合static_assert确保结构体平凡性这也从 ABI 层面保证该结构体可以在进程边界安全拷贝将外部回调集合浅拷贝进ioCallbacks并单独覆写callbackContext传入NULL则清零。WinRT 层的事件分发Process.cppWinRT 封装展示了回调context参数的典型用法——直接把 C 对象指针this作为上下文静态回调函数再还原为对象实例GetEventCallbacks 将onStdOut/onStdErr都设为静态OutputCallbackonExit设为静态ExitCallbackOutputCallback 通过static_castProcess*(context)还原对象再根据ioHandle判断是 stdout 还是 stderr并把字节数组封装成winrt::array_view触发OutputReceived/ErrorReceived事件ExitCallback 同样还原对象并触发Exited事件。这正是把原生回调封装为面向对象事件模型的教科书式写法也是 C 代码中自己管理context时的推荐模式。输出模式的分流wslcsdk.idl从 wslcsdk.idl 可以看到ProcessOutputMode枚举Discard 0、Stream 1、Event 2。其中Event模式对应本文的回调机制输出以事件/回调形式送达而Stream模式通过GetOutputStream/GetInputStream暴露流对象。选择Event模式并搭配本 API 注册回调是获得数据到达即通知语义的标准路径。使用建议与注意事项汇总二选一不混用注册回调后不要再调用WslcGetProcessIOHandle获取 stdout/stderr 句柄否则会失败需要自管句柄的场景请走句柄模式。必须配onExit只要注册了 I/O 回调就应同时注册退出回调避免进程退出与输出缓冲冲刷之间的竞态导致数据丢失。回调里不保留指针data缓冲区仅在回调执行期间有效需要留存数据务必先拷贝。回调要快I/O 回调运行在 WSLC 内部 I/O 处理路径上长时间阻塞会卡住整个输出管道。先设置后启动本函数必须在进程启动前调用启动后再修改无效。清空时 context 必须为 NULL否则参数校验返回E_INVALIDARG。结构体保持平凡不要往WslcProcessCallbacks中塞入带析构的 C 对象它必须保持 trivial 以通过编译期断言。关联 API 导航进程设置初始化WslcInitProcessSettings进程设置配置WslcSetProcessSettingsCmdLine · WslcSetProcessSettingsEnvVariables · WslcSetProcessSettingsWorkingDirectory进程运行期查询WslcGetProcessPid · WslcGetProcessState · WslcGetProcessExitCode · WslcGetProcessExitEvent · WslcSignalProcess进程 I/O 与资源释放WslcGetProcessIOHandle · WslcReleaseProcess全部 C 进程 API 总览Process APIs 索引【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考