如何在Windows上用Dart实现全局按键拦截与替换?
全局键盘拦截与替换实现方案(Dart/Windows)
一、可用Dart包推荐
目前pub.dev没有专门做全局键盘钩子的现成包,但可以用官方维护的win32包直接调用Windows原生API来实现——这个包封装了大部分Win32 API,完全能满足你的需求。
二、基于Win32 API的实现思路
要实现全局按键拦截+替换,核心要做的就是设置全局低级别键盘钩子,在系统处理按键前截获消息,然后拦截原按键、发送替代字符。
关键步骤:
安装win32包
在项目的pubspec.yaml里添加依赖:dependencies: win32: ^5.0.0注册全局键盘钩子
用SetWindowsHookExWAPI注册WH_KEYBOARD_LL类型的钩子——这是低级别钩子,不需要注入到其他进程,实现起来更简单。编写钩子回调函数
在回调里判断按下的按键,如果是需要替换的:- 返回
1,直接拦截原按键,不让它传递到其他程序; - 用
SendInputAPI发送替代字符的模拟输入消息。
- 返回
核心代码示例
下面是简化的实现框架,你可以根据自己的音译规则修改:
import 'dart:ffi'; import 'package:win32/win32.dart'; // 钩子回调函数 int KeyboardHookCallback(int nCode, int wParam, int lParam) { if (nCode >= HC_ACTION) { final keyInfo = PKBDLLHOOKSTRUCT(lParam); final vkCode = keyInfo.ref.vkCode; // 示例:拦截A键,替换为俄语字符Б if (wParam == WM_KEYDOWN && vkCode == 0x41) { // 发送替代字符 final input = INPUT.allocate(); input.ref.type = INPUT_KEYBOARD; input.ref.ki.wVk = 0; // 使用Unicode模式输入 input.ref.ki.wScan = 0x0411; // Б的Unicode编码值 input.ref.ki.dwFlags = KEYEVENTF_UNICODE; SendInput(1, input.addressOf, sizeOf<INPUT>()); input.free(); // 返回1表示已处理,拦截原按键 return 1; } } // 非目标按键,交给系统后续处理 return CallNextHookEx(NULL, nCode, wParam, lParam); } void main() { // 获取当前程序的模块句柄 final hModule = GetModuleHandleW(nullptr); // 注册全局键盘钩子 final hHook = SetWindowsHookExW(WH_KEYBOARD_LL, Pointer.fromFunction<KeyboardHookCallbackNative>(KeyboardHookCallback, 0), hModule, 0); // 保持消息循环,确保程序后台运行时仍能监听按键 final msg = MSG.allocate(); while (GetMessageW(msg.addressOf, NULL, 0, 0) != 0) { TranslateMessage(msg.addressOf); DispatchMessageW(msg.addressOf); } msg.free(); // 退出前卸载钩子,避免内存泄漏 UnhookWindowsHookEx(hHook); } // 回调函数的Native类型定义,需与Win32要求一致 typedef KeyboardHookCallbackNative = Int32 Function(Int32 nCode, Int32 wParam, IntPtr lParam);
注意事项
- 消息循环不能中断:程序最小化后需保持运行,依赖消息循环接收钩子消息,不能随意退出。
- Unicode输入更可靠:
SendInput的KEYEVENTF_UNICODE标志可直接发送任意Unicode字符,无需依赖输入法状态,比模拟按键组合更稳定。 - 权限匹配:如果目标程序以管理员权限运行,你的程序也需要以管理员权限启动才能生效。
- 钩子卸载:程序退出前必须调用
UnhookWindowsHookEx,避免钩子残留导致系统资源浪费。
内容的提问来源于stack exchange,提问作者Yaros
相关产品推荐
相关产品推荐

