最近用钩子做一个改键小软件,有些问题想不明白。先看代码:
private const int WM_KEYDOWN = 0x100;
private const int WM_KEYUP = 0x101;
private const int WM_SYSKEYDOWN = 0x104;
private const int WM_SYSKEYUP = 0x105;
public const int WH_KEYBOARD_LL = 13;public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);public event KeyEventHandler OnKeyDownEvent;
public event KeyEventHandler OnKeyUpEvent;
public event KeyPressEventHandler OnKeyPressEvent;[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);public void Start()
{
if (hKeyboardHook == 0)
{
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
//hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]), 0);
using (System.Diagnostics.Process curProcess = System.Diagnostics.Process.GetCurrentProcess())
using (System.Diagnostics.ProcessModule curModule = curProcess.MainModule)
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(curModule.ModuleName), 0); if (hKeyboardHook == 0)
{
Stop();
throw new Exception("Set GlobalKeyboardHook failed!");
}
}
}private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
if ((nCode >= 0) && (OnKeyDownEvent != null || OnKeyUpEvent != null || OnKeyPressEvent != null))
{
KeyboardHookStruct MyKBHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); //引发OnKeyDownEvent
if (OnKeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
{
Keys keyData = (Keys)MyKBHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
OnKeyDownEvent(this, e);
} //引发OnKeyUpEvent
//if (OnKeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
//{
// Keys keyData = (Keys)MyKBHookStruct.vkCode;
// KeyEventArgs e = new KeyEventArgs(keyData);
// OnKeyUpEvent(this, e);
//}
////引发OnKeyPressEvent
//if (OnKeyPressEvent != null && wParam == WM_KEYDOWN)
//{
// byte[] keyState = new byte[256];
// GetKeyboardState(keyState); // byte[] inBuffer = new byte[2];
// if (ToAscii(MyKBHookStruct.vkCode, MyKBHookStruct.scanCode, keyState, inBuffer, MyKBHookStruct.flags) == 1)
// {
// KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
// OnKeyPressEvent(this, e);
// } //}
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}程序运行后执行Start()方法安装了钩子,然后按任何键都会触发KeyboardHookProc方法()。
OnKeyDownEvent这个事件上我绑了一个方法,这个方法里面会找到指定的窗口句柄,然后用SendMessage把我希望的按键消息发给窗口。这样就实现了改键。我本以为运行了程序后,按任何键后会被安装的钩子拦截,但我在断点调试的时候,打开一个txt文件,随便按个A,按理说应该直接触发程序中的KeyboardHookProc方法,而txt上不该会显示'A'。在我程序跑完后才应该显示出来。但实际上我反复调试出现了三种情况。
1.按A,txt上直接就显示A了,KeyboardHookProc方法也同时被触发。但是按键消息都已经发出去了,程序再触发有什么用?钩子到底怎么拦的?消息被我程序拦截了,在我程序走完之前消息怎么可能发的出去怎么可能会显示出来????2.按A,txt上倒是没有显示出来A,方法被触发了,同时在代码里面光标闪的位置A出来了
这两种情况出现最多
3.是期望的情况,方法被触发,A没在txt上出现也没在我代码上出现。但好像也不对,这种情况出现的少,反正也没正常实现我改键的意图。
求高人解释。本来这程序是写来做魔兽改键精灵的,其实已经能用了,就是有一个BUG实在不知道怎么搞的。
按A键,改为B键。然后在游戏里点A应该使用英雄的B键技能,但实际是在B键技能没有冷却的时候是先使用B键技能再使用A键技能。如果B键技能冷却了,这时按A的话会使用A键技能,游戏屏幕上还是会出现B键技能冷却的提示消息。
这说明我只点了一下,但游戏窗体却收到了两个按键消息,第一个是在程序里用SendMessage发送的指定按键消息,第二个是原按键消息。
但我安装了钩子,处理了按键消息,用SendMessage把处理后的消息发出去。这样为什么会出现这种情况啊?按一次键这个方法会被触发几次发送几次消息啊?不是一次吗?
这个思路有什么问题吗?
还有最后 return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);这句。不是太理解,开始的理解是这次勾到的消息处理完后准备下次勾消息,网上查了查说是将消息发给链表中的下一个钩子。这个对我上面说到的问题有什么影响吗?
想了几天也想不通问题在哪,该怎么改。求指点!
private const int WM_KEYDOWN = 0x100;
private const int WM_KEYUP = 0x101;
private const int WM_SYSKEYDOWN = 0x104;
private const int WM_SYSKEYUP = 0x105;
public const int WH_KEYBOARD_LL = 13;public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);public event KeyEventHandler OnKeyDownEvent;
public event KeyEventHandler OnKeyUpEvent;
public event KeyPressEventHandler OnKeyPressEvent;[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);public void Start()
{
if (hKeyboardHook == 0)
{
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
//hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]), 0);
using (System.Diagnostics.Process curProcess = System.Diagnostics.Process.GetCurrentProcess())
using (System.Diagnostics.ProcessModule curModule = curProcess.MainModule)
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(curModule.ModuleName), 0); if (hKeyboardHook == 0)
{
Stop();
throw new Exception("Set GlobalKeyboardHook failed!");
}
}
}private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
if ((nCode >= 0) && (OnKeyDownEvent != null || OnKeyUpEvent != null || OnKeyPressEvent != null))
{
KeyboardHookStruct MyKBHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); //引发OnKeyDownEvent
if (OnKeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
{
Keys keyData = (Keys)MyKBHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
OnKeyDownEvent(this, e);
} //引发OnKeyUpEvent
//if (OnKeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
//{
// Keys keyData = (Keys)MyKBHookStruct.vkCode;
// KeyEventArgs e = new KeyEventArgs(keyData);
// OnKeyUpEvent(this, e);
//}
////引发OnKeyPressEvent
//if (OnKeyPressEvent != null && wParam == WM_KEYDOWN)
//{
// byte[] keyState = new byte[256];
// GetKeyboardState(keyState); // byte[] inBuffer = new byte[2];
// if (ToAscii(MyKBHookStruct.vkCode, MyKBHookStruct.scanCode, keyState, inBuffer, MyKBHookStruct.flags) == 1)
// {
// KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
// OnKeyPressEvent(this, e);
// } //}
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}程序运行后执行Start()方法安装了钩子,然后按任何键都会触发KeyboardHookProc方法()。
OnKeyDownEvent这个事件上我绑了一个方法,这个方法里面会找到指定的窗口句柄,然后用SendMessage把我希望的按键消息发给窗口。这样就实现了改键。我本以为运行了程序后,按任何键后会被安装的钩子拦截,但我在断点调试的时候,打开一个txt文件,随便按个A,按理说应该直接触发程序中的KeyboardHookProc方法,而txt上不该会显示'A'。在我程序跑完后才应该显示出来。但实际上我反复调试出现了三种情况。
1.按A,txt上直接就显示A了,KeyboardHookProc方法也同时被触发。但是按键消息都已经发出去了,程序再触发有什么用?钩子到底怎么拦的?消息被我程序拦截了,在我程序走完之前消息怎么可能发的出去怎么可能会显示出来????2.按A,txt上倒是没有显示出来A,方法被触发了,同时在代码里面光标闪的位置A出来了
这两种情况出现最多
3.是期望的情况,方法被触发,A没在txt上出现也没在我代码上出现。但好像也不对,这种情况出现的少,反正也没正常实现我改键的意图。
求高人解释。本来这程序是写来做魔兽改键精灵的,其实已经能用了,就是有一个BUG实在不知道怎么搞的。
按A键,改为B键。然后在游戏里点A应该使用英雄的B键技能,但实际是在B键技能没有冷却的时候是先使用B键技能再使用A键技能。如果B键技能冷却了,这时按A的话会使用A键技能,游戏屏幕上还是会出现B键技能冷却的提示消息。
这说明我只点了一下,但游戏窗体却收到了两个按键消息,第一个是在程序里用SendMessage发送的指定按键消息,第二个是原按键消息。
但我安装了钩子,处理了按键消息,用SendMessage把处理后的消息发出去。这样为什么会出现这种情况啊?按一次键这个方法会被触发几次发送几次消息啊?不是一次吗?
这个思路有什么问题吗?
还有最后 return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);这句。不是太理解,开始的理解是这次勾到的消息处理完后准备下次勾消息,网上查了查说是将消息发给链表中的下一个钩子。这个对我上面说到的问题有什么影响吗?
想了几天也想不通问题在哪,该怎么改。求指点!
{
if ((nCode >= 0) && (OnKeyDownEvent != null || OnKeyUpEvent != null || OnKeyPressEvent != null))
{
KeyboardHookStruct MyKBHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); //引发OnKeyDownEvent
if (OnKeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
{
Keys keyData = (Keys)MyKBHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
OnKeyDownEvent(this, e);
[code=C#]return 1;
} //引发OnKeyUpEvent
//if (OnKeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
//{
// Keys keyData = (Keys)MyKBHookStruct.vkCode;
// KeyEventArgs e = new KeyEventArgs(keyData);
// OnKeyUpEvent(this, e);
//}
////引发OnKeyPressEvent
//if (OnKeyPressEvent != null && wParam == WM_KEYDOWN)
//{
// byte[] keyState = new byte[256];
// GetKeyboardState(keyState); // byte[] inBuffer = new byte[2];
// if (ToAscii(MyKBHookStruct.vkCode, MyKBHookStruct.scanCode, keyState, inBuffer, MyKBHookStruct.flags) == 1)
// {
// KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
// OnKeyPressEvent(this, e);
// } //}
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}[/code]当你要将A改B键的时候,触发OnKeyDownEvent事件后,让系统丢弃这个消息要Hook函数要返回一个非0值http://tech.ddvip.com/2009-05/1242811110120172_2.html如果 CallNextHookEx 成功, 它会返回下一个钩子的返回值, 是个连环套; 如果 CallNextHookEx 失败, 会返回 0, 这样钩子链也就断了, 只有当前钩子还在执行任务. 不同类型的钩子函数的返回值是不同的, 对键盘钩子来讲如果返回一个非 0 的值, 表示它处理完以后就把消息给消灭了. 换句话说: 如果给键盘的钩子函数 Result := 0; 说明消息被钩子拦截并处理后就给 "放" 了; 如果给键盘的钩子函数 Result := 1; 说明消息被钩子拦截并处理后又给 "杀" 了