first commit

This commit is contained in:
hero920103 committed 2026-10-24 13:02:19 +08:00
commit 761b95080f
404 files changed
+312608

No files matched your search

+288
View File
@@ -0,0 +1,288 @@
# 按键精灵(anjianjingling)· 技术方案 v2.1
> 版本:v2.3(全 C# + 四区界面 + 榫卯积木 + **脚本 ⇄ 积木 双向转换**)
> 日期:2026-10-23
> 状态:已实现并可编译运行
---
## 0. 技术栈(全部 C# / .NET)
| 项 | 选型 | 说明 |
|---|---|---|
| 语言 / 框架 | **C# + WinForms + .NET 10**(`net10.0-windows`) | 单一技术栈,无前端工程 |
| 脚本引擎 | **MoonSharp 2.0.0**(纯托管 Lua 5.2) | 无 native 依赖,`dotnet build` 即可 |
| 积木编辑器 | **自绘 WinForms 控件 `BlockCanvas`** | GDI+ 圆角积木、拖拽排序、容器嵌套,**非网页** |
| 输入模拟 | `SendInput`(user32) | 系统级键鼠,坐标为屏幕物理像素 |
| 找图 | OpenCvSharp4 `MatchTemplate` | 相似度阈值 0~1 |
| 找色 | `LockBits` 逐像素扫描 | 支持容差 |
| 操作录制 | `WH_KEYBOARD_LL` / `WH_MOUSE_LL` 低级钩子 | 录制 → 智能聚合 → 生成 Lua |
| 屏幕录像 | GDI 抓帧 + JPEG + 自写 AVI(MJPG) 封装 | 不依赖 ffmpeg,输出可播放 avi |
| 内嵌浏览器 | WebView2(仅浏览器标签页用) | 与积木无关,可选功能 |
---
## 0.5 界面布局(四区)
```
┌──────────────────────────────────────────────────────────────┐
│ 菜单栏 文件 / 积木 / 工具 / 帮助 │
├──────────────────────────────────────────────────────────────┤
│ 工具栏 ▶启动 F8 ‖暂停 F9 ■停止 F10 │ +新建 复制 删除 │ 录制 截图 │
├──────────┬───────────────────────────────────┬───────────────┤
│ 任务列表 │ 任务详情 │ 插件 │
│ (左) │ ┌ 任务名 / 启用 / 循环 / 间隔 ─┐ │ 屏幕与定位 │
│ ·任务1 │ │ 脚本代码 积木 录制 浏览器 │ │ 坐标取色器 │
│ ·任务2 │ │ │ │ 框选截图 │
│ ·任务3 │ │ 编辑区 │ │ 找图测试 │
│ │ └───────────────────────────────┘ │ 录制 │
│ 共 N 个 │ │ 系统与帮助 │
├──────────┴───────────────────────────────────┴───────────────┤
│ 运行日志(跨全宽,可拖动高度) │
├──────────────────────────────────────────────────────────────┤
│ 状态栏 状态:空闲 F8 启动 · F9 暂停 … 行1,列1 │
└──────────────────────────────────────────────────────────────┘
```
| 区域 | 内容 | 行为 |
|---|---|---|
| 左:任务列表 | 自绘列表,每项显示任务名 + 循环次数/间隔 + 状态色条 + **启用勾选框** | 单击切换任务;点勾选框直接启/停;右键新建/复制/删除/上下移/立即运行 |
| 中:任务详情 | 顶部属性条(名称/启用/无限/次数/间隔/**启动热键 F1~F7**)+ 四个页签 | 改动实时回写任务,无需保存按钮 |
| 右:插件 | 分组卡片按钮:屏幕与定位 / 录制 / 脚本 / 系统与帮助 | 一键调用工具,不占主编辑区 |
| 下:日志 | 深色日志区,错误红、成功绿 | 跨全宽,分割条可拖 |
---
## 1. 功能清单
### 1.1 Lua 脚本
- 完整 Lua 语法(`if / for / while / function / table / string.format`…)
- 双写法并存:全局简写 `moveTo(100,200)` + 命名空间 `mouse.moveTo(100,200)`
- 语法着色 + 行号栏 + 中文错误提示
- F8 队列运行、F9 暂停、F10 停止、左上角 (0,0) 急停
### 1.2 积木编程(插片耦合 · 与脚本双向转换)
- **单一数据源**:任务只保存 Lua 脚本。积木页是脚本的另一种视图——切到积木页自动「脚本 → 积木」;在积木里增删改(含改参数)自动「积木 → 脚本」回写,两边永远一致,不存在两份代码
- 旧版任务里只有积木没有脚本的,打开时自动把积木转成脚本
- 解析覆盖目录全部命令:流程容器(for/while/if/elseif/else)、找图(ifExists 还原自 do+local __ix 结构)、赋值/自增、mouse.down 等;认不出的行进「自定义代码」块,原样保留不丢失
- 往返稳定:脚本 → 积木 → 脚本 内容一致(二次往返验证通过)
#### 插片耦合造型
- **榫卯咬合**:每块顶部有凹槽、底部有凸榫(7px),相邻块互相插接,视觉上是一条拼好的积木链
- **C 形容器**:`循环 N 次`、`当…时循环`、`无限循环`、`如果…否则` —— 左壁 + 内部插槽底 + 底部封口;空容器显示「把积木拖到这里」
- 「否则」是容器内腔里的横向分隔条,把插槽分成上下两半
- **参数 = 白色插槽**:标题里的参数值画成可嵌入的白色槽(Blockly 风格),选中块后右侧改参数即时刷新
- 双击或拖拽添加;拖动改序 / 拖进容器嵌套;黄色描边 = 选中
- 一键「生成 Lua →」「运行积木」,存为 `.ajb`(JSON)
### 1.3 操作录制(F12)
- 录制真实的键盘、鼠标点击、滚轮、拖动
- 连续鼠标移动合并为一次 `moveTo`(15ms/3px 采样节流)
- 连续可打印字符聚合为 `typeText("...")`
- `Ctrl/Alt/Win + X` 自动合成 `hotkey("Ctrl+X")`
- 时间差 → `delay(ms)`,可设最大延时截断
- 结果直接填入脚本页或直接运行
### 1.4 屏幕录像
- 全屏或框选区域录制,帧率 1~30,可含鼠标指针
- 输出 `recordings/rec_yyyyMMdd_HHmmss.avi`(MJPEG),任意播放器可播
- 固定帧率策略:抓不过来就丢帧,保证视频时长 = 真实时长
### 1.5 任务列表(左侧栏)
- 每个任务独立持有:脚本、积木、名称、启用、次数/无限、轮次间隔
- 单击任务 → 中间详情立即载入该任务的脚本与积木(互不干扰)
- 右键菜单:新建 / 复制 / 删除 / 上移 / 下移 / 立即运行此任务
- F8 依次执行所有「已启用」任务,每项可设次数 / 无限 / 轮次间隔
- 整个列表保存为 `.ajt`(JSON)
### 1.6 插件面板(右侧栏)
| 分组 | 插件 |
|---|---|
| 屏幕与定位 | 坐标取色器、框选截图、全屏截图、找图测试、打开图库目录 |
| 录制 | 键鼠录制、全屏录屏、区域录屏、停止录屏、打开录像目录 |
| 脚本 | 运行当前任务、生成 Lua、打开脚本、载入积木 |
| 系统与帮助 | 窗口列表、Lua 语法速查、关于 |
---
## 2. 目录结构
```
anjianjingling/
├── anjianjingling.csproj
├── app.manifest # requireAdministrator(控制管理员程序必需)
├── Program.cs
├── Forms/
│ ├── MainForm.cs # 主窗体:任务列表 / 详情 / 插件 / 日志 四区布局
│ ├── PickerForm.cs # ★ 坐标取色器(放大镜 + 实时坐标/颜色)
│ ├── BlockCanvas.cs # ★ 自绘积木画布(拖拽/嵌套/插入指示线)
│ ├── LuaEditor.cs # ★ Lua 编辑器(行号 + 语法着色)
│ └── OverlayForm.cs # 框选截图 / 取录屏区域
├── Lua/
│ ├── LuaEngine.cs # ★ MoonSharp 宿主 + 暂停/停止/急停 + 队列
│ ├── ScriptApi.cs # ★ Lua API 注册(mouse/key/screen/win/browser/clip)
│ ├── LuaBlocks.cs # ★ 积木定义表 + Lua 生成器 + JSON
│ ├── LuaTask.cs # 任务模型 + .ajt 读写
│ └── ILuaHost.cs # 能力接口(主窗体实现)
├── Capture/
│ ├── InputRecorder.cs # ★ 低级键鼠钩子录制 → Lua
│ └── ScreenRecorder.cs # ★ 屏幕录像 + AVI(MJPG) 封装
├── Input/ MouseInput.cs KeyboardInput.cs
├── Vision/ ScreenCapture.cs ImageFinder.cs ColorFinder.cs
├── Window/ WindowManager.cs
├── Browser/ WebViewBridge.cs
├── Native/ Win32.cs
├── Common/ LogBus.cs VirtualKeys.cs
├── Samples/ demo_loop.lua demo_text.lua demo_record.lua
└── images/ # 模板图目录(F11 框选截图默认存这里)
```
---
## 3. Lua API 参考
### 3.1 基础
```lua
delay(ms) -- 等待(可被 F9/F10 打断)
delayRandom(min, max) -- 随机等待
log("文本") -- 写运行日志
stop() -- 主动结束脚本
random(1, 10) now() -- 随机数 / 时间戳
```
### 3.2 鼠标
```lua
moveTo(x, y) moveR(dx, dy)
leftClick() / leftClick(x, y)
rightClick() / rightClick(x, y)
middleClick() doubleClick(x, y)
wheel(-1) -- 正数向上,负数向下
drag(x1, y1, x2, y2, ms)
mouse.down("left") mouse.up("left")
local x, y = mouse.pos()
```
### 3.3 键盘
```lua
keyPress("ENTER") -- A~Z 0~9 F1~F12 ENTER SPACE TAB ESC LEFT UP RIGHT DOWN …
hotkey("Ctrl+Shift+Esc")
typeText("中文文本") -- Unicode 注入,不受输入法影响
keyDown("SHIFT") keyUp("SHIFT")
```
### 3.4 视觉
```lua
local x, y = findImage("images/btn.png", 0.85) -- 找不到 x = -1
local x, y = findColor("FF0000", 10)
local x, y = waitImage("images/btn.png", 10000) -- 轮询等待
if exists("images/btn.png") then … end
screenshot("shot.png")
screen.pick("images/btn.png") -- 运行时框选截图存模板
screen.color(x, y) -- "RRGGBB"
```
### 3.5 系统 / 剪贴板 / 浏览器
```lua
run("notepad.exe") activate("记事本") win.list()
clipSet("内容") local s = clipGet()
browser.open("https://…") browser.click("#btn")
browser.type("#input", "内容") browser.wait(".loaded", 10000)
local t = browser.text(".result") browser.js("document.title")
browser.back() browser.forward() browser.reload() browser.shot("web.png")
```
---
## 4. 关键实现说明
### 4.1 积木 → Lua
`BlockCatalog` 中每个积木含 `Title`(显示模板)与 `Template`(Lua 模板)。
渲染与生成共用同一套 `{0} {1}` 占位,参数按 `ArgKind` 决定是否加引号:
| ArgKind | 用途 | 生成 |
|---|---|---|
| Number | 坐标、毫秒、相似度 | 原样 |
| Text | 文件路径、文本 | `"…"` 转义 |
| Key | 键名、鼠标键 | `"…"` |
| Expr | 条件表达式 | 原样 |
| Raw | 变量名、注释 | 原样 |
容器块(LoopN / LoopWhile / If / IfExists)在生成器里递归,缩进 4 空格。
### 4.2 中断模型
每条 Lua API 回调首先调用 `LuaEngine.Tick()`:
- `CancellationToken` 已取消 → 抛 `AbortSignal`(MoonSharp 会原样透传,不被 `pcall` 吞掉)
- 暂停 → 阻塞在 `ManualResetEventSlim`
- 急停 → 每 100ms 节流检测鼠标是否在 (0,0),停留 120ms 即中止
`delay` 按 20ms 分片,期间同样响应暂停/停止。
### 4.3 录制聚合
原始事件(带时间戳)→ 单遍扫描:
1. 连续 `Move` → 只保留落点,生成一次 `moveTo`
2. `Down` + 500ms 内同名 `Up` → 合并成一次点击
3. 修饰键状态栈 + 主键 → `hotkey("Ctrl+X")`
4. 可打印字符进缓冲区,遇到非字符事件 flush 成 `typeText`
5. 事件间隔 ≥30ms → `delay(间隔)`,超过上限则截断
### 4.4 AVI 封装
```
RIFF/AVI
├ LIST hdrl → avih(56) + LIST strl(strh 56 + strf 40)
├ LIST movi → 00dc 帧 chunk(JPEG 数据,奇数长度补 0)
└ idx1 → 每帧 16 字节索引
```
写完回填 RIFF/movi 长度、总帧数、最大帧字节数。MJPEG 无需关键帧,所有帧都是关键帧(flag `0x10`)。
---
## 5. 热键
| 键 | 功能 |
|---|---|
| F8 | 启动任务队列 |
| F9 | 暂停 / 继续 |
| F10 | 停止 |
| F11 | 框选截图(直接存 `images/`,可当模板图) |
| F12 | 开始 / 停止操作录制 |
| 鼠标到 (0,0) | 紧急停止 |
---
## 6. 已验证项
| 项 | 结果 |
|---|---|
| `dotnet build` | 0 错误(仅 WebView2 自带的 WindowsBase 版本警告) |
| 积木 → Lua 生成 | 循环/条件/否则/找图/键盘/拖拽/注释全部正确 |
| Lua 执行 | 找图多返回值、`for`、`table`、剪贴板、`stop()` 正常 |
| 语法错误提示 | 中文提示并带位置 |
| 屏幕录像 | 1.5s @10fps → 16 帧,RIFF/idx1 结构正确 |
| 主窗体构造 | 四区布局全部正常渲染(1360×860) |
| 脚本⇄积木 | 演示脚本 581 字符 → 18 个积木(注释/循环/ifExists/赋值全命中);改积木参数 delay(1234) 后脚本自动回写;往返一致 |
| 新任务 | 模板脚本 4 行 → 4 个积木自动转换 |
| 任务联动 | 改任务名即时同步左侧;切换任务脚本/积木各自恢复(积木 JSON 1345 字节往返正常) |
| 任务增删 | 新建→选中→载入模板脚本;删除后自动选中相邻任务 |
| 插件面板 | 17 个插件项,内容超高时垂直滚动条正常 |
---
## 7. 已知限制
| 限制 | 说明 |
|---|---|
| 管理员权限 | manifest 为 `requireAdministrator`,需以管理员身份运行才能控制系统级窗口 |
| 纯计算死循环 | 不调用任何 API 的 `while true do end` 无法被 F10 中断(无指令级钩子) |
| 中文输入法录制 | 低级钩子只能拿虚拟键码,输入法上屏的中文需改用 `typeText` |
| DirectX 独占全屏 | 抓图/后台消息无效,属系统限制 |
| 游戏内核反作弊 | 应用层模拟无法驱动,明确不支持 |
---
## 8. 后续可扩展
- OCR 文字识别(接 PaddleOCR / Windows.Media.Ocr)
- 录制时自动补找图锚点(点击位置周边取 32×32 模板)
- 积木 → 脚本 双向(Lua 子集解析回积木)
- 脚本加密打包(导出 exe / 加密 lua)
- 多坐标点绑定窗口(窗口相对坐标)