熟悉键盘钩子的帮忙看一下

ddsxd19 2010-03-25 01:36:31
最近用钩子做一个改键小软件,有些问题想不明白。先看代码:


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);这句。不是太理解,开始的理解是这次勾到的消息处理完后准备下次勾消息,网上查了查说是将消息发给链表中的下一个钩子。这个对我上面说到的问题有什么影响吗?


想了几天也想不通问题在哪,该怎么改。求指点!
...全文
217 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
520luoxp 2010-03-26
  • 打赏
  • 举报
回复
顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶
xingyuebuyu 2010-03-26
  • 打赏
  • 举报
回复
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);
[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; 说明消息被钩子拦截并处理后又给 "杀" 了
ddsxd19 2010-03-26
  • 打赏
  • 举报
回复
好的,我回去试试。
cnzdgs 2010-03-26
  • 打赏
  • 举报
回复
当你要改键的时候,Hook函数要返回一个非0值,让系统丢弃这个消息。
ddsxd19 2010-03-25
  • 打赏
  • 举报
回复
我顶顶顶
平生我自如 2010-03-25
  • 打赏
  • 举报
回复
这种问题我通常是帮顶

110,586

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧