已掌握C++热键实现方法,如何实现类似AutoHotkey的热字符串?
用C++实现AutoHotkey风格的热字符串功能
嘿,既然你已经搞定了C++热键的实现,那热字符串的需求其实逻辑上是相通的——本质就是监控用户的键盘输入流,匹配预设的文本序列,触发对应动作。我来给你拆解下思路,再附个实用的代码例子。
首先先明确下AutoHotkey里的热字符串逻辑,比如你可能写过这样的AHK代码:
::runnotepad::Run, notepad.exe
::btw::by the way
前者是输入runnotepad后自动启动记事本,后者是把输入的btw替换成by the way。要在C++里实现这个,核心依赖Windows的全局键盘钩子来捕获输入,再做字符串匹配。
核心实现步骤
- 安装全局键盘钩子:用
SetWindowsHookEx安装WH_KEYBOARD_LL类型的钩子,这种钩子不需要注入其他进程,实现起来更简单。 - 维护输入缓存:用一个字符串变量记录用户最近输入的字符(注意区分大小写,或者按需忽略),超过一定长度就截断,避免冗余。
- 匹配触发字符串:每次有新字符输入(注意处理按键按下/释放,只在按下时记录),更新缓存后检查是否以预设的热字符串结尾。
- 触发动作并清理输入:匹配成功后,先发送对应次数的退格键删除输入的触发字符串,再执行你的动作(启动程序、输入替换文本等),最后重置缓存。
- 卸载钩子:程序退出时一定要用
UnhookWindowsHookEx卸载钩子,避免资源泄漏。
完整代码示例
下面是一个实现“输入runnotepad启动记事本”和“输入btw替换为by the way”的完整C++代码:
#include <windows.h> #include <string> #include <vector> // 存储热字符串和对应的动作 struct Hotstring { std::string trigger; enum ActionType { RUN_PROGRAM, REPLACE_TEXT } type; std::string actionContent; }; std::vector<Hotstring> hotstrings = { {"runnotepad", Hotstring::RUN_PROGRAM, "notepad.exe"}, {"btw", Hotstring::REPLACE_TEXT, "by the way"} }; std::string inputBuffer; HHOOK keyboardHook; // 钩子回调函数 LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode >= 0 && wParam == WM_KEYDOWN) { KBDLLHOOKSTRUCT* pKeyBoard = (KBDLLHOOKSTRUCT*)lParam; // 忽略功能键、控制键,只处理可打印字符 if (pKeyBoard->vkCode >= 0x20 && pKeyBoard->vkCode <= 0x7E) { // 获取字符(考虑Shift键影响大小写) char ch = MapVirtualKey(pKeyBoard->vkCode, MAPVK_VK_TO_CHAR); if (GetAsyncKeyState(VK_SHIFT) & 0x8000) { ch = toupper(ch); } else { ch = tolower(ch); } inputBuffer += ch; // 检查是否匹配热字符串(只检查结尾,避免误触发) for (const auto& hs : hotstrings) { size_t triggerLen = hs.trigger.length(); if (inputBuffer.length() >= triggerLen && inputBuffer.substr(inputBuffer.length() - triggerLen) == hs.trigger) { // 发送退格键删除触发字符串 for (size_t i = 0; i < triggerLen; ++i) { INPUT input = {0}; input.type = INPUT_KEYBOARD; input.ki.wVk = VK_BACK; SendInput(1, &input, sizeof(INPUT)); input.ki.dwFlags = KEYEVENTF_KEYUP; SendInput(1, &input, sizeof(INPUT)); } // 执行对应动作 if (hs.type == Hotstring::RUN_PROGRAM) { ShellExecuteA(NULL, "open", hs.actionContent.c_str(), NULL, NULL, SW_SHOWNORMAL); } else if (hs.type == Hotstring::REPLACE_TEXT) { // 发送替换文本的按键 for (char c : hs.actionContent) { INPUT input = {0}; input.type = INPUT_KEYBOARD; input.ki.wVk = 0; input.ki.wScan = c; input.ki.dwFlags = KEYEVENTF_UNICODE; SendInput(1, &input, sizeof(INPUT)); input.ki.dwFlags |= KEYEVENTF_KEYUP; SendInput(1, &input, sizeof(INPUT)); } } // 重置输入缓存,避免重复触发 inputBuffer.clear(); break; } } // 限制缓存长度,避免无限增长 if (inputBuffer.length() > 20) { inputBuffer = inputBuffer.substr(inputBuffer.length() - 20); } } } return CallNextHookEx(keyboardHook, nCode, wParam, lParam); } int main() { // 安装全局键盘钩子 keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, GetModuleHandle(NULL), 0); if (!keyboardHook) { MessageBoxA(NULL, "Failed to install hook!", "Error", MB_ICONERROR); return 1; } // 消息循环,保持程序运行 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // 卸载钩子 UnhookWindowsHookEx(keyboardHook); return 0; }
注意事项
- 字符大小写:上面的代码把输入转为小写后匹配,如果你需要区分大小写,可以去掉
tolower/toupper的处理。 - 特殊字符:如果需要支持特殊字符(比如中文、符号),需要调整字符获取的逻辑,改用Unicode相关的API(比如
MapVirtualKeyW、SendInputW)。 - 钩子权限:全局钩子需要程序保持运行,所以代码里有一个消息循环,你可以把它做成后台服务或者托盘程序。
- 误触发避免:建议把触发字符串设得稍微长一点,或者用特殊前缀(比如AHK里的冒号),避免日常输入误触发。
内容的提问来源于stack exchange,提问作者user9401759
相关产品推荐
相关产品推荐

