Files
anjianjingling/开发方案.md
T
2026-10-24 13:02:19 +08:00

289 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 按键精灵(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)
- 多坐标点绑定窗口(窗口相对坐标)