Skip to content

ltool v1.4.1

Choose a tag to compare

@github-actions github-actions released this 22 Oct 11:27
· 16 commits to main since this release

🚀 ltool v1.4.1 发布说明 / Release Notes

🎉 欢迎使用 ltool 1.4.1!本次共 11 笔改动,重点是 build/verify 里 16 个一次性探针、17 处…、文档里那行写死的家目录换成 %USERPROFILE%(发…、「测试全部插件保证没有闪退」这一趟补的两件事。下面每一节是一笔提交,原因和实测数字都在里面。


🌟 本次改动

🐛 1. 方案 A:build/verify 里 16 个一次性探针、17 处真账号换成各语言自己的运行时取法(python expanduser / PS 用 $env:APPDATA 与 GetFolderPath Desktop / node process.env.USERPROFILE / shell $HOME),抹成字面量会把还能用的探针弄坏;顺带把桌面上那个私人夹具名和两份会话转录的绝对路径一起换成占位。根因不是忘改,是 .gitignore 那条只挡 *.sh、对已跟踪文件根本不生效,所以补的是会红的闸(tools/tracked_leak_check.py,扫 git ls-files 573 张,占位白名单在里面)而不是注释。闸自己红过一次:token 正则把反引号顿号一起吃掉,文档里那句「占位(x、zzname)」被判成一个叫 x、zzname 的用户 —— 收窄成只吃账号名字符,反向验塞一颗非白名单的探针仍红 1 条。逐张过语法:py_compile 4、node --check 4、bash -n 1、PowerShell 只解析不执行 7(PS_PARSE_BAD=0,BOM/LF 原样);CI YAML 本地过 js-yaml:jobs=1 steps=25。一次踩坑:那条"用了 expanduser 却没 import os"的检查排在写盘之后,于是 16 张已落地第 17 张红着退出 —— 收尾检查要排在写盘前面。

  • (这一笔没写正文,标题就是全部。)

✅ 2. 文档里那行写死的家目录换成 %USERPROFILE%(发版说明的脱敏闸管的是"生成出来的那份",文档本体一直没人管过)。顺手拿同一颗图案把 git 真带走的每一张文件扫了一遍:src/docs/tools 里剩下的全是占位(x / someone / zzname),但 build/verify 那堆一次性探针里有 16 个文件、17 处带的是真用户名 —— 那个目录 .gitignore 只挡了 *.sh,探针脚本没挡住,所以它们早就在公开仓库里了。这一笔只改了文档那一行,那 16 个怎么处理等他拍。

  • (这一笔没写正文,标题就是全部。)

🐛 3. 「测试全部插件保证没有闪退」这一趟补的两件事:① 「页被删后再拿」从一遍改成三遍 —— 这类生命周期问题和堆怎么复用有关,第一趟的 INFO 就抓到 grid、language 两颗"新造那张正好落在刚释放的地址上"(只跑一遍 + 按地址判就是假红,假红会诱人去改本来对的那半句);② 反向验按新判据重跑:g_pages 退回裸指针 ⇒ 第一趟红 4 条、进程死在半路,改回来 300 条 0 红、5964 ms。离线全套 696 条 0 红(plugintest 300 / storetest 104 / cliptest 88 / startuptest 55 / pathcfg 37 / pinyin 68 / linuxdotest 44)

  • (这一笔没写正文,标题就是全部。)

🐛 4. 闪退那三条不该只钉 base64 一颗:g_pages、壳里那颗 m_page、卡片墙那一次 deleteLater,对 19 颗是同一套代码 —— 现在三趟都按颗数着过(页被单删 / 拆墙后再直达 / 整扇窗关掉后再拿),--plugintest 257→298,反向验把 g_pages 退回裸指针重编=红 5 条后 rc=139

  • (这一笔没写正文,标题就是全部。)

✅ 5. ci_wait.sh 自己挪完家之后那两行没跟上:用法提示还写 build/verify,cd 的 ../.. 会从 tools/ 走到仓库外面去(今天靠绝对 API URL 侥幸没坏)。

  • (这一笔没写正文,标题就是全部。)

🐛 6. 把 ci_wait.sh 从 build/verify 挪进 tools/:.gitignore 里 build/verify/*.sh 是被排除的,那个轮询脚本一直只活在我本地(这次它被一截被掐断的 JSON 弄崩过一次,已改成读不到就下一轮再问 —— 这种东西进不了仓库等于没有)

  • (这一笔没写正文,标题就是全部。)

📝 7. migrate 脚本两处我自己的错:卸载键名猜错(真名是 Inno 那串 GUID)导致不出声地退回写死路径;又给无 BOM 的 .ps1 加了中文注释直接 ParserError

  • 改成按 DisplayName 现搜两棵 hive + 把 target 的来源打进结论行(查不到就明说 HARDCODED FALLBACK);全文件回到纯 ASCII。另记一条:exe 字节里 grep 不到 1.4.x(ANSI/UTF-16 都 0),短字面量在 release 产物里会被拆开 —— 装了哪版看注册表 DisplayVersion 安装包文件名,别看 exe 里那串字。

🐛 8. 这一处钉死了:同一条直达路径在旧写法上必崩(rc=139 段错误),在新写法上 257 条 0 红

  • 他给了两条:每次都会、从搜索框直达进去 —— 路径就收窄到 SearchWindow.cpp:1474
  • openTool(id, extra) → ToolsPage::showTool → PluginHost::page() + feed()。
  • "每次都会"排除了偶发时序,所以那条路必须能进判据:加了 4 条走真链
  • (拿 ToolsPage 直接 showTool("base64", "aGVsbG8="),判输入框 / 结果框 / stack 当前格)。
  • 决定性的一步是反向验:把三处缓存(g_pages 和 base64/json 两份 m_page)退回旧的裸指针写法、
  • 判据一字不动重编 → --plugintest 段错误退出 rc=139,
  • 死在"宿主不会把已释放的页再发一次"报红之后的下一步(拿那块已释放的页去 m_stack->indexOf/addWidget)。
  • 和他事件日志那个"崩在 Qt6Core + 读地址 0x8"同一个形状。改回来 → 257 条 0 红。
  • 所以这次不是"修了一处看着像的",是同一条路旧写法必崩、新写法不崩。
  • 判据自己也被修过一次:findChildren<QPlainTextEdit*>() 的下标不是界面顺序(第 0 个是只读的结果框),
  • 第一版按"0 是输入"写,红两条 —— 红的是判据;现在按角色认(只读 = 结果框)。
  • 总数 --plugintest 250 → 253(页缓存 3 条)→ 257(直达 4 条);离线全套 0 红,
  • plugin_version_gate NEEDS_BUMP=0(这一小步只动 core 判据侧)。
  • 交付:install MATCH B0198AF98C29、19 颗对齐、装好的那份 --plugintest 257 过 / 0 红、relaunch pid=3500。

🐛 9. 「b64 会闪退」:插件页的命不在宿主手里,可两边都存了裸指针(core 1.4.1)

  • 取证两层都走了:事件日志 Id=1001 说 2026-10-22 12:00:17 ltool.exe 真崩,
  • faulting=Qt6Core.dll 6.10.2.0 + RVA 0x3fc2b8,code=c0000005、访问地址 0x8;
  • CrashDumps 里同一分钟有 ltool.exe.17412.dmp。和昨天那一批不是一个签名
  • (那 10 次全是 0x1489c9 = 已修掉的 app.arguments() 那个)—— 这是新的一个偏移。
  • 判据脚本 build/verify/crashsig.ps1 把这条链留在了仓库里(bash 里传 $_ 会被吃,所以走 .ps1 纯 ASCII)。
  • 修的那一处是代码上真站得住的:插件页是 createPage(parent) 交给调用方那棵树拥有的,
  • 而 ToolsPage::rebuildGrid() 会把 stack 摘掉那一格 deleteLater();
  • 宿主这边 PluginHost::g_pages、各壳里那份 m_page 存的都是裸指针,谁都不知道对方已经没了
  • ⇒ 下一次 page(id) 或 feedText() 就是 use-after-free,形状正好是"在 Qt 里读偏移 8 的成员"。
  • g_pages 换成 QHash<QString, QPointer>:缓存里那颗跟着页一起死,page() 遇到死的就重造;
  • base64 / json 的 m_page 换成 QPointer,页没了 feedText 老实返回 false(不假装收下);
  • grid 那份 m_page 只写不读,连着删掉(不留空开关)。
  • 判据:--plugintest 加 3 条 —— 把 base64 那张页真删一次再问宿主要什么。
  • 修完 dead/new + yes;反向验(page() 换回"缓存里有就发出去")当场红 2 条(dead/same、no),
  • 其余 251 条照绿。总数 250 → 253。
  • 号:base64 1.0.2、json 1.0.3、grid 1.0.3(行为修复走补丁位);core 公开面没动布局
  • (core_abi_gate GATE_HITS=0,API 仍 4),ltool.iss 1.4.0 → 1.4.1 —— 1.4.0 已发出去,
  • 发出去的号不能被后来的产物复用。副作用照实说:1.4.1 的商店清单地址要等你推 v1.4.1 的 tag
  • 才不 404,在那之前应用退回本地缓存那 19 条并明说(设计里的行为)。
  • 老实一句:这处补丁我没能证明就是他这一次崩的那一处 —— 偏移在 Qt6Core 内部、
  • 附近没有可读导出(pe_rva.py 量到的是 QBluetoothPermission 那个 staticMetaObject 之后 23KB 的未导出代码),
  • 单凭一个数定不到调用方。所以补了两件让下次自己能说话的东西:
  • 崩溃处理里加栈回溯:CaptureStackBackTrace 打最多 12 帧,每帧一行 "CRASH FRAME ## 模块名+0xRVA" 进 log.txt,行号可以喂回 pe_rva.py 问旁边是谁; 只用固定缓冲区 + fromLatin1(异常处理里再多分配一次,第二次崩会把线索一起吃掉)。
  • store: 重排卡片墙 那行加「摘掉的那格=<类名>」:正常永远该是卡片墙那一类, 哪天摘到一张插件页,那行会先替崩溃说话。
  • 交付:install MATCH EB3EE0CCCF9F、19 颗对齐、装好的那份 --plugintest 253 过 / 0 红、
  • relaunch pid=13408;四道闸绿;离线全套 0 红。

🧩 10. 插件仓库同步完:461a394(三颗源码 + sdk 26 头 + dist 自洽)+ b4ba2e1(去掉两处写死的 12),那边 CI 终态 success

  • 本地 clone 落后 origin 三笔(191fb80 早同步过 19 颗),第一遍在旧 base 上做出"新放 36",
  • 真实增量只有"新放 1 / 改动 10":clipboard 1.1.3、startup 1.1.0、json 1.0.2。
  • sdk 重铺成 26 个头(多的那颗是 Theme.h —— 我先按"应该是 ClipboardStore.h"写进 README,
  • 拿 git show 191fb80:sdk/INFO.txt 一比才发现,别按应该是哪颗写文档)。
  • 独立编(那边没主仓库的 PCH):新建 build-sync,19 颗 dll / 0 error;dist 的 dll + index.json 一起重算,
  • 按他们 CI 的量法自洽(19 条、api 全 4、sha256 与字节逐个对上)。
  • 那边 run 39645316369 = success:验 SDK 快照 / dist 自洽 19 / 一个 dll 都不许缺(日志列出的产物正好 19 行)/ Qt 版本。
  • b4ba2e1 只是把他们两处写死的"12"改成按 plugins/*/ 现枚举(第二次同步就过期,这次就是)。
  • push 到插件仓库连吃两次 443 reset,第三次直连才过 —— 推完拿 ls-remote 对上 sha 才算成功(b4ba2e1 已对上)。
  • 版本:插件仓库不升任何号(它是主仓库的镜像);core 这轮只有文档 + 两个工具。

🧩 11. v1.4.0 发出去了:23 个资产、商店那条 404 的链随发版活了,并对着公开那一面复验过

  • run 39641826096 = success。Release id 419844618:安装包 22,572,344 B + 19 颗 dll +
  • ltool-sdk-1.4.0.zip + plugins-index.json + sha256.txt(共 23 个资产)。
  • tools/postrelease_check.py(新)打的是公开 URL而不是 CI 日志,三件事:
  • 公开说明里带家目录的行 = 0(用的就是发版说明那把同一颗图案,21,792 字);
  • releases/download/v1.4.0/plugins-index.json = HTTP 200、19 条(装 1.4.0 之后那条 404 的链活了);
  • sha256.txt 里安装包那一行 == 资产 API 的 digest(1895560fd733…)。
  • 对外一律 subprocess curl + 重试:这台机的 msys python 走 urllib 是
  • CERTIFICATE_VERIFY_FAILED(没有可信 CA 那份),一次抖动不算"那条链没活"。
  • tag 走过一次弯路并如实记下来:第一次推的 v1.4.0 指在 e6128e8(带那颗写宽的判据),
  • CI 在我自己那道闸上红、Release 没建成(当时查 404);按拍定的"移到修复那一笔"删远端同名 tag、
  • 在 bf53b66 重打再推。删时没有 Release 挂在下面,无连带副作用。
  • README 的 --autoupdatenow 那一行改成现在的状态:v1.4.0 已发、清单可取,
  • 但这一轮没有跑那一栏 —— 跑了会把装着的 19 颗换成商店那批(MSVC 每次字节不同),
  • 而"以哪份为准"那条路还没拍。
  • tools/sync_plugins.py(新,插件仓库那一半):类名从主仓库 CMakeLists 现问,
  • dry/apply 两档,报每颗新放/改动/已一致;这次 新放 36 / 改动 36 / 已一致 34,
  • 6 颗整个缺的连 CMakeLists.txt 一起补。sdk/ 重铺成 26 个头 + 4 个 onnxruntime 头 + ltoolcore.lib。
  • 插件仓库还没推:要先在本地把 19 颗独立编一遍(那边没 PCH,少 include 只有这条路看得见)。

📦 下载与安装

Windows 10 及以上(安装包已声明 MinVersion=10.0)。按用户安装,不需要管理员,默认装到 %LOCALAPPDATA%\Programs\ltool;双击安装向导走完即可,托盘常驻,Alt+Space 呼出。

文件 大小 sha256 前 8 位 说明
ltool-1.4.1-setup.exe 21.53 MB 4feb2231 就用这个:双击安装/升级(约 22 MB,开箱零插件,插件由商店按需拉)
ltool-sdk-1.4.1.zip 270 KB d24ed685 插件 SDK 快照:头文件闭包 + 导入库,给独立编插件用
plugins-index.json 11 KB 8439cfff 插件商店清单:这一版 app 读的就是它(地址按版本号钉死,见下面那条)
ltool-ads.dll 93 KB ab5e0d2b 商店里的插件 ads(v1.0.1,插件接口 4)
ltool-base64.dll 68 KB fe70eeee 商店里的插件 base64(v1.0.2,插件接口 4)
ltool-capture.dll 361 KB b16f7cd7 商店里的插件 capture(v1.0.7,插件接口 4)
ltool-clipboard.dll 135 KB e494b170 商店里的插件 clipboard(v1.1.3,插件接口 4)
ltool-disk.dll 180 KB 4f257c9c 商店里的插件 disk(v1.0.2,插件接口 4)
ltool-downloads.dll 66 KB 02f4c556 商店里的插件 downloads(v1.0.2,插件接口 4)
ltool-grid.dll 154 KB 560d2c3d 商店里的插件 grid(v1.0.3,插件接口 4)
ltool-json.dll 79 KB b379c8df 商店里的插件 json(v1.0.3,插件接口 4)
ltool-language.dll 174 KB f8546eca 商店里的插件 language(v1.0.2,插件接口 4)
ltool-linuxdo.dll 178 KB 40c62de9 商店里的插件 linuxdo(v1.0.2,插件接口 4)
ltool-network.dll 100 KB 9887c5f9 商店里的插件 network(v1.0.1,插件接口 4)
ltool-record.dll 276 KB 665b64b9 商店里的插件 record(v1.0.2,插件接口 4)
ltool-registry.dll 94 KB 0f921ea9 商店里的插件 registry(v1.0.2,插件接口 4)
ltool-repair.dll 88 KB 7f1d7504 商店里的插件 repair(v1.0.2,插件接口 4)
ltool-shell.dll 135 KB f86b9640 商店里的插件 shell(v1.0.2,插件接口 4)
ltool-software.dll 237 KB 4497a04f 商店里的插件 software(v1.0.2,插件接口 4)
ltool-startup.dll 150 KB 7fe5b5a3 商店里的插件 startup(v1.1.0,插件接口 4)
ltool-tasks.dll 96 KB 4eb7bde4 商店里的插件 tasks(v1.0.1,插件接口 4)
ltool-user.dll 140 KB cacb81f3 商店里的插件 user(v1.0.3,插件接口 4)
sha256.txt 2 KB — 上面这些文件的 SHA-256 全表(Get-FileHash -Algorithm SHA256 逐行对得上)

一共 23 个文件。校验:Get-FileHash <文件> -Algorithm SHA256,或下 sha256.txt 对整批。

插件商店读的是 releases/download/v1.4.1/plugins-index.json —— 清单和这 19 颗 dll 都出自编出这一版 core 的那同一次构建,所以"商店里那颗和这台机器的 core 对不上"这件事从源头没了(对不上也只会是被拒绝并说清,不是闪退)。


Full Changelog: v1.4.0...v1.4.1