first commit
This commit is contained in:
commit
761b95080f
404 files changed
+312608
No files matched your search
@@ -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)
|
||||
- 多坐标点绑定窗口(窗口相对坐标)
|
||||
Reference in new issue
Block a user