TauriCPP 基于 WebView2 的轻量级 C++ 桌面应用框架

TauriCPP

基于 WebView2 的轻量级 C++ 桌面应用框架,灵感来自 Tauri。使用 Web 前端 + C++ 后端构建现代桌面应用------单文件部署,源码不泄露。

特性

  • 单 EXE 部署 --- WebView2Loader.dll 和所有前端资源均嵌入可执行文件,无需任何外部文件。
  • 源码保护 --- 前端资源(HTML/CSS/JS)编译时作为 Windows 资源嵌入 exe,运行时通过 VirtualFS 从内存提供,不生成临时文件。
  • 双向通信 --- JS 调用 C++ 命令(Promise 风格),C++ 向 JS 推送事件。
  • 开发/生产双模式 --- 开发时自动从文件系统加载,生产时从嵌入资源加载,无需修改代码。
  • 极简依赖 --- 仅需 WebView2 SDK 和 nlohmann/json,不捆绑 Chromium,不依赖 .NET 运行时,不依赖 Qt。
  • C++17 --- 现代 C++,API 简洁清晰。

快速开始

环境要求

  • Visual Studio 2019+(需安装 C++ 桌面开发工作负载)
  • vcpkg(classic 模式,安装在 C:\vcpkg)
  • Python 3.7+(用于资源打包脚本)
  • PowerShell 5.1+

安装依赖(首次使用)

.\build.ps1 -SetupDeps

此命令将:

  1. 下载 nlohmann/json.hpp 到 third_party/ 目录
  2. 通过 vcpkg 安装 webview2(vcpkg install webview2:x64-windows)

构建

.\build.ps1 # 构建(Release 配置)

.\build.ps1 -Clean # 清理构建目录并重新构建

输出:build\sample.exe

运行

.\build\sample.exe

项目结构

TauriCPP/

├── CMakeLists.txt # CMake 构建配置

├── build.ps1 # PowerShell 构建脚本(Ninja + MSVC)

├── vcpkg.json # vcpkg 依赖清单

├── include/tauricpp/

│ ├── app.hpp # 应用入口

│ ├── bridge.hpp # 前后端通信桥

│ ├── window.hpp # Win32 + WebView2 窗口

│ ├── virtual_fs.hpp # 内存虚拟文件系统

│ └── embedded_dll.hpp # 嵌入式 DLL 加载器(delay-load 钩子)

├── src/

│ ├── app.cpp

│ ├── bridge.cpp # 含注入前端的 JS 桥接代码

│ ├── window.cpp # WebView2 初始化与资源拦截

│ ├── virtual_fs.cpp

│ └── embedded_dll.cpp # delay-load 钩子(__pfnDliNotifyHook2)

├── sample/

│ ├── src/main.cpp # 示例应用

│ └── frontend/

│ ├── index.html

│ ├── css/style.css

│ └── js/app.js

├── tools/

│ └── pack_resources.py # 前端文件 -> .rc 资源打包工具

└── third_party/

└── nlohmann/json.hpp # JSON 库(单头文件)

使用方法

1. 创建应用

#include "tauricpp/app.hpp"

#include "tauricpp/embedded_dll.hpp"

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {

// 从 exe 资源段加载嵌入的 WebView2Loader.dll

tauricpp::EmbeddedDll::Load("1", "EMBEDDED_DLL", "WebView2Loader.dll");

// 配置应用

tauricpp::App::Config config;

config.window_config.title = "我的应用";

config.window_config.width = 1200;

config.window_config.height = 800;

config.window_config.center = true;

tauricpp::App app(config);

// 注册前端可调用的命令

app.GetBridge().RegisterCommand("greet", \[\](const nlohmann::json& args) {

std::string name = args.value("name", "World");

return nlohmann::json{{"message", "你好, " + name + "!"}};

});

// 向前端推送事件

app.GetBridge().Emit("timer", nlohmann::json{{"tick", 1}});

return app.Run();

}

2. 前端 JavaScript API

// JS -> C++:调用命令(返回 Promise)

const result = await tauricpp.invoke('greet', { name: 'World' });

// result: { "message": "你好, World!" }

// C++ -> JS:监听事件

tauricpp.listen('timer', function(data) {

console.log('定时器:', data);

});

// 移除监听

tauricpp.removeListener('timer', callback);

3. 将前端打包进 EXE

sample/frontend/ 目录中的前端文件在构建时由 tools/pack_resources.py 自动打包为 Windows 资源脚本并编译进 exe。运行时,VirtualFS 从 exe 资源段加载文件,通过 WebResourceRequested 拦截请求从内存提供内容------不写入任何临时文件到磁盘。

架构

┌─────────────────────────────────────────────────┐

│ sample.exe │

│ │

│ ┌──────────┐ ┌───────────┐ ┌──────────────┐ │

│ │ App │ │ Bridge │ │ Window │ │

│ │ │──│ │──│ │ │

│ │ Config │ │ Register │ │ Win32 + │ │

│ │ Run() │ │ Emit() │ │ WebView2 │ │

│ └──────────┘ └───────────┘ └──────┬───────┘ │

│ │ │

│ ┌──────────────┐ ┌─────────────────┐│ │

│ │ VirtualFS │ │ EmbeddedDll ││ │

│ │ │ │ ││ │

│ │ RegisterFile │ │ 从资源段加载 ││ │

│ │ FindFile │ │ delay-load 钩子 ││ │

│ └──────┬───────┘ └─────────────────┘│ │

│ │ │ │

│ ┌──────▼──────────────────────────────▼───────┐ │

│ │ Windows 资源段 │ │

│ │ ┌─────────────┐ ┌──────────────────────┐ │ │

│ │ │ WebView2 │ │ 前端资源 │ │ │

│ │ │ Loader.dll │ │ (HTML/CSS/JS/...) │ │ │

│ │ └─────────────┘ └──────────────────────┘ │ │

│ └─────────────────────────────────────────────┘ │

└─────────────────────────────────────────────────┘

运行机制

  1. 构建时:pack_resources.py 扫描前端目录,生成带数字 ID 的 .rc 资源脚本,与 WebView2Loader.dll 一起编译进 exe。
  2. 运行时 - DLL 加载:EmbeddedDll::Load() 从 exe 资源段提取 WebView2Loader.dll 写入临时文件,调用 LoadLibraryW() 加载后立即删除临时文件。__pfnDliNotifyHook2 delay-load 钩子拦截 MSVC 的延迟加载机制,直接返回已加载的模块句柄。
  3. 运行时 - 资源提供:SetVirtualHostNameToFolderMapping 将 tauricpp.app 映射为虚拟主机名。WebResourceRequested 拦截所有 https://tauricpp.app/\* 请求,从 VirtualFS(内存中)提供内容,自动设置 MIME 类型、CORS 和缓存头。
  4. 运行时 - 通信:AddScriptToExecuteOnDocumentCreated 在页面脚本执行前注入桥接 JS。postMessage / WebMessageReceived 处理 JS→C++ 通信,ExecuteScript 处理 C++→JS 通信。

对比

|-----------|-----------------------|-----------------|--------------------|----------------|-----------------|
| | TauriCPP | Electron | Tauri | WPF | Core UI |
| 后端语言 | C++ | JS/TS | Rust | C#/.NET | C/JS(QuickJS) |
| 渲染引擎 | WebView2 (Edge) | Chromium | WebView2/WebKitGTK | DirectX (.NET) | Direct2D/D3D11 |
| 前端技术 | 任意 Web 框架 | 任意 Web 框架 | 任意 Web 框架 | XAML | .uix(类 Vue SFC) |
| 二进制体积 | ~1-10 MB | 100+ MB | 3-10 MB | 需 .NET 运行时 | ~3 MB DLL |
| 内存占用 | 50-80 MB | 150+ MB | 30-80 MB | 80+ MB | < 30 MB |
| 启动速度 | < 500ms | 1-3s | < 500ms | 0.5-1s | < 200ms |
| 源码保护 | 是(嵌入 exe,内存提供) | 否(asar 可解包) | 有限 | 否(IL 可反编译) | 是(编译为二进制) |
| 单文件部署 | 是 | 否 | 是(打包后) | 否 | 是(单个 DLL) |
| 跨平台 | 仅 Windows | Win/macOS/Linux | Win/macOS/Linux | 仅 Windows | 仅 Windows |
| 运行时依赖 | WebView2(Win10/11 预装) | 捆绑 Chromium | WebView2/WebKitGTK | .NET 运行时 | 无 |
| 后端生态 | C++ 原生库 | npm 生态 | Rust crates 生态 | NuGet 生态 | C ABI,任意语言 |

TauriCPP vs Electron

Electron 捆绑整个 Chromium 引擎,二进制体积 100+ MB,内存占用 150+ MB。TauriCPP 使用系统自带的 WebView2(Windows 10/11 已预装),二进制体积控制在 10 MB 以内。前端源码嵌入 exe 并从内存提供------不像 asar 归档可以被解包提取。

TauriCPP vs Tauri

Tauri 是最相似的框架,但需要 Rust 语言。TauriCPP 使用 C++,可直接调用 Windows API 和现有 C++ 代码库,无需 FFI 开销。两者在 Windows 上都使用 WebView2,二进制体积相近。Tauri 支持跨平台(macOS/Linux 通过 WebKitGTK),TauriCPP 仅支持 Windows,但构建工具链更简单。

TauriCPP vs WPF

WPF 需要 .NET 运行时,使用 XAML 定义 UI。TauriCPP 使用标准 Web 技术(HTML/CSS/JS),可利用庞大的 Web 生态,前端团队技能可直接复用。WPF 应用的 IL 可被反编译,TauriCPP 将前端资源嵌入原生二进制,更难提取。

TauriCPP vs Core UI

Core UI 是一个原生渲染框架,使用 Direct2D 渲染和自定义 .uix 组件格式 + QuickJS 引擎------极其轻量(3 MB,< 30 MB 内存)。但它使用自定义 UI 框架,CSS 支持有限,控件集较小。TauriCPP 使用完整的 Web 平台(任意 CSS、任意 JS 框架、完整 DOM),可访问整个 npm 生态。代价是内存占用较高(WebView2 开销),但 UI 灵活性和开发者熟悉度大幅提升。

API 参考

App

tauricpp::App::Config config;

config.window_config.title = "我的应用";

config.window_config.width = 1024;

config.window_config.height = 768;

config.window_config.center = true;

config.window_config.resizable = true;

config.window_config.start_url = "https://tauricpp.app/index.html";

tauricpp::App app(config);

app.OnSetup(\[\](tauricpp::App& app) { /* 消息循环前调用 */ });

int exitCode = app.Run();

Bridge

auto& bridge = app.GetBridge();

// 注册命令(前端通过 tauricpp.invoke('cmd', args) 调用)

bridge.RegisterCommand("cmd_name", \[\](const nlohmann::json& args) -> nlohmann::json {

return {{"result", "ok"}};

});

// 向前端推送事件(前端通过 tauricpp.listen('event', cb) 监听)

bridge.Emit("event_name", nlohmann::json{{"key", "value"}});

Window

auto& window = app.GetWindow();

window.ExecuteJs("console.log('Hello from C++')");

HWND hwnd = window.GetHwnd();

VirtualFS

auto& vfs = app.GetFS();

// 注册文件到虚拟文件系统

vfs.RegisterFile("/path/to/file.html", "<h1>Hello</h1>", "text/html");

// 查找文件

tauricpp::VirtualFS::VFile file;

if (vfs.FindFile("/path/to/file.html", file)) {

// file.data: std::vector<uint8_t>

// file.mime_type: std::string

}

构建选项

.\build.ps1 # 构建(Release,Ninja + MSVC)

.\build.ps1 -Clean # 清理并重新构建

.\build.ps1 -SetupDeps # 安装依赖(仅首次使用)

系统要求

  • Windows 10/11(WebView2 运行时,大多数系统已预装)
  • Visual Studio 2019+(需安装 C++ 桌面开发工作负载)
  • CMake 3.20+
  • Ninja(随 VS C++ 工作负载安装)
  • vcpkg(classic 模式)
  • Python 3.7+

许可证

MIT

传送门

masonwu21/TauriCPP

相关推荐
Johnstons1 小时前
如何精确模拟网络丢包进行测试?实测指南
开发语言·网络·php·网络测试·网络损伤·弱网模拟
枕星而眠1 小时前
Linux IO多路复用:select、poll、epoll 核心原理与进阶实战
linux·运维·服务器·c++·后端
csdn_aspnet1 小时前
Python 霍尔分区算法(Hoare‘s Partition Algorithm)
开发语言·python·算法
jjjlike1 小时前
三款主流AI编程工具实测对比:以C++自研UI引擎项目为测评基准
c++·ai编程
程序喵大人1 小时前
C++与C语言的核心区别是啥
c语言·开发语言·c++
jjjava2.01 小时前
全面拆解 Java 锁:分类辨析 + 底层原理精讲
java·开发语言
曹牧1 小时前
Java:import NeverUsed
java·开发语言·log4j
飞天狗1111 小时前
2024第十五届蓝桥杯c/c++B组国赛题解
c语言·数据结构·c++·算法·蓝桥杯