我正在开发一个用于处理VSTO插件中键盘输入的类,目前我已经使用Windows钩子来实现,并取得了相对成功。
以下是代码:
//.....
private const int WH_KEYBOARD = 2;
private const int WH_MOUSE = 7;
private enum WM : uint {
KEYDOWN = 0x0100,
KEYFIRST = 0x0100,
KEYLAST = 0x0108,
KEYUP = 0x0101,
MOUSELEFTDBLCLICK = 0x0203,
MOUSELEFTBTNDOWN = 0x0201,
MOUSELEFTBTNUP = 0x0202,
MOUSEMIDDBLCLICK = 0x0209,
MOUSEMIDBTNDOWN = 0x0207,
MOUSEMIDBTNUP = 0x0208,
MOUSERIGHTDBLCLK = 0x0206,
MOUSERIGHTBTNDOWN = 0x0204,
MOUSERIGHTBTNUP = 0x0205
}
private hookProcedure proc;
private static IntPtr hookID = IntPtr.Zero;
//Enganches
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr SetWindowsHookEx(int hookId, hookProcedure proc, IntPtr hInstance, uint thread);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern bool unHookWindowsHookEx(int hookId);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CallNextHookEx(IntPtr hookId, int ncode, IntPtr wparam, IntPtr lparam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string name);
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetCurrentThreadId();
public CPInputListener() {
proc = keyBoardCallback;
hookID = setHook(proc);
}
private IntPtr setHook(hookProcedure procedure){
ProcessModule module = Process.GetCurrentProcess().MainModule;
uint threadId = (uint)GetCurrentThreadId();
return SetWindowsHookEx(WH_KEYBOARD, procedure, IntPtr.Zero, threadId);
}
public void stopListeningAll() {
unHookWindowsHookEx(WH_KEYBOARD);//For now
}
private IntPtr keyBoardCallback(int ncode, IntPtr wParam, IntPtr lParam) {
if (ncode >= 0) {
//LPARAM pretty useless
Keys key = (Keys)wParam;
KeyEventArgs args = new KeyEventArgs(key);
onKeyDown(args);//for now
}
return CallNextHookEx(hookID, ncode, wParam, lParam);
}
//....
我成功地接收到键盘输入,但是这里有个大谜团:每次按下一个键,无论速度多快,事件(onKeyDown)都会被调用10次,恰好不多也不少。
如果长按键,则事件将继续被调用,但是每次调用10次,而不是只调用一次。
到目前为止,我已经尝试过:
1. 使用wParam在KeyUp时调用所需的事件:似乎不起作用,在所有处理KeyDown和KeyUp事件的代码中,都使用IntPtr wParam,但是我只能从该变量中检索到按键代码,这并没有帮助。
2. 使用lParam或nCode:这些变量在这10个调用之间给出不一致的值,ncode倾向于检索0和3,而lParam则是一些看起来像未管理的内存地址的值...
我期望的是:
当按下键时,onKeyDown只被调用一次,或者在另一方面,能够调用onKeyUp方法,我期望它每次释放一个键时只被调用一次。
如何绕过这个问题:
如果找不到合理的答案,我想使用自定义定时器来丢弃所有那些调用,并仅使用最后一个调用,如果其他所有方法都失败了,您会推荐这样做吗?
非常感谢!保持快乐和善良!:D