您现在的位置: 天下网吧 >> 网吧天地 >> 天下码农 >> 微信小程序 >> 正文

如何在C#中使用全局鼠标、键盘Hook

2011-2-10vczx佚名

  今天,有个同事问我,怎样在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处理过程。

[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中的定义:

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

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

        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

欢迎访问最专业的网吧论坛,无盘论坛,网吧经营,网咖管理,网吧专业论坛 https://bbs.txwb.com

关注天下网吧微信/下载天下网吧APP/天下网吧小程序,一起来超精彩

本文来源:vczx 作者:佚名

声明
声明:本站所发表的文章、评论及图片仅代表作者本人观点,与本站立场无关。若文章侵犯了您的相关权益,请及时与我们联系,我们会及时处理,感谢您对本站的支持!联系邮箱:support@txwb.com,系统开号,技术支持,服务联系QQ:1175525021本站所有有注明来源为天下网吧或天下网吧论坛的原创作品,各位转载时请注明来源链接!
天下网吧 网吧天下