zoukankan      html  css  js  c++  java
  • 如何在C#中使用全局鼠标、键盘Hook

      今天,有个同事问我,怎样在C#中使用全局钩子?以前写的全局钩子都是用unmanaged C或C++写个DLL来实现,可大家都知道,C#是基于.Net Framework的,是managed,怎么实现全局钩子呢?于是开始到网上搜索,好不容易找到一篇,318804 - HOW TO: Set a Windows Hook in Visual C# .NET,里面详细的说明了如何使用鼠标钩子捕获鼠标的移动等,可是,它只能在Application里起作用,出了Application就没用了,就是说它还是没有实现全局钩子,而且文章结尾处说:“Global Hooks are not supported in the .NET Framework...”,这可怎么办呢?

      别担心,办法总是有的,经过一番摸索以后,发现WH_KEYBORAD_LL和WH_MOUSE_LL这两个low-level的hook可以被安装成全局的,这就好办了,我们不妨用这两个low-level的hook替换掉WH_KEYBORAD和WH_MOUSE,于是开始测试。结果成功了,在C#里实现了全局钩子。

      我们来看一下主要代码段。示例源码下载地址请访问我的网站:http://www.vczx.com/article/show.php?id=1672

      首先倒入所需要的windows函数,主要有三个,SetWindowsHookEX用来安装钩子,UnhookWindowsHookEX用来卸载钩子以及CallNextHookEX用来将hook信息传递到链表中下一个hook处理过程。

    [DllImport("user32.dll", CharSet = CharSet.Auto,
               CallingConvention 
    = CallingConvention.StdCall, SetLastError = true)]
            
    private static extern int SetWindowsHookEx(
                
    int idHook,
                HookProc lpfn,
                IntPtr hMod,
                
    int dwThreadId);

    [DllImport(
    "user32.dll", CharSet = CharSet.Auto,
                CallingConvention 
    = CallingConvention.StdCall, SetLastError = true)]
            
    private static extern int UnhookWindowsHookEx(int idHook);

    [DllImport(
    "user32.dll", CharSet = CharSet.Auto,
                 CallingConvention 
    = CallingConvention.StdCall)]
            
    private static extern int CallNextHookEx(
                
    int idHook,
                
    int nCode,
                
    int wParam,
                IntPtr lParam);

      下面是有关这两个low-level hook在Winuser.h中的定义:

    /// <summary>
            
    /// Windows NT/2000/XP: Installs a hook procedure that monitors low-level mouse input events.
            
    /// </summary> 

            private const int WH_MOUSE_LL       = 14;
            
    /// <summary>
            
    /// Windows NT/2000/XP: Installs a hook procedure that monitors low-level keyboard  input events.
            
    /// </summary> 

            private const int WH_KEYBOARD_LL    = 13;

      在安装全局钩子的时候,我们就要做替换了,将WH_MOUSE和WH_KEYBORAD分别换成WH_MOUSE_LL和WH_KEYBORAD_LL:

    //install hook
                    hMouseHook = SetWindowsHookEx(
                        WH_MOUSE_LL, 
    //原来是WH_MOUSE
                        MouseHookProcedure,
                        Marshal.GetHINSTANCE(
                            Assembly.GetExecutingAssembly().GetModules()[
    0]),
                        
    0);

    //install hook
                    hKeyboardHook = SetWindowsHookEx(
                        WH_KEYBOARD_LL, 
    //原来是WH_KEYBORAD
                        KeyboardHookProcedure,
                        Marshal.GetHINSTANCE(
                        Assembly.GetExecutingAssembly().GetModules()[
    0]),
                        
    0);
     
      这样替换了之后,我们就可以实现全局钩子了,而且,不需要写DLL。看一下程序运行情况:



      下面是关于鼠标和键盘的两个Callback函数:

    private int MouseHookProc(int nCode, int wParam, IntPtr lParam)
            
    {
                
    // if ok and someone listens to our events
                if ((nCode >= 0&& (OnMouseActivity != null))
                
    {
                    
    //Marshall the data from callback.
                    MouseLLHookStruct mouseHookStruct = (MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct));

                    
    //detect button clicked
                    MouseButtons button = MouseButtons.None;
                    
    short mouseDelta = 0;
                    
    switch (wParam)
                    
    {
                        
    case WM_LBUTTONDOWN:
                            
    //case WM_LBUTTONUP: 
                            
    //case WM_LBUTTONDBLCLK: 
                            button = MouseButtons.Left;
                            
    break;
                        
    case WM_RBUTTONDOWN:
                            
    //case WM_RBUTTONUP: 
                            
    //case WM_RBUTTONDBLCLK: 
                            button = MouseButtons.Right;
                            
    break;
                        
    case WM_MOUSEWHEEL:
                            
    //If the message is WM_MOUSEWHEEL, the high-order word of mouseData member is the wheel delta. 
                            
    //One wheel click is defined as WHEEL_DELTA, which is 120. 
                            
    //(value >> 16) & 0xffff; retrieves the high-order word from the given 32-bit value
                            mouseDelta = (short)((mouseHookStruct.mouseData >> 16& 0xffff);
                            
    //TODO: X BUTTONS (I havent them so was unable to test)
                            
    //If the message is WM_XBUTTONDOWN, WM_XBUTTONUP, WM_XBUTTONDBLCLK, WM_NCXBUTTONDOWN, WM_NCXBUTTONUP, 
                            
    //or WM_NCXBUTTONDBLCLK, the high-order word specifies which X button was pressed or released, 
                            
    //and the low-order word is reserved. This value can be one or more of the following values. 
                            
    //Otherwise, mouseData is not used. 
                            break;
                    }


                    
    //double clicks
                    int clickCount = 0;
                    
    if (button != MouseButtons.None)
                        
    if (wParam == WM_LBUTTONDBLCLK || wParam == WM_RBUTTONDBLCLK) clickCount = 2;
                        
    else clickCount = 1;

                    
    //generate event 
                     MouseEventArgs e = new MouseEventArgs(
                                                        button,
                                                        clickCount,
                                                        mouseHookStruct.pt.x,
                                                        mouseHookStruct.pt.y,
                                                        mouseDelta);
                    
    //raise it
                    OnMouseActivity(this, e);
                }

                
    //call next hook
                return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
            }

    private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
            
    {
                
    //indicates if any of underlaing events set e.Handled flag
                bool handled = false;
                
    //it was ok and someone listens to events
                if ((nCode >= 0&& (KeyDown != null || KeyUp != null || KeyPress != null))
                
    {
                    
    //read structure KeyboardHookStruct at lParam
                    KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
                    
    //raise KeyDown
                    if (KeyDown != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
                    
    {
                        Keys keyData 
    = (Keys)MyKeyboardHookStruct.vkCode;
                        KeyEventArgs e 
    = new KeyEventArgs(keyData);
                        KeyDown(
    this, e);
                        handled 
    = handled || e.Handled;
                    }


                    
    // raise KeyPress
                    if (KeyPress != null && wParam == WM_KEYDOWN)
                    
    {
                        
    bool isDownShift = ((GetKeyState(VK_SHIFT) & 0x80== 0x80 ? true : false);
                        
    bool isDownCapslock = (GetKeyState(VK_CAPITAL) != 0 ? true : false);

                        
    byte[] keyState = new byte[256];
                        GetKeyboardState(keyState);
                        
    byte[] inBuffer = new byte[2];
                        
    if (ToAscii(MyKeyboardHookStruct.vkCode,
                                  MyKeyboardHookStruct.scanCode,
                                  keyState,
                                  inBuffer,
                                  MyKeyboardHookStruct.flags) 
    == 1)
                        
    {
                            
    char key = (char)inBuffer[0];
                            
    if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key)) key = Char.ToUpper(key);
                            KeyPressEventArgs e 
    = new KeyPressEventArgs(key);
                            KeyPress(
    this, e);
                            handled 
    = handled || e.Handled;
                        }

                    }


                    
    // raise KeyUp
                    if (KeyUp != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
                    
    {
                        Keys keyData 
    = (Keys)MyKeyboardHookStruct.vkCode;
                        KeyEventArgs e 
    = new KeyEventArgs(keyData);
                        KeyUp(
    this, e);
                        handled 
    = handled || e.Handled;
                    }


                }


                
    //if event handled in application do not handoff to other listeners
                if (handled)
                    
    return 1;
                
    else
                    
    return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
            }

    参考资料:MSDN
  • 相关阅读:
    P4936 题解
    初赛
    洛谷P2763题解
    探秘最小生成树&&洛谷P2126题解
    洛谷P2630 题解
    洛谷P2125 题解
    洛谷P1510 题解
    洛谷P3572题解
    Codeforces 448C Painting Fence(分治法)
    Codeforces 999F Cards and Joy(二维DP)
  • 原文地址:https://www.cnblogs.com/michaelxu/p/511557.html
Copyright © 2011-2022 走看看