WSLC-Neofetch:用 C++/WinRT 编写原生 Windows 可执行文件,在 WSL 容器中运行 Linux 命令
2026/9/11 8:35:56 网站建设 项目流程

WSLC-Neofetch:用 C++/WinRT 编写原生 Windows 可执行文件,在 WSL 容器中运行 Linux 命令

【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL

导读

WSLC-Neofetch 是 WSL 仓库中 doc/samples 目录下的一个官方示例,演示如何用C++/WinRT 投影编写一个原生 Windows 可执行文件(neofetch.exe),通过WSL Container API(Microsoft.WSL.Containers启动一个轻量级 WSL 容器、在容器内运行 Linux 的neofetch命令,并把输出实时流式回传到 Windows 终端。读完本文,你将掌握 WSL 容器 SDK 的核心生命周期(会话 → 拉镜像 → 建容器 → 执行进程 → 清理)、C++/WinRT 回调驱动的 I/O 流式传输写法,以及如何像本示例一样把命令行参数原样转发进容器,做出"一个 .exe 就是一条 Linux 命令"的实战效果。


一、示例背景:从 Windows 调用 Linux 命令的新姿势

传统上,在 Windows 上运行 Linux 命令要么使用 WSL 发行版(wsl.exe+ 发行版环境),要么依赖 Docker Desktop。WSLC-Neofetch 展示的是另一条路径:WSL Container API(WSLC)——由Microsoft.WSL.ContainersSDK 提供的一套原生编程接口,让任何 Windows 原生程序(C/C++、C#)直接创建和管理运行在 WSL 内部的 Linux 容器。

本示例的核心特性(源自 WSLC-Neofetch README):

  • 运行neofetch.exe会启动一个轻量级 WSL 容器,在其中运行neofetch,并将输出流式回传到你的终端;
  • 命令行参数会被完整转发,因此neofetch.exe --help的效果与 Linux 下的neofetch --help完全一致;
  • 进度消息(以[wslc] ...前缀出现)写入stderr,因此对 stdout 做管道重定向是安全的,不会污染命令输出。

配套的四个示例(见 doc/samples/README.md)覆盖了不同语言栈:

示例语言说明
WSLC-HelloWorldC最小示例,在alpine容器中运行echo并打印输出
WSLC-NeofetchC++/WinRT在原生 Windows.exe中运行 Linuxneofetch命令
WSLC-NextCloudC#在容器中运行 Nextcloud 服务,暴露于http://localhost:8080
WSLC-CustomContainerC# CLI用自定义 Containerfile 构建镜像(F5 自动构建),在终端生成可扫描的二维码

其中 WSLC-HelloWorld 使用扁平 C API(wslcsdk.h),而本示例刻意选择了C++/WinRT 投影winrt/Microsoft.WSL.Containers.h),是学习现代投影写法的理想范本。


二、构建:Visual Studio 与命令行两种方式

README 给出了两种构建方式(前提是 x64):

方式一:Visual Studio打开WSLCNeofetch.sln解决方案,直接以 x64 平台构建即可。

方式二:开发者命令提示符

nuget restore WSLCNeofetch.sln msbuild WSLCNeofetch.sln /p:Configuration=Debug /p:Platform=x64

构建产物位于x64\Debug\neofetch.exe

依赖包:NuGet 解析结果

本示例的 NuGet 依赖在 packages.config 中声明:

<packages> <package id="Microsoft.Windows.CppWinRT" version="3.0.260520.1" targetFramework="native" /> <package id="Microsoft.WSL.Containers" version="2.9.3" targetFramework="native" /> </packages>
  • Microsoft.Windows.CppWinRT:C++/WinRT 工具链,负责从 WinMD 元数据生成投影头文件;
  • Microsoft.WSL.Containers:WSLC SDK 本体,其文档位于 nuget/Microsoft.WSL.Containers/docs/README.MD,其中说明:包会自动配置 include 目录与链接库(wslcsdk.lib)、把运行时 DLLwslcsdk.dll复制到输出目录,并注入激活清单(manifest),使RoGetActivationFactory无需 COM 注册即可将 WinRT 类解析到wslcsdk.dll

前提条件:需要安装 WSL(wsl --install --no-distribution,它同时提供wslcCLI),且该 SDK 目前仍处于Preview阶段(wslcsdk.h 明确声明 API 可能发生破坏性变更,不应用于生产负载)。


三、运行:一个 .exe 就是一条 Linux 命令

x64\Debug\neofetch.exe # 显示系统信息 x64\Debug\neofetch.exe --help # 参数被转发给 neofetch

第一行直接展示系统信息;第二行把--help转发到容器内的neofetch,输出 Linux 原生帮助文本。由于[wslc]进度消息全部走 stderr,你可以安全地执行:

x64\Debug\neofetch.exe | findstr /i "OS"

stdout 中只包含neofetch本身的输出,不会混入 SDK 日志。


四、源码逐段拆解:C++/WinRT 版本的完整生命周期

核心实现位于 neofetch.cpp,整体流程与 C 版 helloworld.c 一一对应,可对照阅读。下面按执行顺序拆解。

4.1 头文件与命名空间

#include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.Collections.h> #include <winrt/Microsoft.WSL.Containers.h> using namespace winrt; using namespace winrt::Microsoft::WSL::Containers;

winrt/Microsoft.WSL.Containers.hMicrosoft.WSL.Containers包的 C++/WinRT 投影头文件。SDK 的 WinRT 激活类清单(见 Microsoft.WSL.Containers.manifest)列出了SessionSessionSettingsContainerContainerSettingsProcessProcessSettingsPullImageOptions等可激活类,全部通过wslcsdk.dll提供。

4.2 常量与工具函数

constexpr std::wstring_view c_imageName = L"anrginit/ubuntu-neofetch:1.0"; void WriteToConsole(FILE* stream, array_view<uint8_t const> data) { fprintf(stream, "%.*s", static_cast<int>(data.size()), reinterpret_cast<const char*>(data.data())); fflush(stream); } std::wstring GetStoragePath() { wchar_t exePath[MAX_PATH]; GetModuleFileNameW(nullptr, exePath, MAX_PATH); wchar_t* lastSlash = wcsrchr(exePath, L'\\'); if (lastSlash != nullptr) *(lastSlash + 1) = L'\0'; return std::wstring{exePath} + L"WslcStorage"; }
  • 镜像固定为anrginit/ubuntu-neofetch:1.0(内含neofetch的 Ubuntu 镜像);
  • WriteToConsole把容器 stdout/stderr 的数据块直接写到 Windows 控制台流并立即刷新;
  • GetStoragePath在可执行文件旁生成WslcStorage目录作为会话存储路径——刻意不依赖任何硬编码绝对路径。这与 C 版 helloworld.c 的GetStoragePath思路完全一致。

4.3 参数转发:argv[0] 替换为 "neofetch"

int wmain(int argc, wchar_t* argv[]) { init_apartment(); std::vector<hstring> commandLine{L"neofetch"}; for (int i = 1; i < argc; ++i) { commandLine.emplace_back(argv[i]); } ...

关键设计:argv[0]neofetch.exe自身)被替换成 Linux 侧的neofetch,其余参数原样透传。这是"neofetch.exe --help等价于neofetch --help"的实现基础。init_apartment()初始化 C++/WinRT 的线程模型(默认多线程单元)。

4.4 第一步:创建并启动会话(Session)

SessionSettings sessionSettings{L"WSLCNeofetch", GetStoragePath()}; sessionSettings.CpuCount(4); sessionSettings.MemorySizeInMB(2048); Session session{sessionSettings}; session.Start();

Session对应一个 WSL 容器"虚拟机"租期,承载其下所有容器。SessionSettings构造函数接收会话名与存储路径;CpuCount(4)MemorySizeInMB(2048)分别限定 4 个 CPU 与 2 GB 内存。在扁平 C API 中,这两个选项对应 wslcsdk.h 的WslcSetSessionSettingsCpuCountWslcSetSessionSettingsMemory,此外 C API 还暴露了WslcSetSessionSettingsTimeout(会话超时,毫秒)、WslcSetSessionSettingsVhd(自定义 VHD 要求,支持动态/固定分配,见WslcVhdType)与WslcSetSessionSettingsFeatureFlags(如 GPU 加速WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU)等可选设置。

4.5 第二步:拉取镜像(PullImage)

session.PullImage(PullImageOptions{hstring{c_imageName}});

从注册表拉取anrginit/ubuntu-neofetch:1.0到本地。C API 对应的 WslcPullSessionImage 支持progressCallback上报分层拉取进度(Pulling fs layer/Downloading/Verifying Checksum/Extracting/Pull complete,见WslcImageProgressStatus枚举),并支持registryAuth认证。若后续想避免每次联网拉取,C API 还提供了WslcImportSessionImage/WslcLoadSessionImage(从 tar/文件导入镜像,见 wslcsdk.h)。

4.6 第三步:创建并启动容器(Container)

ProcessSettings initProcess; initProcess.CommandLine(single_threaded_vector<hstring>({L"/bin/sleep", L"60"})); ContainerSettings containerSettings{hstring{c_imageName}}; containerSettings.Name(L"wslc-neofetch"); containerSettings.InitProcess(initProcess); containerSettings.EnableAutoRemove(true); Container container = session.CreateContainer(containerSettings); container.Start();

容器由ContainerSettings描述:

  • Name(L"wslc-neofetch"):容器名称;
  • InitProcessinit 进程。这里用/bin/sleep 60让容器保持存活 60 秒,以便后续exec运行neofetch——这是 WSLC 的关键机制:容器内必须有一个常驻 init 进程,进程(Process)是挂在容器之上被CreateProcess出来的;
  • EnableAutoRemove(true):容器退出后自动清理,对应 C API 的WSLC_CONTAINER_FLAG_AUTO_REMOVE标志(wslcsdk.h)。

C API 中ContainerSettings还支持更多可选配置:WslcSetContainerSettingsNetworkingModeNONE隔离 /BRIDGED桥接)、WslcSetContainerSettingsHostName/DomainNameWslcSetContainerSettingsPortMappings(Windows 端口 ↔ 容器端口映射,TCP/UDP)、WslcSetContainerSettingsVolumes(Windows 目录挂载进容器,支持只读)、WslcSetContainerSettingsNamedVolumes以及WSLC_CONTAINER_FLAG_ENABLE_GPU/PRIVILEGED标志等,均可在创建前设置。

4.7 第四步:Exec 进程并流式接收输出(Process)

这是本示例最有代表性的部分——回调驱动的异步 I/O

ProcessSettings processSettings; processSettings.CommandLine(single_threaded_vector<hstring>(std::move(commandLine))); processSettings.OutputMode(ProcessOutputMode::Event); Process process = container.CreateProcess(processSettings); handle exitEvent{CreateEvent(nullptr, TRUE, FALSE, nullptr)}; int32_t exitCode = -1; process.OutputReceived([](array_view<uint8_t const> data) { WriteToConsole(stdout, data); }); process.ErrorReceived([](array_view<uint8_t const> data) { WriteToConsole(stderr, data); }); process.Exited(& { exitCode = code; if (!SetEvent(exitEvent.get())) fwprintf(stderr, L"[wslc] Warning: SetEvent failed (0x%08X)\n", GetLastError()); }); process.Start(); DWORD waitResult = WaitForSingleObject(exitEvent.get(), 30000); if (waitResult == WAIT_TIMEOUT) fwprintf(stderr, L"[wslc] Error: Timed out waiting for the process to exit.\n"); else if (waitResult != WAIT_OBJECT_0) throw_last_error();
  • OutputMode(ProcessOutputMode::Event):将输出模式切换为事件回调,配合OutputReceived/ErrorReceived两个 lambda 把容器 stdout/stderr 数据块即时转发到 Windows 控制台;
  • Exited回调记录退出码并触发exitEvent,主线程用WaitForSingleObject等待 30 秒超时;
  • C API 的对应物是 WslcProcessCallbacks 中的onStdOut/onStdErr/onExit三件套(注意头文件注释:缓冲区由 WSLC 持有、仅在回调期间有效,需要保留数据必须自行拷贝;且回调需尽快返回,否则会阻塞 SDK 内部 I/O 处理)。WslcSetProcessSettingsCmdLine/WslcSetProcessSettingsEnvVariables还可设置工作目录、环境变量等。

4.8 第五步:清理与错误处理

container.Stop(Signal::SIGTERM, std::chrono::seconds{5}); session.Terminate();
  • container.Stop(Signal::SIGTERM, 5s):以 SIGTERM 信号优雅停止容器,超时 5 秒。C API 的WslcSignal枚举(wslcsdk.h)定义了SIGHUP(1)、SIGINT(2)、SIGQUIT(3)、SIGKILL(9)、SIGTERM(15);
  • session.Terminate():结束整个会话(虚拟机租期);
  • 外层try/catch (hresult_error const& ex)统一捕获 WinRT 错误,打印 HRESULT 码(0x%08X)并返回 1。C 版则通过goto cleanup逐级释放WslcReleaseProcessWslcStopContainerWslcTerminateSession等句柄。

4.9 完整生命周期全景

阶段C++/WinRT(本示例)扁平 C API(helloworld.c)
会话SessionSettings+Session::Start()WslcInitSessionSettingsWslcCreateSession
拉镜像session.PullImage(...)WslcPullSessionImage
建容器ContainerSettings+CreateContainerWslcInitContainerSettingsWslcCreateContainer
启容器container.Start()WslcStartContainer
跑进程ProcessSettings+CreateProcessWslcInitProcessSettingsWslcCreateContainerProcess
收输出OutputReceived/ErrorReceived/Exited委托onStdOut/onStdErr/onExit回调
清理Stop(SIGTERM)Terminate()WslcStopContainerWslcTerminateSession

五、扩展讨论:如何把这个模式推广到任意 Linux 命令

WSLC-Neofetch 的可贵之处在于它几乎不包含任何与neofetch强绑定的逻辑——commandLine只是把argv替换后整体转发。这意味着只需修改commandLine向量与镜像,即可把同一套模板套用到任意 Linux CLI:

  • 换命令:把commandLine首元素从L"neofetch"改为L"htop"L"lazygit"等,并换成包含该工具的镜像;
  • 拉长命令/bin/sleep 60的 init 进程超时可根据任务耗时调整(若命令超时,可考虑给 init 更长的存活时间或改用更合适的机制);
  • 改用本地镜像:若不想每次联网拉取,可用 WSLC-CustomContainer 演示的构建集成——<WslcImage>MSBuild 项在构建期自动执行wslc image build+wslc image save生成 tar,再通过 SDK 的导入/加载接口(WslcImportSessionImageFromFile/WslcLoadSessionImageFromFile)从本地 tar 载入,无注册表拉取步骤;
  • 跨语言对照:想理解扁平 C API 的同学可对照 WSLC-HelloWorld,两份代码的生命周期与回调语义几乎一一对应。

值得注意:该 SDK 处于预览期(wslcsdk.h 与 Microsoft.WSL.Containers 文档 均给出 Preview 警告),用于生产负载前需评估 API 稳定性风险。


六、小结

WSLC-Neofetch 以不足 140 行的 C++/WinRT 代码,完整展示了 WSL Container API 的五段生命周期:会话(Session)→ 拉镜像(PullImage)→ 建容器(Container)→ 执行进程(Process)→ 优雅清理。它的核心工程经验有三条:

  1. 参数转发:把argv[0]替换为容器内命令名、其余参数原样透传,即可获得"原生 .exe 即 Linux 命令"的体验;
  2. 回调流式输出OutputReceived/ErrorReceived/Exited委托让容器 I/O 与 Windows 控制台无缝衔接,且进度日志走 stderr、业务输出走 stdout,天然适合管道;
  3. 零硬编码路径:存储目录基于可执行文件位置动态生成(WslcStorage),示例开箱即用、便于分发。

如果你正准备为 Windows 桌面应用集成 Linux 工具链,或想学习Microsoft.WSL.ContainersSDK 的 C++/WinRT 投影写法,这个示例是绝佳的起点。更完整的 API 面(GPU 加速、端口映射、卷挂载、镜像导入导出、注册表认证等)可进一步查阅 wslcsdk.h 与 Microsoft.WSL.Containers 包文档。

【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询