在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#里实现了全局钩子。 

  我们来看一下主要代码段。 

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

C#代码   收藏代码
  1. [DllImport("user32.dll", CharSet = CharSet.Auto,  
  2.            CallingConvention = CallingConvention.StdCall, SetLastError = true)]  
  3.         private static extern int SetWindowsHookEx(  
  4.             int idHook,  
  5.             HookProc lpfn,  
  6.             IntPtr hMod,  
  7.             int dwThreadId);  
  8.   
  9. [DllImport("user32.dll", CharSet = CharSet.Auto,  
  10.             CallingConvention = CallingConvention.StdCall, SetLastError = true)]  
  11.         private static extern int UnhookWindowsHookEx(int idHook);  
  12.   
  13. [DllImport("user32.dll", CharSet = CharSet.Auto,  
  14.              CallingConvention = CallingConvention.StdCall)]  
  15.         private static extern int CallNextHookEx(  
  16.             int idHook,  
  17.             int nCode,  
  18.             int wParam,  
  19.             IntPtr lParam);  
  20.   
  21.   下面是有关这两个low-level hook在Winuser.h中的定义:  
  22.   
  23.   
  24. /// <summary>  
  25.         /// Windows NT/2000/XP: Installs a hook procedure that monitors low-level mouse input events.  
  26.         /// </summary>  
  27.         private const int WH_MOUSE_LL       = 14;  
  28.         /// <summary>  
  29.         /// Windows NT/2000/XP: Installs a hook procedure that monitors low-level keyboard input events.  
  30.         /// </summary>  
  31.         private const int WH_KEYBOARD_LL    = 13;  
  32.   
  33.   在安装全局钩子的时候,我们就要做替换了,将WH_MOUSE和WH_KEYBORAD分别换成WH_MOUSE_LL和WH_KEYBORAD_LL:  
  34.   
  35.   
  36. //install hook  
  37.                 hMouseHook = SetWindowsHookEx(  
  38.                     WH_MOUSE_LL, //原来是WH_MOUSE  
  39.                     MouseHookProcedure,  
  40.                     Marshal.GetHINSTANCE(  
  41.                         Assembly.GetExecutingAssembly().GetModules()[0]),  
  42.                     0);  
  43.   
  44. //install hook  
  45.                 hKeyboardHook = SetWindowsHookEx(  
  46.                     WH_KEYBOARD_LL, //原来是WH_KEYBORAD  
  47.                     KeyboardHookProcedure,  
  48.                     Marshal.GetHINSTANCE(  
  49.                     Assembly.GetExecutingAssembly().GetModules()[0]),  
  50.                     0);  
  51.   
  52.   这样替换了之后,我们就可以实现全局钩子了,而且,不需要写DLL。看一下程序运行情况:  
  53.   
  54.   
  55.   
  56.   下面是关于鼠标和键盘的两个Callback函数:  
  57.   
  58.   
  59. private int MouseHookProc(int nCode, int wParam, IntPtr lParam)  
  60.   
  61.         {  
  62.   
  63.             // if ok and someone listens to our events  
  64.   
  65.             if ((nCode >= 0) && (OnMouseActivity != null))  
  66.   
  67.             {  
  68.   
  69.                 //Marshall the data from callback.  
  70.   
  71.                 MouseLLHookStruct mouseHookStruct = (MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct));  
  72.   
  73.   
  74.                 //detect button clicked  
  75.   
  76.                 MouseButtons button = MouseButtons.None;  
  77.   
  78.                 short mouseDelta = 0;  
  79.   
  80.                 switch (wParam)  
  81.   
  82.                 {  
  83.   
  84.                     case WM_LBUTTONDOWN:  
  85.   
  86.                         //case WM_LBUTTONUP:  
  87.   
  88.                         //case WM_LBUTTONDBLCLK:  
  89.   
  90.                         button = MouseButtons.Left;  
  91.   
  92.                         break;  
  93.   
  94.                     case WM_RBUTTONDOWN:  
  95.   
  96.                         //case WM_RBUTTONUP:  
  97.   
  98.                         //case WM_RBUTTONDBLCLK:  
  99.   
  100.                         button = MouseButtons.Right;  
  101.   
  102.                         break;  
  103.   
  104.                     case WM_MOUSEWHEEL:  
  105.   
  106.                         //If the message is WM_MOUSEWHEEL, the high-order word of mouseData member is the wheel delta.  
  107.   
  108.                         //One wheel click is defined as WHEEL_DELTA, which is 120.  
  109.   
  110.                         //(value >> 16) & 0xffff; retrieves the high-order word from the given 32-bit value  
  111.   
  112.                         mouseDelta = (short)((mouseHookStruct.mouseData >> 16) & 0xffff);  
  113.   
  114.                         //TODO: X BUTTONS (I havent them so was unable to test)  
  115.   
  116.                         //If the message is WM_XBUTTONDOWN, WM_XBUTTONUP, WM_XBUTTONDBLCLK, WM_NCXBUTTONDOWN, WM_NCXBUTTONUP,  
  117.   
  118.                         //or WM_NCXBUTTONDBLCLK, the high-order word specifies which X button was pressed or released,  
  119.   
  120.                         //and the low-order word is reserved. This value can be one or more of the following values.  
  121.   
  122.                         //Otherwise, mouseData is not used.  
  123.   
  124.                         break;  
  125.   
  126.                 }  
  127.   
  128.   
  129.                 //double clicks  
  130.   
  131.                 int clickCount = 0;  
  132.   
  133.                 if (button != MouseButtons.None)  
  134.   
  135.                     if (wParam == WM_LBUTTONDBLCLK || wParam == WM_RBUTTONDBLCLK) clickCount = 2;  
  136.   
  137.                     else clickCount = 1;  
  138.   
  139.   
  140.                 //generate event  
  141.   
  142.                  MouseEventArgs e = new MouseEventArgs(  
  143.   
  144.                                                     button,  
  145.   
  146.                                                     clickCount,  
  147.   
  148.                                                     mouseHookStruct.pt.x,  
  149.   
  150.                                                     mouseHookStruct.pt.y,  
  151.   
  152.                                                     mouseDelta);  
  153.   
  154.                 //raise it  
  155.   
  156.                 OnMouseActivity(this, e);  
  157.   
  158.             }  
  159.   
  160.             //call next hook  
  161.   
  162.             return CallNextHookEx(hMouseHook, nCode, wParam, lParam);  
  163.   
  164.         }  
  165.   
  166.   
  167. private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)  
  168. {  
  169.             //indicates if any of underlaing events set e.Handled flag  
  170.             bool handled = false;  
  171.             //it was ok and someone listens to events  
  172.             if ((nCode >= 0) && (KeyDown != null || KeyUp != null || KeyPress != null))  
  173.             {  
  174.                 //read structure KeyboardHookStruct at lParam  
  175.                 KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));  
  176.                 //raise KeyDown  
  177.                 if (KeyDown != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))  
  178.                 {  
  179.                     Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;  
  180.                     KeyEventArgs e = new KeyEventArgs(keyData);  
  181.                     KeyDown(this, e);  
  182.                     handled = handled || e.Handled;  
  183.                 }  
  184.   
  185.                 // raise KeyPress  
  186.                 if (KeyPress != null && wParam == WM_KEYDOWN)  
  187.                 {  
  188.                     bool isDownShift = ((GetKeyState(VK_SHIFT) & 0x80) == 0x80 ? true : false);  
  189.                     bool isDownCapslock = (GetKeyState(VK_CAPITAL) != 0 ? true : false);  
  190.   
  191.                     byte[] keyState = new byte[256];  
  192.                     GetKeyboardState(keyState);  
  193.                     byte[] inBuffer = new byte[2];  
  194.                     if (ToAscii(MyKeyboardHookStruct.vkCode,  
  195.                               MyKeyboardHookStruct.scanCode,  
  196.                               keyState,  
  197.                               inBuffer,  
  198.                               MyKeyboardHookStruct.flags) == 1)  
  199.                     {  
  200.                         char key = (char)inBuffer[0];  
  201.                         if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key)) key = Char.ToUpper(key);  
  202.                         KeyPressEventArgs e = new KeyPressEventArgs(key);  
  203.                         KeyPress(this, e);  
  204.                         handled = handled || e.Handled;  
  205.                     }  
  206.                 }  
  207.   
  208.                 // raise KeyUp  
  209.                 if (KeyUp != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))  
  210.                 {  
  211.                     Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;  
  212.                     KeyEventArgs e = new KeyEventArgs(keyData);  
  213.                     KeyUp(this, e);  
  214.                     handled = handled || e.Handled;  
  215.                 }  
  216.   
  217.             }  
  218.   
  219.             //if event handled in application do not handoff to other listeners  
  220.             if (handled)  
  221.                 return 1;  
  222.             else  
  223.                 return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);  
  224.         }  

你可能感兴趣的:(C#)