鼠标键盘录制 工具
🖱️ Mouse & Keyboard Macro Recorder
支持中英双语
中文界面
exe文件可以在这里下载,https://github.com/kenyucanyou/MouseRecorder

English Interface

🖱️ 鼠标键盘宏录制工具
A lightweight Mouse and Keyboard Macro Recorder built with AutoIt.
It allows users to record, save, edit, and replay mouse and keyboard actions with precise timing.
这是一个使用 AutoIt 开发的轻量级鼠标键盘宏录制工具,
可以 记录、保存、编辑并回放鼠标和键盘操作,并保持精确的时间间隔。
✨ Features | 功能特点
🎥 Mouse Action Recording
鼠标操作录制
Record real-time mouse actions including:
支持记录以下鼠标操作:
- Mouse movement
- Left click / Right click
- Mouse button press & release
- Precise cursor coordinates
包括:
- 鼠标移动
- 左键点击 / 右键点击
- 鼠标按下与释放
- 精确坐标记录
⌨ Keyboard Recording
键盘录制
Supports recording common keyboard input:
支持记录常见键盘输入:
- English letters
A-Z - Numbers
0-9 Enter.(period)
支持:
- 英文字母
A-Z - 数字
0-9 - 回车键
- 英文句号
⏱ Precise Timing Replay
精确时间回放
Each action records the exact execution time, allowing playback to reproduce the original user behavior.
每个操作都会记录 精确时间间隔,回放时可以真实还原用户操作节奏。
🔁 Macro Loop Playback
宏循环播放
Macros can be repeated multiple times.
宏可以设置循环执行次数。
Useful for:
适用于:
- Automated testing
- Repetitive tasks
- Game automation
- Data entry automation
💾 Macro Save & Load
宏文件保存与加载
Recorded macros can be saved to a file and loaded later.
录制的宏可以保存到文件并随时加载。
Macro format:
宏文件格式:
time | action | x | y
Example:
示例:
120|move|520|420
180|left_down|520|420
210|left_up|520|420
🌐 Bilingual Interface
双语界面
The software supports:
软件支持:
- English interface
- Chinese interface
方便不同用户使用。
🎛 Visual Macro Timeline
可视化宏时间轴
All recorded actions are displayed in a ListView timeline.
所有操作会显示在 时间轴列表中,包括:
- Time
- Action
- Mouse coordinates
方便查看和调试宏操作。
🚀 Use Cases | 使用场景
This tool is useful for:
该工具适用于:
Automation
- repetitive office tasks
- automated mouse operations
Testing
- GUI automation testing
- software behavior testing
Productivity
- repetitive clicking tasks
- workflow automation
🧠 Technical Highlights
技术亮点
⚡ Lightweight
The compiled executable is very small (< 1MB).
编译后的程序 非常小 (<1MB)。
⚡ No Dependencies
No installation required.
无需安装依赖。
⚡ Built with AutoIt
Advantages:
- native Windows automation
- fast execution
- very small binary size
⚡ Efficient Event Recording
Uses high-frequency polling:
AdlibRegister("CaptureMouse", 20)
Captures user input every 20ms for accurate recording.
📦 Example Workflow
使用流程
Start Recording
↓
Perform Mouse / Keyboard Actions
↓
Stop Recording
↓
Save Macro
↓
Replay Macro
⌨ Hotkeys | 快捷键
| Key | Function |
|---|---|
| F2 | Start Recording |
| F3 | Stop Recording |
| F4 | Play Macro |
| ESC | Stop Playback |
📂 Macro File Format
宏文件格式
time | action | x | y
Example:
120|move|600|300
180|left_down|600|300
210|left_up|600|300
📜 License
MIT License
⭐ Project Goals
项目目标
Create a simple but powerful automation tool that helps users record and replay repetitive actions efficiently.
创建一个 简单但强大的自动化工具,帮助用户高效完成重复操作。
下面是Autoit的源代码。
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Icon=..\Downloads\ico_mouse_128_28636.ico
#AutoIt3Wrapper_UseX64=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Array.au3>
#include <GuiListView.au3>
#include <GuiStatusBar.au3>
#include <File.au3>
#include <Misc.au3>
#include <ButtonConstants.au3>
#include <FontConstants.au3>
; =========================
; 全局配置
; =========================
Global $g_sLang = "CN" ; 默认中文: CN / 英文: EN
; 新增:播放控制与中止
Global $g_bPlaying = False
Global $g_bAbort = False
; 组件句柄
Global $chkRecordMove, $btnInsertDelay
Global $hGUI, $btnRecord, $btnStop, $btnPlay, $btnSave, $btnLoad, $btnLang
Global $lblMain, $lblLoop, $inputLoop, $List, $hStatus
Global $hListContextMenu, $mnuDelete, $mnuMoveUp, $mnuMoveDown, $mnuClearAll
; 动作数组:5列 [time, type, x, y, label]
; - time : 触发相对时间(毫秒)
; - type : 原始动作类型(英文如 move/left_down/key_down/delay)
; - x : 对鼠标=坐标X;对键盘=VK码;对delay=毫秒
; - y : 对鼠标=坐标Y;其余=0
; - label : 显示说明的“存储值”(语言无关:A、Ctrl、LButton、500ms 等)
Global $aActions[0][5]
; 录制状态
Global $bRecording = False
Global $nStartTime
Global $lastX = -1, $lastY = -1
Global $lastLeft = 0, $lastRight = 0
Global $lastKey[256]
; =========================
; 语言文本映射表
; =========================
Global $g_aLangText = [ _
["Title", "鼠标键盘宏录制器", "Mouse & Keyboard Macro Recorder"], _
["MainLabel", "鼠标 & 键盘 宏录制工具", "Mouse & Keyboard Macro Recorder"], _
["BtnRecord", "开始录制 (F2)", "Start Record (F2)"], _
["BtnStop", "停止录制 (F3)", "Stop Record (F3)"], _
["BtnPlay", "播放宏 (F4)", "Play Macro (F4)"], _
["BtnSave", "保存", "Save"], _
["BtnLoad", "加载", "Load"], _
["BtnLang", "中文/EN", "中文/EN"], _
["LblLoop", "循环次数:", "Loop Count:"], _
["MenuDelete", "删除选中行 (Del)", "Delete Selected Row (Del)"], _
["MenuMoveUp", "上移 (Ctrl+↑)", "Move Up (Ctrl+↑)"], _
["MenuMoveDown", "下移 (Ctrl+↓)", "Move Down (Ctrl+↓)"], _
["MenuClearAll", "清空全部动作", "Clear All Actions"], _
["StatusReady", " 就绪", " Ready"], _
["StatusActionCount", " 动作数:", " Action Count:"], _
["StatusRecording", " 正在录制... (按 F3 或停止按钮结束)", " Recording... (Press F3 or Stop Button to End)"], _
["StatusStopRecord", " 已停止录制 共 ", " Recording Stopped Total "], _
["StatusStopRecord2", " 条动作", " actions"], _
["StatusPlaying", " 正在播放... (按 ESC 可中止)", " Playing... (Press ESC to Stop)"], _
["StatusPlayStopped", " 播放已被用户中止", " Play Stopped by User"], _
["StatusPlayComplete", " 播放完成", " Play Completed"], _
["StatusDeleted", " 已删除 1 条动作 剩余 ", " Deleted 1 action Remaining "], _
["StatusMoved", " 动作已移动 当前第 ", " Action Moved Current No. "], _
["StatusCleared", " 已清空所有动作", " All Actions Cleared"], _
["StatusSaved", " 已保存到 ", " Saved to "], _
["StatusLoaded", " 已加载 ", " Loaded "], _
["MsgStartRecord", "开始录制", "Start Recording"], _
["MsgStartRecordText", "已开始录制鼠标和键盘操作", "Started recording mouse and keyboard actions"], _
["MsgStopRecord", "录制结束", "Recording Finished"], _
["MsgStopRecordText", "录制完成,共记录 ", "Recording completed, total "], _
["MsgStopRecordText2", " 条动作", " actions recorded"], _
["MsgNoActions", "提示", "Tip"], _
["MsgNoActionsText", "当前没有可播放的动作", "No actions to play"], _
["MsgStartPlay", "开始播放", "Start Playing"], _
["MsgStartPlayText", "即将开始播放,按 ESC 键可中途停止", "About to start playing, press ESC to stop"], _
["MsgPlayStopped", "已中止", "Aborted"], _
["MsgPlayStoppedText", "播放被手动停止", "Play stopped manually"], _
["MsgPlayComplete", "完成", "Completed"], _
["MsgPlayCompleteText", "宏播放结束", "Macro play finished"], _
["MsgConfirmClear", "确认", "Confirmation"], _
["MsgConfirmClearText", "确定要清空所有录制动作吗?", "Are you sure to clear all recorded actions?"], _
["DlgSaveTitle", "保存宏", "Save Macro"], _
["DlgLoadTitle", "打开宏", "Open Macro"], _
["ActionMove", "移动鼠标", "Mouse Move"], _
["ActionLBtnDown", "左键按下", "Left Button Down"], _
["ActionLBtnUp", "左键松开", "Left Button Up"], _
["ActionRBtnDown", "右键按下", "Right Button Down"], _
["ActionRBtnUp", "右键松开", "Right Button Up"], _
["ActionKeyDown", "按键按下", "Key Down"], _
["ActionKeyUp", "按键松开", "Key Up"], _
["ActionDelay", "延时", "Delay"], _
["ColTime", "时间(ms)", "Time(ms)"], _
["ColAction", "动作类型", "Action Type"], _
["ColXKey", "X/键码", "X/Key Code"], _
["ColY", "Y", "Y"], _
["ColLabel", "键名/说明", "Key/Text"], _
["ChkRecordMove", "录制鼠标移动", "Record Mouse Move"], _
["BtnInsertDelay", "插入延时", "Insert Delay"] _
]
; =========================
; 语言工具函数
; =========================
Func GetLangText($sKey)
For $i = 0 To UBound($g_aLangText) - 1
If $g_aLangText[$i][0] = $sKey Then
If $g_sLang = "CN" Then
Return $g_aLangText[$i][1]
Else
Return $g_aLangText[$i][2]
EndIf
EndIf
Next
Return $sKey
EndFunc ;==>GetLangText
Func _VKToName($vk)
$vk = Number($vk)
If $vk >= 0x41 And $vk <= 0x5A Then Return Chr($vk) ; A..Z
If $vk >= 0x30 And $vk <= 0x39 Then Return Chr($vk) ; 0..9
Switch $vk
Case 0x1B
Return "Esc"
Case 0x0D
Return "Enter"
Case 0x09
Return "Tab"
Case 0x20
Return "Space"
Case 0x08
Return "Backspace"
Case 0x2E
Return "Delete"
Case 0x24
Return "Home"
Case 0x23
Return "End"
Case 0x21
Return "PageUp"
Case 0x22
Return "PageDown"
Case 0x25
Return "Left"
Case 0x26
Return "Up"
Case 0x27
Return "Right"
Case 0x28
Return "Down"
Case 0x10
Return "Shift"
Case 0x11
Return "Ctrl"
Case 0x12
Return "Alt"
Case 0x5B
Return "LWin"
Case 0x5C
Return "RWin"
Case 0xBA
Return ";"
Case 0xBB
Return "="
Case 0xBC
Return ","
Case 0xBD
Return "-"
Case 0xBE
Return "."
Case 0xBF
Return "/"
Case 0xDB
Return "["
Case 0xDC
Return "\"
Case 0xDD
Return "]"
Case 0xDE
Return "'"
EndSwitch
If $vk >= 0x70 And $vk <= 0x87 Then Return "F" & ($vk - 0x6F) ; F1..F24
Return "VK_" & Hex($vk, 2) ; 回退
EndFunc ;==>_VKToName
Func _ToDisplayLabel($sActionType, $sStored)
If $sActionType = "left_down" Or $sActionType = "left_up" Then
Return ($g_sLang = "CN" ? "左键" : "Left Button")
ElseIf $sActionType = "right_down" Or $sActionType = "right_up" Then
Return ($g_sLang = "CN" ? "右键" : "Right Button")
ElseIf $sActionType = "middle_down" Or $sActionType = "middle_up" Then
Return ($g_sLang = "CN" ? "中键" : "Middle Button")
ElseIf $sActionType = "move" Then
Return ($g_sLang = "CN" ? "移动" : "Move")
ElseIf $sActionType = "delay" Then
Return $sStored ; 例如 "500ms"
EndIf
Return $sStored ; 键盘/其他:直接显示存储值
EndFunc ;==>_ToDisplayLabel
; 播放控制:ESC中止 / 安全释放 / 可中断睡眠
Func _AbortPlay()
$g_bAbort = True
EndFunc ;==>_AbortPlay
Func _ReleaseSafety()
MouseUp("left")
MouseUp("right")
Local $aMods[5] = [0x10, 0x11, 0x12, 0x5B, 0x5C] ; Shift/Ctrl/Alt/LWin/RWin
For $i = 0 To UBound($aMods) - 1
DllCall("user32.dll", "none", "keybd_event", "byte", $aMods[$i], "byte", 0, "dword", 2, "ulong_ptr", 0)
Next
EndFunc ;==>_ReleaseSafety
Func _DoEvents()
Local $msg = GUIGetMsg()
; 读出让GUI保持响应
EndFunc ;==>_DoEvents
Func _SleepAbortable($ms)
Local $t0 = TimerInit()
While TimerDiff($t0) < $ms
If $g_bAbort Or _IsPressed("1B") Then Return False
_DoEvents()
Sleep(10)
WEnd
Return True
EndFunc ;==>_SleepAbortable
; =========================
; GUI
; =========================
$hGUI = GUICreate(GetLangText("Title"), 740, 520, -1, -1, BitOR($WS_OVERLAPPEDWINDOW, $WS_CLIPCHILDREN))
GUISetBkColor(0xF5F6F5)
$lblMain = GUICtrlCreateLabel(GetLangText("MainLabel"), 30, 15, 680, 40)
GUICtrlSetFont(-1, 16, $FW_BOLD, 0, "微软雅黑")
GUICtrlSetColor(-1, 0x2C3E50)
Local $btnW1 = 130, $btnW2 = 60, $btnH = 38, $btnY = 65, $gap = 10
$btnRecord = GUICtrlCreateButton(GetLangText("BtnRecord"), 30, $btnY, $btnW1, $btnH)
GUICtrlSetBkColor(-1, 0x27AE60)
GUICtrlSetColor(-1, 0xFFFFFF)
GUICtrlSetFont(-1, 10, $FW_SEMIBOLD, 0, "微软雅黑")
$btnStop = GUICtrlCreateButton(GetLangText("BtnStop"), 30 + $btnW1 + $gap, $btnY, $btnW1, $btnH)
GUICtrlSetBkColor(-1, 0xE74C3C)
GUICtrlSetColor(-1, 0xFFFFFF)
GUICtrlSetFont(-1, 10, $FW_SEMIBOLD, 0, "微软雅黑")
$btnPlay = GUICtrlCreateButton(GetLangText("BtnPlay"), 30 + ($btnW1 + $gap) * 2, $btnY, $btnW1, $btnH)
GUICtrlSetBkColor(-1, 0x3498DB)
GUICtrlSetColor(-1, 0xFFFFFF)
GUICtrlSetFont(-1, 10, $FW_SEMIBOLD, 0, "微软雅黑")
$btnSave = GUICtrlCreateButton(GetLangText("BtnSave"), 30 + ($btnW1 + $gap) * 3, $btnY, $btnW2, $btnH)
GUICtrlSetBkColor(-1, 0x95A5A6)
GUICtrlSetColor(-1, 0xFFFFFF)
GUICtrlSetFont(-1, 10, 400, 0, "微软雅黑")
$btnLoad = GUICtrlCreateButton(GetLangText("BtnLoad"), 30 + ($btnW1 + $gap) * 3 + $btnW2 + $gap, $btnY, $btnW2, $btnH)
GUICtrlSetBkColor(-1, 0x95A5A6)
GUICtrlSetColor(-1, 0xFFFFFF)
GUICtrlSetFont(-1, 10, 400, 0, "微软雅黑")
$btnLang = GUICtrlCreateButton(GetLangText("BtnLang"), 30 + ($btnW1 + $gap) * 3 + ($btnW2 + $gap) * 2, $btnY, $btnW2, $btnH)
GUICtrlSetBkColor(-1, 0x8E44AD)
GUICtrlSetColor(-1, 0xFFFFFF)
GUICtrlSetFont(-1, 10, 400, 0, "微软雅黑")
$lblLoop = GUICtrlCreateLabel(GetLangText("LblLoop"), 30, 115, 80, 24)
GUICtrlSetFont(-1, 10, 400, 0, "微软雅黑")
GUICtrlSetColor(-1, 0x34495E)
$inputLoop = GUICtrlCreateInput("1", 110, 112, 60, 28)
GUICtrlSetFont(-1, 10, 400, 0, "Consolas")
GUICtrlSetBkColor(-1, 0xFFFFFF)
$chkRecordMove = GUICtrlCreateCheckbox(GetLangText("ChkRecordMove"), 200, 115, 160, 24)
GUICtrlSetFont(-1, 10, 400, 0, "微软雅黑")
GUICtrlSetColor(-1, 0x34495E)
GUICtrlSetState(-1, $GUI_UNCHECKED)
$btnInsertDelay = GUICtrlCreateButton(GetLangText("BtnInsertDelay"), 380, 112, 160, 28)
GUICtrlSetFont(-1, 10, 400, 0, "微软雅黑")
GUICtrlSetBkColor(-1, 0xF39C12)
GUICtrlSetColor(-1, 0xFFFFFF)
; === ListView(5列) ===
$List = GUICtrlCreateListView( _
GetLangText("ColTime") & "|" & _
GetLangText("ColAction") & "|" & _
GetLangText("ColXKey") & "|" & _
GetLangText("ColY") & "|" & _
GetLangText("ColLabel"), _
30, 150, 680, 320, _
BitOR($LVS_REPORT, $LVS_SINGLESEL, $LVS_SHOWSELALWAYS) _
)
_GUICtrlListView_SetExtendedListViewStyle($List, BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_GRIDLINES, $LVS_EX_DOUBLEBUFFER, $LVS_EX_FLATSB))
_GUICtrlListView_SetBkColor($List, 0xFCFCFC)
_GUICtrlListView_SetTextBkColor($List, 0xFCFCFC)
_GUICtrlListView_SetTextColor($List, 0x2C3E50)
_GUICtrlListView_SetColumnWidth($List, 0, 110)
_GUICtrlListView_SetColumnWidth($List, 1, 180)
_GUICtrlListView_SetColumnWidth($List, 2, 90)
_GUICtrlListView_SetColumnWidth($List, 3, 90)
_GUICtrlListView_SetColumnWidth($List, 4, 140)
GUICtrlSetFont($List, 10, 400, 0, "Consolas")
; 右键菜单
$hListContextMenu = GUICtrlCreateContextMenu($List)
$mnuDelete = GUICtrlCreateMenuItem(GetLangText("MenuDelete"), $hListContextMenu)
$mnuMoveUp = GUICtrlCreateMenuItem(GetLangText("MenuMoveUp"), $hListContextMenu)
$mnuMoveDown = GUICtrlCreateMenuItem(GetLangText("MenuMoveDown"), $hListContextMenu)
GUICtrlCreateMenuItem("", $hListContextMenu)
$mnuClearAll = GUICtrlCreateMenuItem(GetLangText("MenuClearAll"), $hListContextMenu)
GUICtrlSetFont($mnuDelete, 9, 400, 0, "微软雅黑")
GUICtrlSetFont($mnuMoveUp, 9, 400, 0, "微软雅黑")
GUICtrlSetFont($mnuMoveDown, 9, 400, 0, "微软雅黑")
GUICtrlSetFont($mnuClearAll, 9, 400, 0, "微软雅黑")
; 状态栏
$hStatus = _GUICtrlStatusBar_Create($hGUI)
_GUICtrlStatusBar_SetSimple($hStatus, False)
Local $aPartsWidths[2] = [400, -1]
_GUICtrlStatusBar_SetParts($hStatus, $aPartsWidths)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusReady"), 0)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusActionCount") & "0 ", 1)
GUISetState()
; 热键
HotKeySet("{F2}", "StartRecording")
HotKeySet("{F3}", "StopRecording")
HotKeySet("{F4}", "PlayMacro")
; ESC 在 PlayMacro 播放期间动态注册
; =========================
; GUI 主循环
; =========================
While 1
$msg = GUIGetMsg(1)
Switch $msg[0]
Case $GUI_EVENT_CLOSE
Exit
Case $btnRecord
StartRecording()
Case $btnStop
StopRecording()
Case $btnPlay
PlayMacro()
Case $btnSave
SaveMacro()
Case $btnLoad
LoadMacro()
Case $btnLang
SwitchLanguage()
Case $mnuDelete
DeleteSelectedItem()
Case $mnuMoveUp
MoveSelectedItem(-1)
Case $mnuMoveDown
MoveSelectedItem(1)
Case $btnInsertDelay
InsertDelay()
Case $mnuClearAll
If MsgBox(4 + 32 + 256, GetLangText("MsgConfirmClear"), GetLangText("MsgConfirmClearText")) = 6 Then
ReDim $aActions[0][5]
_GUICtrlListView_DeleteAllItems($List)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusCleared"), 0)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusActionCount") & "0 ", 1)
EndIf
EndSwitch
; 快捷键:Delete / Ctrl+Up / Ctrl+Down
If _IsPressed("2E") Then
DeleteSelectedItem()
Sleep(150)
EndIf
If _IsPressed("11") Then
If _IsPressed("26") Then MoveSelectedItem(-1)
Sleep(120)
If _IsPressed("28") Then MoveSelectedItem(1)
Sleep(120)
EndIf
Sleep(10)
WEnd
; =========================
; 功能函数
; =========================
Func SwitchLanguage()
$g_sLang = ($g_sLang = "CN") ? "EN" : "CN"
WinSetTitle($hGUI, "", GetLangText("Title"))
GUICtrlSetData($lblMain, GetLangText("MainLabel"))
GUICtrlSetData($btnRecord, GetLangText("BtnRecord"))
GUICtrlSetData($btnStop, GetLangText("BtnStop"))
GUICtrlSetData($btnPlay, GetLangText("BtnPlay"))
GUICtrlSetData($btnSave, GetLangText("BtnSave"))
GUICtrlSetData($btnLoad, GetLangText("BtnLoad"))
GUICtrlSetData($btnLang, GetLangText("BtnLang"))
GUICtrlSetData($lblLoop, GetLangText("LblLoop"))
GUICtrlSetData($chkRecordMove, GetLangText("ChkRecordMove"))
GUICtrlSetData($btnInsertDelay, GetLangText("BtnInsertDelay"))
; 重建 ListView(5列)
GUICtrlDelete($List)
$List = GUICtrlCreateListView( _
GetLangText("ColTime") & "|" & _
GetLangText("ColAction") & "|" & _
GetLangText("ColXKey") & "|" & _
GetLangText("ColY") & "|" & _
GetLangText("ColLabel"), _
30, 150, 680, 320, _
BitOR($LVS_REPORT, $LVS_SINGLESEL, $LVS_SHOWSELALWAYS) _
)
_GUICtrlListView_SetExtendedListViewStyle($List, BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_GRIDLINES, $LVS_EX_DOUBLEBUFFER, $LVS_EX_FLATSB))
_GUICtrlListView_SetBkColor($List, 0xFCFCFC)
_GUICtrlListView_SetTextBkColor($List, 0xFCFCFC)
_GUICtrlListView_SetTextColor($List, 0x2C3E50)
_GUICtrlListView_SetColumnWidth($List, 0, 110)
_GUICtrlListView_SetColumnWidth($List, 1, 180)
_GUICtrlListView_SetColumnWidth($List, 2, 90)
_GUICtrlListView_SetColumnWidth($List, 3, 90)
_GUICtrlListView_SetColumnWidth($List, 4, 140)
GUICtrlSetFont($List, 10, 400, 0, "Consolas")
; 右键菜单也要重建以绑定到新的List控件
$hListContextMenu = GUICtrlCreateContextMenu($List)
$mnuDelete = GUICtrlCreateMenuItem(GetLangText("MenuDelete"), $hListContextMenu)
$mnuMoveUp = GUICtrlCreateMenuItem(GetLangText("MenuMoveUp"), $hListContextMenu)
$mnuMoveDown = GUICtrlCreateMenuItem(GetLangText("MenuMoveDown"), $hListContextMenu)
GUICtrlCreateMenuItem("", $hListContextMenu)
$mnuClearAll = GUICtrlCreateMenuItem(GetLangText("MenuClearAll"), $hListContextMenu)
GUICtrlSetFont($mnuDelete, 9, 400, 0, "微软雅黑")
GUICtrlSetFont($mnuMoveUp, 9, 400, 0, "微软雅黑")
GUICtrlSetFont($mnuMoveDown, 9, 400, 0, "微软雅黑")
GUICtrlSetFont($mnuClearAll, 9, 400, 0, "微软雅黑")
; 重新加载已有动作(语言化显示)
For $i = 0 To UBound($aActions) - 1
Local $t = Round($aActions[$i][0], 0)
Local $sType = $aActions[$i][1]
Local $sDispType = ""
Switch $sType
Case "move"
$sDispType = GetLangText("ActionMove")
Case "left_down"
$sDispType = GetLangText("ActionLBtnDown")
Case "left_up"
$sDispType = GetLangText("ActionLBtnUp")
Case "right_down"
$sDispType = GetLangText("ActionRBtnDown")
Case "right_up"
$sDispType = GetLangText("ActionRBtnUp")
Case "key_down"
$sDispType = GetLangText("ActionKeyDown")
Case "key_up"
$sDispType = GetLangText("ActionKeyUp")
Case "delay"
$sDispType = GetLangText("ActionDelay")
; 兼容旧中文类型字符串
Case "移动鼠标"
$sType = "move" ; 纠正存储
$sDispType = GetLangText("ActionMove")
Case "左键按下"
$sType = "left_down" ; 纠正存储
$sDispType = GetLangText("ActionLBtnDown")
Case "左键松开"
$sType = "left_up"
$sDispType = GetLangText("ActionLBtnUp")
Case "右键按下"
$sType = "right_down"
$sDispType = GetLangText("ActionRBtnDown")
Case "右键松开"
$sType = "right_up"
$sDispType = GetLangText("ActionRBtnUp")
Case "按键按下"
$sType = "key_down"
$sDispType = GetLangText("ActionKeyDown")
Case "按键松开"
$sType = "key_up"
$sDispType = GetLangText("ActionKeyUp")
EndSwitch
Local $sLabelDisp = _ToDisplayLabel($sType, $aActions[$i][4])
GUICtrlCreateListViewItem( _
$t & "|" & _
$sDispType & "|" & _
$aActions[$i][2] & "|" & _
$aActions[$i][3] & "|" & _
$sLabelDisp, _
$List _
)
Next
; 更新状态栏
Local $iCount = UBound($aActions)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusReady"), 0)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusActionCount") & $iCount & " ", 1)
EndFunc ;==>SwitchLanguage
; 录制
Func StartRecording()
If $bRecording Then Return
ReDim $aActions[0][5]
_GUICtrlListView_DeleteAllItems($List)
For $i = 0 To 255
$lastKey[$i] = 0
Next
$nStartTime = TimerInit()
$bRecording = True
AdlibRegister("CaptureMouse", 20)
AdlibRegister("CaptureKeyboard", 20)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusRecording"), 0)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusActionCount") & "0 ", 1)
MsgBox(64, GetLangText("MsgStartRecord"), GetLangText("MsgStartRecordText"))
EndFunc ;==>StartRecording
Func StopRecording()
If Not $bRecording Then Return
$bRecording = False
AdlibUnRegister("CaptureMouse")
AdlibUnRegister("CaptureKeyboard")
Local $cnt = UBound($aActions)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusStopRecord") & $cnt & GetLangText("StatusStopRecord2"), 0)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusActionCount") & $cnt, 1)
MsgBox(64, GetLangText("MsgStopRecord"), GetLangText("MsgStopRecordText") & $cnt & GetLangText("MsgStopRecordText2"))
EndFunc ;==>StopRecording
Func CaptureMouse()
If Not $bRecording Then Return
Local $pos = MouseGetPos()
Local $x = $pos[0], $y = $pos[1]
Local $left = _IsPressed("01"), $right = _IsPressed("02")
If GUICtrlRead($chkRecordMove) = $GUI_CHECKED Then
If $x <> $lastX Or $y <> $lastY Then
AddAction("move", $x, $y)
$lastX = $x
$lastY = $y
EndIf
Else
$lastX = $x
$lastY = $y
EndIf
If $left And Not $lastLeft Then AddAction("left_down", $x, $y)
If Not $left And $lastLeft Then AddAction("left_up", $x, $y)
If $right And Not $lastRight Then AddAction("right_down", $x, $y)
If Not $right And $lastRight Then AddAction("right_up", $x, $y)
$lastLeft = $left
$lastRight = $right
EndFunc ;==>CaptureMouse
Func CaptureKeyboard()
If Not $bRecording Then Return
Static $aHex[256]
If $aHex[0] = "" Then
For $i = 0 To 255
$aHex[$i] = Hex($i, 2)
Next
EndIf
For $i = 1 To 255
Local $pressed = _IsPressed($aHex[$i])
If $pressed And Not $lastKey[$i] Then AddAction("key_down", $i, 0)
If Not $pressed And $lastKey[$i] Then AddAction("key_up", $i, 0)
$lastKey[$i] = $pressed
Next
EndFunc ;==>CaptureKeyboard
Func AddAction($sType, $x, $y)
Local $time = TimerDiff($nStartTime)
Local $n = UBound($aActions)
ReDim $aActions[$n + 1][5]
$aActions[$n][0] = $time
$aActions[$n][1] = $sType
$aActions[$n][2] = $x
$aActions[$n][3] = $y
; 第5列:存储值(语言无关)
Local $sLabelStored = ""
Switch $sType
Case "key_down", "key_up"
$sLabelStored = _VKToName($x)
Case "left_down", "left_up"
$sLabelStored = "LButton"
Case "right_down", "right_up"
$sLabelStored = "RButton"
Case "middle_down", "middle_up"
$sLabelStored = "MButton"
Case "delay"
$sLabelStored = $x & "ms"
Case "move"
$sLabelStored = "" ; 可留空
EndSwitch
$aActions[$n][4] = $sLabelStored
; 列2:动作类型(本地化)
Local $sDispType = ""
Switch $sType
Case "move"
$sDispType = GetLangText("ActionMove")
Case "left_down"
$sDispType = GetLangText("ActionLBtnDown")
Case "left_up"
$sDispType = GetLangText("ActionLBtnUp")
Case "right_down"
$sDispType = GetLangText("ActionRBtnDown")
Case "right_up"
$sDispType = GetLangText("ActionRBtnUp")
Case "key_down"
$sDispType = GetLangText("ActionKeyDown")
Case "key_up"
$sDispType = GetLangText("ActionKeyUp")
Case "delay"
$sDispType = GetLangText("ActionDelay")
EndSwitch
Local $sLabelDisp = _ToDisplayLabel($sType, $sLabelStored)
GUICtrlCreateListViewItem( _
Round($time, 0) & "|" & _
$sDispType & "|" & _
$x & "|" & _
$y & "|" & _
$sLabelDisp, _
$List _
)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusActionCount") & ($n + 1), 1)
EndFunc ;==>AddAction
Func InsertDelay()
Local $iSel = _GUICtrlListView_GetSelectedIndices($List)
If $iSel = "" Or $iSel = -1 Then
MsgBox(48, GetLangText("MsgNoActions"), GetLangText("MsgSelectRow"))
Return
EndIf
Local $index = Number($iSel)
Local $sDelay = InputBox(GetLangText("DlgDelayTitle"), GetLangText("DlgDelayPrompt"), "1000")
If @error Then Return
Local $iDelay = Number($sDelay)
If $iDelay < 1 Then $iDelay = 1000
Local $newIndex = $index + 1
_ArrayInsert($aActions, $newIndex)
ReDim $aActions[UBound($aActions)][5]
If $newIndex > 0 Then
$aActions[$newIndex][0] = $aActions[$newIndex - 1][0] + 1
Else
$aActions[$newIndex][0] = 0
EndIf
$aActions[$newIndex][1] = "delay"
$aActions[$newIndex][2] = $iDelay
$aActions[$newIndex][3] = 0
$aActions[$newIndex][4] = $iDelay & "ms"
Local $hListView = GUICtrlGetHandle($List)
Local $dispTime = Round($aActions[$newIndex][0], 0)
Local $sDispType = GetLangText("ActionDelay")
Local $sLabelDisp = _ToDisplayLabel("delay", $aActions[$newIndex][4])
Local $idx = _GUICtrlListView_InsertItem($hListView, $dispTime, $newIndex)
_GUICtrlListView_AddSubItem($hListView, $idx, $sDispType, 1)
_GUICtrlListView_AddSubItem($hListView, $idx, $iDelay, 2)
_GUICtrlListView_AddSubItem($hListView, $idx, "0", 3)
_GUICtrlListView_AddSubItem($hListView, $idx, $sLabelDisp, 4)
_GUICtrlListView_SetItemSelected($List, $newIndex, True, True)
_GUICtrlListView_EnsureVisible($List, $newIndex)
Local $cnt = UBound($aActions)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusActionCount") & $cnt, 1)
EndFunc ;==>InsertDelay
Func DeleteSelectedItem()
Local $iSel = _GUICtrlListView_GetSelectedIndices($List)
If $iSel = "" Or $iSel = -1 Then Return
Local $index = Number($iSel)
_ArrayDelete($aActions, $index)
_GUICtrlListView_DeleteItem($List, $index)
Local $count = UBound($aActions)
If $index >= $count Then $index = $count - 1
If $index >= 0 Then
_GUICtrlListView_SetItemSelected($List, $index, True, True)
_GUICtrlListView_EnsureVisible($List, $index)
EndIf
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusDeleted") & $count & GetLangText("StatusStopRecord2"), 0)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusActionCount") & $count, 1)
EndFunc ;==>DeleteSelectedItem
Func MoveSelectedItem($direction)
Local $iSel = _GUICtrlListView_GetSelectedIndices($List)
If $iSel = "" Or $iSel = -1 Then Return
Local $index = Number($iSel)
Local $newIndex = $index + $direction
If $newIndex < 0 Or $newIndex >= UBound($aActions) Then Return
; 交换两行(如果 _ArraySwap 对 2D 不可靠,可替换为手动交换)
_ArraySwap($aActions, $index, $newIndex)
_GUICtrlListView_DeleteAllItems($List)
For $i = 0 To UBound($aActions) - 1
Local $t = Round($aActions[$i][0], 0)
Local $sType = $aActions[$i][1]
Local $sDispType = ""
Switch $sType
Case "move"
$sDispType = GetLangText("ActionMove")
Case "left_down"
$sDispType = GetLangText("ActionLBtnDown")
Case "left_up"
$sDispType = GetLangText("ActionLBtnUp")
Case "right_down"
$sDispType = GetLangText("ActionRBtnDown")
Case "right_up"
$sDispType = GetLangText("ActionRBtnUp")
Case "key_down"
$sDispType = GetLangText("ActionKeyDown")
Case "key_up"
$sDispType = GetLangText("ActionKeyUp")
Case "delay"
$sDispType = GetLangText("ActionDelay")
EndSwitch
Local $sLabelDisp = _ToDisplayLabel($sType, $aActions[$i][4])
GUICtrlCreateListViewItem( _
$t & "|" & _
$sDispType & "|" & _
$aActions[$i][2] & "|" & _
$aActions[$i][3] & "|" & _
$sLabelDisp, _
$List _
)
Next
_GUICtrlListView_SetItemSelected($List, $newIndex, True, True)
_GUICtrlListView_EnsureVisible($List, $newIndex)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusMoved") & ($newIndex + 1) & GetLangText("StatusStopRecord2"), 0)
EndFunc ;==>MoveSelectedItem
Func SwitchToEnglish()
DllCall("user32.dll", "long", "LoadKeyboardLayout", "str", "00000409", "int", 1)
EndFunc ;==>SwitchToEnglish
; 播放(可中止)
Func PlayMacro()
If $g_bPlaying Then Return
If UBound($aActions) = 0 Then
MsgBox(16, GetLangText("MsgNoActions"), GetLangText("MsgNoActionsText"))
Return
EndIf
SwitchToEnglish()
Local $loop = Number(GUICtrlRead($inputLoop))
If $loop < 1 Then $loop = 1
$g_bAbort = False
$g_bPlaying = True
; 播放期:绑定 ESC 中止
HotKeySet("{ESC}", "_AbortPlay")
; 播放期禁用按钮,避免重入/误触
GUICtrlSetState($btnRecord, $GUI_DISABLE)
GUICtrlSetState($btnStop, $GUI_DISABLE)
GUICtrlSetState($btnPlay, $GUI_DISABLE)
GUICtrlSetState($btnSave, $GUI_DISABLE)
GUICtrlSetState($btnLoad, $GUI_DISABLE)
GUICtrlSetState($btnLang, $GUI_DISABLE)
GUICtrlSetState($btnInsertDelay, $GUI_DISABLE)
GUICtrlSetState($chkRecordMove, $GUI_DISABLE)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusPlaying"), 0)
MsgBox(64, GetLangText("MsgStartPlay"), GetLangText("MsgStartPlayText"))
Local $aborted = False
For $k = 1 To $loop
Local $start = TimerInit()
For $i = 0 To UBound($aActions) - 1
If $g_bAbort Or _IsPressed("1B") Then
$aborted = True
ExitLoop 2
EndIf
Local $time = $aActions[$i][0]
Local $type = $aActions[$i][1]
Local $x = $aActions[$i][2]
Local $y = $aActions[$i][3]
Local $wait = $time - TimerDiff($start)
If $wait > 0 Then
If Not _SleepAbortable($wait) Then
$aborted = True
ExitLoop 2
EndIf
EndIf
If $g_bAbort Or _IsPressed("1B") Then
$aborted = True
ExitLoop 2
EndIf
Switch $type
Case "move", "移动鼠标"
MouseMove($x, $y, 0)
Case "left_down", "左键按下"
MouseMove($x, $y, 0)
MouseDown("left")
Case "left_up", "左键松开"
MouseMove($x, $y, 0)
MouseUp("left")
Case "right_down", "右键按下"
MouseMove($x, $y, 0)
MouseDown("right")
Case "right_up", "右键松开"
MouseMove($x, $y, 0)
MouseUp("right")
Case "delay"
If Not _SleepAbortable($x) Then
$aborted = True
ExitLoop 2
EndIf
Case "key_down", "按键按下"
DllCall("user32.dll", "none", "keybd_event", "byte", $x, "byte", 0, "dword", 0, "ulong_ptr", 0)
Case "key_up", "按键松开"
DllCall("user32.dll", "none", "keybd_event", "byte", $x, "byte", 0, "dword", 2, "ulong_ptr", 0)
EndSwitch
Next
Next
; 统一收尾
_ReleaseSafety()
HotKeySet("{ESC}") ; 解除 ESC
GUICtrlSetState($btnRecord, $GUI_ENABLE)
GUICtrlSetState($btnStop, $GUI_ENABLE)
GUICtrlSetState($btnPlay, $GUI_ENABLE)
GUICtrlSetState($btnSave, $GUI_ENABLE)
GUICtrlSetState($btnLoad, $GUI_ENABLE)
GUICtrlSetState($btnLang, $GUI_ENABLE)
GUICtrlSetState($btnInsertDelay, $GUI_ENABLE)
GUICtrlSetState($chkRecordMove, $GUI_ENABLE)
If $aborted Then
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusPlayStopped"), 0)
MsgBox(64, GetLangText("MsgPlayStopped"), GetLangText("MsgPlayStoppedText"))
Else
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusPlayComplete"), 0)
MsgBox(64, GetLangText("MsgPlayComplete"), GetLangText("MsgPlayCompleteText"))
EndIf
$g_bPlaying = False
EndFunc ;==>PlayMacro
; 保存为 TAB 分隔 5 列;每条记录 1 行,更稳健
Func SaveMacro()
Local $file = FileSaveDialog(GetLangText("DlgSaveTitle"), "", "Macro (*.macro)", 2)
If @error Then Return
Local $h = FileOpen($file, 2)
If $h = -1 Then Return
For $i = 0 To UBound($aActions) - 1
Local $line = _
$aActions[$i][0] & @TAB & _
$aActions[$i][1] & @TAB & _
$aActions[$i][2] & @TAB & _
$aActions[$i][3] & @TAB & _
$aActions[$i][4]
FileWriteLine($h, $line)
Next
FileClose($h)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusSaved") & $file, 0)
EndFunc ;==>SaveMacro
; 加载:优先解析 TAB 分隔;若字段数不足,再尝试按 "\n" 字面分隔(兼容旧版)
Func LoadMacro()
Local $file = FileOpenDialog(GetLangText("DlgLoadTitle"), "", "Macro (*.macro)")
If @error Then Return
Local $lines
_FileReadToArray($file, $lines)
ReDim $aActions[0][5]
_GUICtrlListView_DeleteAllItems($List)
For $i = 1 To $lines[0]
Local $p = StringSplit($lines[$i], @TAB, 2)
If UBound($p) < 4 Then
; 兼容旧格式:尝试用 "\n" 文本分隔
Local $p2 = StringSplit($lines[$i], "\n", 2)
If UBound($p2) >= 4 Then
$p = $p2
Else
ContinueLoop
EndIf
EndIf
Local $n = UBound($aActions)
ReDim $aActions[$n + 1][5]
$aActions[$n][0] = Number($p[0])
$aActions[$n][1] = $p[1]
$aActions[$n][2] = $p[2]
$aActions[$n][3] = $p[3]
If UBound($p) >= 5 Then
$aActions[$n][4] = $p[4]
Else
; 旧格式补写
Switch $aActions[$n][1]
Case "key_down", "key_up"
$aActions[$n][4] = _VKToName($aActions[$n][2])
Case "left_down", "left_up"
$aActions[$n][4] = "LButton"
Case "right_down", "right_up"
$aActions[$n][4] = "RButton"
Case "middle_down", "middle_up"
$aActions[$n][4] = "MButton"
Case "delay"
$aActions[$n][4] = $aActions[$n][2] & "ms"
Case Else
$aActions[$n][4] = ""
EndSwitch
EndIf
Local $sDispType = ""
Switch $aActions[$n][1]
Case "move"
$sDispType = GetLangText("ActionMove")
Case "left_down"
$sDispType = GetLangText("ActionLBtnDown")
Case "left_up"
$sDispType = GetLangText("ActionLBtnUp")
Case "right_down"
$sDispType = GetLangText("ActionRBtnDown")
Case "right_up"
$sDispType = GetLangText("ActionRBtnUp")
Case "key_down"
$sDispType = GetLangText("ActionKeyDown")
Case "key_up"
$sDispType = GetLangText("ActionKeyUp")
Case "delay"
$sDispType = GetLangText("ActionDelay")
Case Else
$sDispType = $aActions[$n][1]
EndSwitch
Local $sLabelDisp = _ToDisplayLabel($aActions[$n][1], $aActions[$n][4])
GUICtrlCreateListViewItem( _
Round($aActions[$n][0], 0) & "|" & _
$sDispType & "|" & _
$aActions[$n][2] & "|" & _
$aActions[$n][3] & "|" & _
$sLabelDisp, _
$List _
)
Next
Local $cnt = UBound($aActions)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusLoaded") & $cnt & GetLangText("StatusStopRecord2"), 0)
_GUICtrlStatusBar_SetText($hStatus, GetLangText("StatusActionCount") & $cnt, 1)
EndFunc ;==>LoadMacro

浙公网安备 33010602011771号