# Keilbox **Repository Path**: ronxia/keilbox ## Basic Information - **Project Name**: Keilbox - **Description**: Keilbox 是一个面向 Keil 开发场景的 Web 小工具执行器。 它本身不内置一堆功能,而是从云端拉取 HTML 代码,在本地窗口中直接运行。CRC 计算器、进制转换器、串口工具、时间戳转换……这些零散但常用的小工具,都可以作为一份 HTML 放在云端,由 Keilbox 统一加载执行。 工具在云端更新,在本地运行。 - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2026-11-01 - **Last Updated**: 2026-11-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # KeilBox 项目 README > Keil 工具箱:一个把自定义工具集成到 Keil µVision **Tools 菜单**的安装器 + 运行时组合方案。 > 安装器负责"注册菜单项",运行时(KeilBox.exe)负责"解析配置、下载、缓存、渲染 HTML / 启动 EXE"。 --- ## 一、项目结构 | 文件 | 角色 | 说明 | | --- | --- | --- | | `KeilboxInstaller.cpp` | **安装器**(生成 `ToolInstaller.exe`) | 通过注册表把工具写入 Keil 的 Tools 菜单 | | `keilbox.cpp` | **运行时**(生成 `KeilBox.exe`) | 被 Tools 菜单调起,按 `run.txt` 配置下载/缓存/展示 | | `tools.json` | **工具清单**(远端) | 安装器拉取,展示可选工具列表 | | `run.txt` | **单工具配置**(远端/本地) | 运行时拉取,决定具体行为 | 部署时通常把 `KeilBox.exe` 与安装器放在同一目录,安装器会自动探测 `KeilBox.exe`(缺失时回退 `HtmlViewer.exe`)。 --- ## 二、功能 ### 2.1 安装器(ToolInstaller.exe) - **自动定位 Keil** - 依次查询注册表:`HKLM\SOFTWARE\Keil\Products\{MDK,C51}`(含 WOW6432Node)。 - 兜底扫描常见目录 `C:\Keil_v5`、`D:\Keil` 等。 - 再兜底从 `PATH` 环境变量里找 `UV4.exe`。 - 均失败时弹出文件夹选择框手动指定(校验 `TOOLS.INI` 或 `UV4\UV4.exe`)。 - **拉取工具清单** - 从可编辑的 URL 下载 `tools.json`(默认托管在腾讯云 COS)。 - 极简 JSON 解析,只认字段:`name` / `desc` / `run`。 - **勾选式安装** - ListView 复选,支持全选 / 反选。 - 同名工具自动跳过(幂等安装,可重复运行)。 - 空槽位优先复用,避免注册表项无限膨胀。 - **菜单落点** - 写入 `HKCU\Software\Keil\µVision5\ToolM`(并兼容乱码键名 `礦ision5` 与 `µVision4`)。 - 每工具写入:`Mtx{n}`(名称)、`Mex{n}`(程序路径)、`Mag{n}`(参数)、`Mid{n}`(工作目录)、`Mfg{n}`(标志)。 - 程序路径固定为同目录的 `KeilBox.exe`,参数为 `run` 字段(通常是 `run.txt` 的 URL)。 - **健壮性** - 对 `run` 结尾做启发式检查(`.txt/.exe/.bat/.cmd/.html/.htm`),异常仅提示、不阻断。 - 全部已安装 / 部分跳过 / 全部成功均有对应提示。 ### 2.2 运行时(KeilBox.exe) **命令行** ``` KeilBox.exe <目标> <目标> 可以是: - https://.../xxx.txt → 在线 run.txt(远程配置 + 下载缓存) - C:\path\xxx.txt → 本地 run.txt - https://.../page → 直接 WebView 打开 - C:\path\xxx.html/.htm → 本地 HTML(UTF-8 规范化 + base 注入后加载) - C:\path\xxx.exe → 直接启动 --current <文件> → 供页面 JS 通过 bridge 读写的"当前文件" --local <目录> → 以虚拟域名 https://appassets.example/ 托管本地目录 其余参数 → 透传给被启动的 EXE ``` **run.txt 格式** ```ini cnname: 我的工具 ver: 1.2.3 url: https://example.com/tool-1.2.3.zip run: index.html ; 相对缓存目录;或 xxx.exe ico: favicon.ico ; 可选,相对缓存目录或绝对路径 win: 1000,700 ; 可选,初始窗口大小 ``` - 支持 `:` 或 `=` 分隔,忽略 `#;[]` 开头的行,字段名大小写不敏感。 - 必填:`ver` / `url` / `run`。 **缓存策略(关键)** ``` %LOCALAPPDATA%\KeilBox\cache\<工具名>\ <工具名>.txt ← 根副本(最新一次成功运行的内容) <版本号>\ run.txt ← 版本级副本 ...解压后的全部文件... ``` - 以 `run.txt` 的**原始字节**做全文比较,完全一致才算命中缓存。 - 命中 → 直接用;未命中 → 删除该版本目录 → 重新下载 ZIP → 解压。 - 网络失败 / 解析失败时会回退到本地缓存中**版本号最大**的一份。 - 版本号按"逐段数值"比较(`1.10 > 1.9`),不是字符串比较。 **WebView2 宿主** - 依赖 **WebView2 Runtime**(缺失弹提示)。 - UserDataFolder:`%LOCALAPPDATA%\KeilBox\WebView2`。 - `--local ` 时通过 `SetVirtualHostNameToFolderMapping` 把本地目录映射到 `https://appassets.example/`,规避 `file://` 跨域限制。 - 本地 HTML 加载前会:BOM/GBK → UTF-8 规范化、缺失则补 ``、注入 `` 修正相对资源。 - 右键菜单:**置顶 / 刷新 / 复制页面地址 / 复制页面源码**;`Ctrl+T` 切换置顶。 - 页面内 `` 注释可覆盖初始窗口大小。 - 标题自动拼接:`名称 [v版本 · 本地缓存/在线下载]`。 **JS Bridge(页面 ↔ 宿主)** 页面通过 `window.chrome.webview.postMessage()` 发送 JSON,宿主以 JSON 回复,消息形如: ```json { "id": "任意", "method": "ping|readCurrent|writeCurrent|closeWindow", "params": { ... } } ``` | method | 参数 | 返回/作用 | | --- | --- | --- | | `ping` | — | `pong` + `version` + `currentFile` | | `readCurrent` | — | 文件内容(自动识别 UTF-8/BOM/GBK),含 `encoding`、`bom`、`bytes` | | `writeCurrent` | `content`, `backup`(默认 true) | 按读取时的编码回写;`backup=true` 生成 `.bak` | | `closeWindow` | — | 关闭宿主窗口 | 安全约束:仅当页面来源是 `https://appassets.example/` 或 `file:///` 时 bridge 才启用。 --- ## 三、实用指南 ### 3.1 快速上手 1. 把 `KeilBox.exe`(或兼容名 `HtmlViewer.exe`)和 `ToolInstaller.exe` 放在同一目录。 2. 运行 `ToolInstaller.exe` → 确认 Keil 目录 → 点"下载列表"。 3. 勾选需要的工具 → "安装到 Keil"。 4. **完全退出并重启 Keil**,`Tools` 菜单即可看到新条目。 ### 3.2 添加一个自己的工具 **方式 A:只挂一个网页/本地页(最简单)** 直接注册一个 `Mag` 指向 URL 或本地 HTML: ``` KeilBox.exe https://你的站点/工具页 KeilBox.exe C:\MyTools\panel.html ``` **方式 B:走 run.txt(可远程更新 + 缓存)** 1. 打包工具为 `tool.zip`(zip 内根目录放 `index.html` 或 `tool.exe`)。 2. 上传 zip,然后写一份 `run.txt` 传上去: ```ini cnname: CRC 计算器 ver: 1.0.0 url: https://你的站点/crc-1.0.0.zip run: index.html ``` 3. 在 `tools.json` 的 `tools` 数组里追加: ```json { "name": "CRC 计算器", "desc": "16/32 位 CRC 计算", "run": "https://你的站点/crc.txt" } ``` 4. 升级时:改 zip、改 `ver`、改 `url`(或保持 URL 不变只改 ver),用户下次点击即自动重下,旧版本仍可离线回退。 ### 3.3 在页面里读写"当前工程文件" 安装器写入的 `Mag` 可以带上 `--current "工程文件路径"`,例如配合 Keil 的 `%f`(当前文件)宏: 页面里即可这样用: ```js const wv = window.chrome?.webview; function rpc(method, params = {}) { return new Promise((resolve, reject) => { const id = Math.random().toString(36).slice(2); const handler = (e) => { const msg = e.data; if (msg.id !== id) return; wv.removeEventListener('message', handler); msg.error ? reject(new Error(msg.error.message)) : resolve(msg.result); }; wv.addEventListener('message', handler); wv.postMessage({ id, method, params }); }); } // 读取当前文件(自动识别编码) const { content, encoding } = await rpc('readCurrent'); // 回写(自动生成 .bak) await rpc('writeCurrent', { content: content + '\n// edited', backup: true }); ``` ### 3.4 常见问题排查 | 现象 | 排查 | | --- | --- | | 菜单里没出现新工具 | 是否**完全**退出 Keil 再开;检查 `HKCU\Software\Keil\µVision5\ToolM` 是否有 `Mtx*` | | 提示"未找到 KeilBox.exe" | 运行时和安装器是否同目录;或改名为 `HtmlViewer.exe` | | 首次运行白屏 | 是否安装 WebView2 Runtime;`%LOCALAPPDATA%\KeilBox\WebView2` 是否可写 | | 页面资源 404 | 本地 HTML 相对路径会被 `` 修正;`--local` 模式请确保入口是 `index.html` | | 断网时无法启动 | 是否曾有成功运行历史(有缓存即可离线);缓存目录在 `%LOCALAPPDATA%\KeilBox\cache` | | 中文乱码 | HTML 保存为 UTF-8;run.txt 保存为 UTF-8(BOM 可有可无) | ### 3.5 手动清理 - 卸载工具:删除 `ToolM` 下对应的 `Mtx/Mex/Mag/Mid/Mfg{n}` 五件套即可。 - 清缓存:删除 `%LOCALAPPDATA%\KeilBox\cache`。 - 彻底卸载:删除上述注册表项 + 两个 `%LOCALAPPDATA%\KeilBox` 子目录。 --- ## 四、扩展开发 ### 4.1 编译环境 - **VS2022 / x64**,`Win32` 子系统。 - 安装器依赖:`comctl32 / winhttp / shlwapi / advapi32`。 - 运行时依赖:`winhttp / bcrypt / shlwapi / shell32 / ole32` + **WebView2LoaderStatic.lib** + **libzip**(`zip.lib`)。 - 建议启用通用控件 v6 清单以获得现代外观。 ### 4.2 代码结构速览 **安装器** ``` HttpGet → WinHTTP 抓 tools.json JsonGet/ParseTools→ 极简 JSON(只取 name/desc/run) FindKeilDir → 注册表 → 常见路径 → PATH WriteToolToRegistry ChooseToolMKey 决定键路径(µVision5/4,含乱码兼容) GetInstalledToolNames 同名判重 GetUsedIndices / PickFreeIndex 空洞优先分配索引 ``` **运行时** ``` ParseRunTxtContent → run.txt 解析 HttpGetToString → 抓 run.txt / zip ExtractZip → libzip 解压 PrepareCache → 全文比较命中 → 未命中重下 HtmlBridge → JS ↔ 宿主 RPC ShowWebView → WebView2 宿主 + 虚拟域名 + 右键菜单 PrepareLocalHtml → 编码规范化 + 注入 RunFromTxt → 缓存就绪后分派到 HTML / EXE ``` ### 4.3 常见扩展点 **① 支持更多 run.txt 字段** 在 `ParseRunTxtContent` 里加分支即可(它同时供"内存解析"和"文件解析"使用): ```cpp else if (k == L"args") out.args = v; // 给 EXE 追加参数 ``` **② 支持 tar.gz / 7z 包** `ExtractZip` 是唯一解压入口,新增一个 `ExtractArchive`,按扩展名/魔数分派即可,其它流程(缓存、全文比较)无需改动。 **③ 新增 JS Bridge 方法** 在 `HtmlBridge::Handle` 的 `if/else if` 链里加方法,返回 JSON 字符串: ```cpp else if (method == "getProjectPath") { result = "{\"path\":" + MiniJson::Str(W2U(g_currentFile)) + "}"; } ``` 注意:所有错误通过 `throw std::runtime_error("CODE")` 上报,会自动包装为 `{error:{code:"ERR",message:...}}`。 **④ 多环境注册表兼容** `ChooseToolMKey()` 是唯一定位入口。若需支持新版 Keil 命名,新增探测顺序即可,无需改动写入逻辑。 **⑤ 自定义菜单图标 / 标志位** `WriteToolToRegistry` 里 `Mfg` 目前写 `0x00000900`(0x09 = 可执行 + 独立窗口类标志的组合,具体含义随 Keil 版本略有差异)。需要不同行为时调整该 DWORD 即可。 **⑥ tools.json 增加字段** `ParseTools` 已按 `name/desc/run` 取字段;如需 `category`、`icon` 等,加一行 `JsonGet(item, p, "category")` 并在 UI 中新增列。注意 `JsonGet` 用的是"从 `pos` 开始找 key"的线性扫描,字段顺序无关,但同一对象内不要重复 key。 **⑦ 离线优先策略** `PrepareCache` 目前是"在线优先,失败回退"。若想"离线优先、后台更新",可改为:先检查 `verDir` 存在且 `TxtContentSame` 则直接返回,再异步下载新版覆盖。注意保持"根副本同步"这一不变量。 ### 4.4 开发约定(来自现有代码) - 所有内部字符串统一用 `std::wstring`,只在网络/文件/JSON 边界做 UTF-8 转换(`U2W/W2U`)。 - 缓存目录的"根副本 + 版本副本"双写是硬约束,改动 `PrepareCache` 时务必同时维护。 - 注入 ``/`` 时,若页面已存在同名标签则跳过,避免破坏作者原意。 - 任何写文件/写注册表的失败都必须向上返回错误信息(`std::wstring& err`),由 UI 层负责弹窗。 - 未命中重下时先删旧版本目录、再解压,失败则整体回滚删除,保证缓存不会处于半成品状态。 --- ## 五、许可与致谢 - HTTP:WinHTTP(系统自带) - 渲染:Microsoft Edge WebView2 - 解压:libzip - 图标:`LoadImageW` 从文件读取,无需资源编译 > 使用时请确保所分发的工具内容与 `run.txt` / `tools.json` 的 URL 合法合规。