最近用钩子做一个改键小软件,有些问题想不明白。先看代码:
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);这句。不是太理解,开始的理解是这次勾到的消息处理完后准备下次勾消息,网上查了查说是将消息发给链表中的下一个钩子。这个对我上面说到的问题有什么影响吗?
想了几天也想不通问题在哪,该怎么改。求指点!

解决方案 »

  1.   

    当你要改键的时候,Hook函数要返回一个非0值,让系统丢弃这个消息。
      

  2.   

    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; 说明消息被钩子拦截并处理后又给 "杀" 了