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

14 KiB
Raw Blame History

按键精灵(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 基础

delay(ms)                    -- 等待(可被 F9/F10 打断)
delayRandom(min, max)        -- 随机等待
log("文本")                  -- 写运行日志
stop()                       -- 主动结束脚本
random(1, 10)  now()         -- 随机数 / 时间戳

3.2 鼠标

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 键盘

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 视觉

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 系统 / 剪贴板 / 浏览器

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)
  • 多坐标点绑定窗口(窗口相对坐标)