今天,有个同事问我,怎样在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.         }  
posted on 2014-07-02 09:17  GC2013  阅读(1010)  评论(0编辑  收藏  举报