delphi 键盘按键替换工具
设计界面:

功能设计:
1.按下窗体右上角的关闭按钮时,程序隐藏
2.按下ctrl+~组合键时,程序隐藏或者显示
3.程序最多支持三组按键更替,PressCom指定的按键会被替换为TargetCom按键,当某一组任意值为空是,该组不执行更替任务
4.当scGPCheckBox1勾选时,程序才执行更替任务,否则不执行
5.点击ExitBtn按钮时,程序才真正退出
6.所有的TscGPComboBox都要添加所有键盘的按键
7.这三组控件不能重复替换相同的按键
以下代码通过AI生成,功能已实测通过.
unit ButtonReplacementTool; interface uses winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, scGPControls, scGPExtControls, scControls, System.Win.Registry; type // ============================================================ // KBDLLHOOKSTRUCT - 低级键盘钩子结构体 // 用于接收系统级键盘事件的信息 // ============================================================ KBDLLHOOKSTRUCT = record vkCode: DWORD; // 虚拟键码(Virtual Key Code) scanCode: DWORD; // 扫描码(硬件扫描码) flags: DWORD; // 标志位(扩展键、ALT键等) time: DWORD; // 事件的时间戳 dwExtraInfo: ULONG_PTR; // 附加信息 end; PKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT; // 指向结构体的指针类型 TMain = class(TForm) // ============================================================ // 界面组件声明 // ============================================================ scGPCheckBox1: TscGPCheckBox; // 启用/禁用按键映射的复选框 PressCom1: TscGPComboBox; // 第一组:被替换的按键(按下此键触发替换) TargetCom1: TscGPComboBox; // 第一组:目标按键(替换为按此键) scGPLabel1: TscGPLabel; // 第一组的标签 SaveBtn: TscGPButton; // 保存设置按钮 ExitBtn: TscGPButton; // 退出程序按钮 PressCom2: TscGPComboBox; // 第二组:被替换的按键 TargetCom2: TscGPComboBox; // 第二组:目标按键 scGPLabel2: TscGPLabel; // 第二组的标签 PressCom3: TscGPComboBox; // 第三组:被替换的按键 TargetCom3: TscGPComboBox; // 第三组:目标按键 scGPLabel3: TscGPLabel; // 第三组的标签 // ============================================================ // 事件处理方法 // ============================================================ procedure FormCreate(Sender: TObject); // 窗体创建时 procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); // 窗体关闭询问 procedure ExitBtnClick(Sender: TObject); // 退出按钮点击 procedure SaveBtnClick(Sender: TObject); // 保存按钮点击 procedure FormShow(Sender: TObject); // 窗体显示时 procedure scGPCheckBox1Click(Sender: TObject); // 复选框点击 procedure FormDestroy(Sender: TObject); // 窗体销毁时 private { Private declarations } FHotKeyId: Integer; // 全局热键的ID FIsHidden: Boolean; // 窗体是否处于隐藏状态 FKeyboardHook: HHOOK; // 键盘钩子句柄 // ============================================================ // 按键映射数组:存储三组按键映射配置 // 每组包含:被替换的按键、目标按键、是否激活 // ============================================================ FKeyMapping: array[1..3] of record PressKey: DWORD; // 被替换的按键(按下此键) TargetKey: DWORD; // 目标按键(替换为按此键) Active: Boolean; // 该组映射是否激活 end; // ============================================================ // 私有方法声明 // ============================================================ procedure InitializeComboBoxes; // 初始化下拉组合框,填充所有按键选项 procedure LoadSettings; // 从注册表加载保存的设置 procedure SaveSettings; // 保存设置到注册表 procedure UpdateKeyMapping; // 更新按键映射配置 procedure ApplyKeyMapping; // 应用按键映射(安装键盘钩子) procedure RemoveKeyMapping; // 移除按键映射(卸载键盘钩子) procedure HandleHotKey(var MSG: TMessage); // 处理全局热键消息 public { Public declarations } procedure WndProc(var MSG: TMessage); override; // 重写窗口过程以接收热键消息 end; var Main: TMain; HookInstance: HHOOK = 0; // 全局键盘钩子句柄(用于低级键盘钩子回调) IsProcessingSimulatedKey: Boolean = False; // 防止递归的标志 (防止同一个按键即时当前按键又是目标按键) implementation {$R *.dfm} uses winapi.ShellAPI; // ============================================================ // KeyboardProc - 低级键盘钩子回调函数 // 当系统检测到键盘事件时,此函数被调用 // 参数: // nCode - 钩子代码(HC_ACTION 表示有键盘事件) // wParam - 消息类型(WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP) // lParam - 指向 KBDLLHOOKSTRUCT 结构体的指针 // 返回值: // 0 - 允许按键事件继续传递 // 1 - 阻止按键事件(吃掉该按键) // ============================================================ function KeyboardProc(nCode: Integer; wParam: wParam; lParam: lParam): LRESULT; stdcall; var p: PKBDLLHOOKSTRUCT; i: Integer; KeyDown: Boolean; begin // 先调用下一个钩子 Result := CallNextHookEx(HookInstance, nCode, wParam, lParam); // ============================================================ // 防止递归:如果正在处理模拟按键,直接退出 // 这样可以避免 PageUp → Home → Delete 的连锁反应 // ============================================================ if IsProcessingSimulatedKey then Exit; if nCode = HC_ACTION then begin p := PKBDLLHOOKSTRUCT(lParam); if Assigned(p) then begin KeyDown := (wParam = WM_KEYDOWN) or (wParam = WM_SYSKEYDOWN); if KeyDown then begin for i := 1 to 3 do begin if Main.FKeyMapping[i].Active and (Main.FKeyMapping[i].PressKey = p.vkCode) then begin // 设置标志,防止模拟按键再次触发钩子 IsProcessingSimulatedKey := True; try // 模拟按下目标按键 keybd_event(Main.FKeyMapping[i].TargetKey, 0, 0, 0); // 模拟释放目标按键 keybd_event(Main.FKeyMapping[i].TargetKey, 0, KEYEVENTF_KEYUP, 0); finally // 重置标志 IsProcessingSimulatedKey := False; end; // 阻止原始按键 Result := 1; Exit; end; end; end; end; end; end; // ============================================================ // GetKeyName - 将虚拟键码转换为可读的按键名称 // 参数:KeyCode - 虚拟键码(0-255) // 返回:按键的字符串描述 // ============================================================ function GetKeyName(KeyCode: Integer): string; begin Result := ''; // ============================================================ // 1. 字母键 A-Z (65-90) // ============================================================ if (KeyCode >= 65) and (KeyCode <= 90) then begin Result := Chr(KeyCode); Exit; end; // ============================================================ // 2. 数字键 0-9 (48-57) // ============================================================ if (KeyCode >= 48) and (KeyCode <= 57) then begin Result := Chr(KeyCode); Exit; end; // ============================================================ // 3. 功能键 F1-F12 (F13-F24 不常用,去掉) // ============================================================ if (KeyCode >= VK_F1) and (KeyCode <= VK_F12) then begin Result := 'F' + IntToStr(KeyCode - VK_F1 + 1); Exit; end; // ============================================================ // 4. 小键盘数字键 Num0-Num9 // ============================================================ if (KeyCode >= VK_NUMPAD0) and (KeyCode <= VK_NUMPAD9) then begin Result := 'Num' + IntToStr(KeyCode - VK_NUMPAD0); Exit; end; // ============================================================ // 5. 常用特殊键(只保留最常用的) // ============================================================ case KeyCode of // ---------- 编辑键 ---------- VK_BACK: Result := 'Backspace'; // 退格键 VK_TAB: Result := 'Tab'; // Tab键 VK_RETURN: Result := 'Enter'; // 回车键 VK_ESCAPE: Result := 'Esc'; // Escape键 VK_SPACE: Result := 'Space'; // 空格键 VK_DELETE: Result := 'Delete'; // Delete键 VK_INSERT: Result := 'Insert'; // Insert键 // ---------- 光标控制键 ---------- VK_HOME: Result := 'Home'; // Home键 VK_END: Result := 'End'; // End键 VK_PRIOR: Result := 'PageUp'; // PageUp键 VK_NEXT: Result := 'PageDown'; // PageDown键 VK_LEFT: Result := 'Left'; // 左箭头 VK_UP: Result := 'Up'; // 上箭头 VK_RIGHT: Result := 'Right'; // 右箭头 VK_DOWN: Result := 'Down'; // 下箭头 // ---------- 修饰键 ---------- VK_SHIFT: Result := 'Shift'; // Shift键 VK_CONTROL: Result := 'Ctrl'; // Ctrl键 VK_MENU: Result := 'Alt'; // Alt键 VK_CAPITAL: Result := 'CapsLock'; // CapsLock键 VK_NUMLOCK: Result := 'NumLock'; // NumLock键 VK_SCROLL: Result := 'ScrollLock'; // ScrollLock键 // ---------- Windows键 ---------- VK_LWIN: Result := 'LeftWin'; // 左Windows键 VK_RWIN: Result := 'RightWin'; // 右Windows键 VK_APPS: Result := 'Apps'; // 应用程序键(右键菜单) // ---------- 符号键 ---------- VK_OEM_1: Result := '; :'; // 分号键 VK_OEM_PLUS: Result := '+ ='; // 加号/等号键 VK_OEM_COMMA: Result := ', <'; // 逗号键 VK_OEM_MINUS: Result := '- _'; // 减号/下划线键 VK_OEM_PERIOD: Result := '. >'; // 句号键 VK_OEM_2: Result := '/ ?'; // 斜杠键 VK_OEM_3: Result := '` ~'; // 反引号键 VK_OEM_4: Result := '[ {'; // 左方括号 VK_OEM_5: Result := '\ |'; // 反斜杠 VK_OEM_6: Result := '] }'; // 右方括号 VK_OEM_7: Result := ''' "'; // 单引号键 // ---------- 截图键 ---------- VK_SNAPSHOT: Result := 'PrintScreen'; // 截屏键 // ---------- 暂停/中断 ---------- VK_PAUSE: Result := 'Pause'; // Pause键 else // 未识别的按键返回空字符串(不显示) Result := ''; end; end; // ============================================================ // TMain.FormCreate - 窗体创建事件 // 在程序启动时执行初始化操作 // ============================================================ procedure TMain.FormCreate(Sender: TObject); begin // 设置程序主窗体在任务栏显示 Application.MainFormOnTaskBar := True; // 初始化窗体可见状态 FIsHidden := False; // 初始化键盘钩子句柄为0(表示未安装) FKeyboardHook := 0; // ============================================================ // 注册全局热键 Ctrl+~ (用于显示/隐藏程序主窗口) // GlobalAddAtom - 创建全局原子,确保热键ID唯一 // RegisterHotKey - 注册热键 // 参数1: 窗口句柄(接收热键消息的窗口) // 参数2: 热键ID // 参数3: 修饰键(MOD_CONTROL = Ctrl键) // 参数4: 虚拟键码(VK_OEM_3 = ~ 键) // ============================================================ FHotKeyId := GlobalAddAtom('MyHotKey') - $C000; if not RegisterHotKey(Handle, FHotKeyId, MOD_CONTROL, VK_OEM_3) then begin ShowMessage('无法注册热键 Ctrl+~'); end; // 初始化下拉组合框(填充所有按键选项) InitializeComboBoxes; // 从注册表加载保存的设置 LoadSettings; // 更新按键映射(根据加载的设置) UpdateKeyMapping; end; // ============================================================ // TMain.FormDestroy - 窗体销毁事件 // 在程序退出时清理资源 // ============================================================ procedure TMain.FormDestroy(Sender: TObject); begin // 移除键盘钩子(如果已安装) RemoveKeyMapping; // 注销全局热键(如果已注册) if FHotKeyId <> 0 then UnregisterHotKey(Handle, FHotKeyId); end; // ============================================================ // TMain.FormCloseQuery - 窗体关闭询问事件 // 当用户点击窗口右上角的 X 按钮时触发 // 阻止直接关闭,改为隐藏到系统托盘(实际是隐藏到任务栏) // ============================================================ procedure TMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin CanClose := False; // 阻止关闭 Hide; // 隐藏窗体 FIsHidden := True; // 标记为隐藏状态 end; // ============================================================ // TMain.ExitBtnClick - 退出按钮点击事件 // 真正退出程序,释放所有资源 // ============================================================ procedure TMain.ExitBtnClick(Sender: TObject); begin // 移除键盘钩子 RemoveKeyMapping; // 注销热键 if FHotKeyId <> 0 then UnregisterHotKey(Handle, FHotKeyId); // 终止应用程序 Application.Terminate; end; // ============================================================ // TMain.SaveBtnClick - 保存按钮点击事件 // 保存当前设置到注册表并应用 // ============================================================ procedure TMain.SaveBtnClick(Sender: TObject); begin SaveSettings; // 保存设置到注册表 UpdateKeyMapping; // 更新并应用按键映射 ShowMessage('设置已保存并应用'); end; // ============================================================ // TMain.FormShow - 窗体显示事件 // 当窗体从隐藏状态变为显示时触发 // ============================================================ procedure TMain.FormShow(Sender: TObject); begin FIsHidden := False; // 标记为可见状态 end; // ============================================================ // TMain.scGPCheckBox1Click - 复选框点击事件 // 启用或禁用按键映射功能 // ============================================================ procedure TMain.scGPCheckBox1Click(Sender: TObject); begin if scGPCheckBox1.Checked then ApplyKeyMapping // 勾选:应用按键映射(安装钩子) else RemoveKeyMapping; // 取消勾选:移除按键映射(卸载钩子) end; // ============================================================ // TMain.InitializeComboBoxes - 初始化下拉组合框 // 向所有组合框添加按键选项列表 // ============================================================ procedure TMain.InitializeComboBoxes; var i: Integer; // 循环计数器(遍历按键码) KeyName: string; // 按键名称 ComboBoxes: array[1..6] of TscGPComboBox; // 所有组合框数组 begin // 将6个组合框赋值到数组中,方便统一处理 ComboBoxes[1] := PressCom1; ComboBoxes[2] := TargetCom1; ComboBoxes[3] := PressCom2; ComboBoxes[4] := TargetCom2; ComboBoxes[5] := PressCom3; ComboBoxes[6] := TargetCom3; // ---------- 首先为所有组合框添加一个空选项(None) ---------- for var j := 1 to 6 do begin ComboBoxes[j].Items.Clear; // 清空原有项目 var temp := ComboBoxes[j].Items.Add; // 添加新项目 temp.Caption := 'None'; // 显示为 "None" end; // ---------- 遍历所有虚拟键码(0-255)添加按键选项 ---------- for i := 0 to 255 do begin // 跳过鼠标按键(左键、右键、中键等) if (i >= VK_LBUTTON) and (i <= VK_RBUTTON) then Continue; // 跳过 Cancel 键 if i = VK_CANCEL then Continue; // 获取按键的可读名称 KeyName := GetKeyName(i); if KeyName='' then Continue; // 将按键添加到所有6个组合框中 // 格式:按键名称|键码(例如 "A|65") // 这样既显示可读名称,又保留了键码值 for var j := 1 to 6 do begin var temp := ComboBoxes[j].Items.Add; temp.Caption := KeyName + '|' + IntToStr(i); end; end; // ---------- 设置默认选中 "None" ---------- for var j := 1 to 6 do ComboBoxes[j].ItemIndex := 0; end; // ============================================================ // TMain.LoadSettings - 从注册表加载设置 // 读取之前保存的按键映射配置 // ============================================================ procedure TMain.LoadSettings; var Reg: TRegistry; // 注册表对象 begin Reg := TRegistry.Create; try Reg.RootKey := HKEY_CURRENT_USER; // 使用当前用户注册表根键 // 打开注册表键 "Software\KeyMapper" if Reg.OpenKey('Software\KeyMapper', False) then begin // 加载三组按键的索引值 PressCom1.ItemIndex := Reg.ReadInteger('Press1'); TargetCom1.ItemIndex := Reg.ReadInteger('Target1'); PressCom2.ItemIndex := Reg.ReadInteger('Press2'); TargetCom2.ItemIndex := Reg.ReadInteger('Target2'); PressCom3.ItemIndex := Reg.ReadInteger('Press3'); TargetCom3.ItemIndex := Reg.ReadInteger('Target3'); // 加载启用状态 scGPCheckBox1.Checked := Reg.ReadBool('Enabled'); end; finally Reg.Free; // 释放注册表对象 end; end; // ============================================================ // TMain.SaveSettings - 保存设置到注册表 // 保存当前按键映射配置以便下次启动时加载 // ============================================================ procedure TMain.SaveSettings; var Reg: TRegistry; // 注册表对象 begin Reg := TRegistry.Create; try Reg.RootKey := HKEY_CURRENT_USER; // 使用当前用户注册表根键 // 打开(或创建)注册表键 "Software\KeyMapper" if Reg.OpenKey('Software\KeyMapper', True) then begin // 保存三组按键的索引值 Reg.WriteInteger('Press1', PressCom1.ItemIndex); Reg.WriteInteger('Target1', TargetCom1.ItemIndex); Reg.WriteInteger('Press2', PressCom2.ItemIndex); Reg.WriteInteger('Target2', TargetCom2.ItemIndex); Reg.WriteInteger('Press3', PressCom3.ItemIndex); Reg.WriteInteger('Target3', TargetCom3.ItemIndex); // 保存启用状态 Reg.WriteBool('Enabled', scGPCheckBox1.Checked); end; finally Reg.Free; // 释放注册表对象 end; end; // ============================================================ // TMain.UpdateKeyMapping - 更新按键映射配置 // 从组合框读取用户选择的按键,更新内存中的映射数组 // 并检查是否有重复的按键映射 // ============================================================ procedure TMain.UpdateKeyMapping; // ---------------------------------------------------------- // 辅助函数:从组合框中获取按键值 // 参数:ComboBox - 组合框对象 // 返回:选中的按键的虚拟键码(如果没有选中则返回0) // ---------------------------------------------------------- function GetKeyValue(ComboBox: TscGPComboBox): DWORD; var ItemText: string; // 项目的显示文本 P: Integer; // 分隔符位置 begin Result := 0; // 默认返回0(表示未选择) // 如果选中的不是第一个(None)项目 if ComboBox.ItemIndex > 0 then begin ItemText := ComboBox.Items[ComboBox.ItemIndex].Caption; P := Pos('|', ItemText); // 查找分隔符 "|" if P > 0 then // 提取分隔符后面的数字(键码) Result := StrToIntDef(Copy(ItemText, P + 1, MaxInt), 0); end; end; begin // ---------- 先移除现有的按键映射(卸载钩子) ---------- RemoveKeyMapping; // ---------- 从组合框读取三组按键映射 ---------- // 第一组 FKeyMapping[1].PressKey := GetKeyValue(PressCom1); FKeyMapping[1].TargetKey := GetKeyValue(TargetCom1); FKeyMapping[1].Active := (PressCom1.ItemIndex > 0) and (TargetCom1.ItemIndex > 0); // 第二组 FKeyMapping[2].PressKey := GetKeyValue(PressCom2); FKeyMapping[2].TargetKey := GetKeyValue(TargetCom2); FKeyMapping[2].Active := (PressCom2.ItemIndex > 0) and (TargetCom2.ItemIndex > 0); // 第三组 FKeyMapping[3].PressKey := GetKeyValue(PressCom3); FKeyMapping[3].TargetKey := GetKeyValue(TargetCom3); FKeyMapping[3].Active := (PressCom3.ItemIndex > 0) and (TargetCom3.ItemIndex > 0); // ---------- 检查是否有重复的被替换按键 ---------- // 组1 和 组2 不能相同 if FKeyMapping[1].Active and FKeyMapping[2].Active and (FKeyMapping[1].PressKey = FKeyMapping[2].PressKey) then begin ShowMessage('组1和组2的按键不能相同'); Exit; end; // 组1 和 组3 不能相同 if FKeyMapping[1].Active and FKeyMapping[3].Active and (FKeyMapping[1].PressKey = FKeyMapping[3].PressKey) then begin ShowMessage('组1和组3的按键不能相同'); Exit; end; // 组2 和 组3 不能相同 if FKeyMapping[2].Active and FKeyMapping[3].Active and (FKeyMapping[2].PressKey = FKeyMapping[3].PressKey) then begin ShowMessage('组2和组3的按键不能相同'); Exit; end; // ---------- 如果启用状态为真,则应用按键映射 ---------- if scGPCheckBox1.Checked then ApplyKeyMapping; end; // ============================================================ // TMain.ApplyKeyMapping - 应用按键映射 // 安装低级键盘钩子,开始拦截并替换按键 // ============================================================ procedure TMain.ApplyKeyMapping; begin // 如果钩子已经存在,先移除(避免重复安装) if HookInstance <> 0 then RemoveKeyMapping; // ============================================================ // 安装低级键盘钩子 // SetWindowsHookEx 参数说明: // 参数1: WH_KEYBOARD_LL - 低级键盘钩子类型 // 参数2: @KeyboardProc - 钩子回调函数地址 // 参数3: HInstance - 当前应用程序实例句柄 // 参数4: 0 - 全局钩子(监控所有线程) // ============================================================ HookInstance := SetWindowsHookEx(WH_KEYBOARD_LL, @KeyboardProc, HInstance, 0); // 检查钩子是否安装成功 if HookInstance = 0 then ShowMessage('安装键盘钩子失败!错误代码:' + IntToStr(GetLastError)) else FKeyboardHook := HookInstance; // 保存钩子句柄 end; // ============================================================ // TMain.RemoveKeyMapping - 移除按键映射 // 卸载键盘钩子,停止拦截按键 // ============================================================ procedure TMain.RemoveKeyMapping; begin // 如果钩子已安装(句柄不为0) if HookInstance <> 0 then begin UnhookWindowsHookEx(HookInstance); // 卸载钩子 HookInstance := 0; // 重置句柄 FKeyboardHook := 0; // 重置保存的句柄 end; end; // ============================================================ // TMain.WndProc - 重写的窗口过程 // 用于接收和处理 Windows 消息 // 特别用于接收全局热键消息(WM_HOTKEY) // ============================================================ procedure TMain.WndProc(var Msg: TMessage); begin // 如果收到热键消息,交给 HandleHotKey 处理 if Msg.Msg = WM_HOTKEY then HandleHotKey(Msg); // 调用父类的窗口过程,确保其他消息正常处理 inherited WndProc(Msg); end; // ============================================================ // TMain.HandleHotKey - 处理全局热键消息 // 当用户按下 Ctrl+~ 时,切换窗体的显示/隐藏状态 // ============================================================ procedure TMain.HandleHotKey(var Msg: TMessage); begin // 检查热键ID是否匹配 if Msg.WParam = FHotKeyId then begin if FIsHidden then begin // 如果窗体当前隐藏,则显示并置前 Show; // 显示窗体 Application.BringToFront; // 将窗体置于最前 FIsHidden := False; // 更新状态为可见 end else begin // 如果窗体当前可见,则隐藏 Hide; // 隐藏窗体 FIsHidden := True; // 更新状态为隐藏 end; end; end; end.

浙公网安备 33010602011771号