如何在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处理过程。

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

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

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

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

None.gif          private   const   int  WH_KEYBOARD_LL     =   13 ;

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

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

HookTest.jpg

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

None.gif private   int  MouseHookProc( int  nCode,  int  wParam, IntPtr lParam)
ExpandedBlockStart.gif        
{
InBlock.gif            
// if ok and someone listens to our events
InBlock.gif
            if ((nCode >= 0&& (OnMouseActivity != null))
ExpandedSubBlockStart.gif            
{
InBlock.gif                
//Marshall the data from callback.
InBlock.gif
                MouseLLHookStruct mouseHookStruct = (MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct));
InBlock.gif
InBlock.gif                
//detect button clicked
InBlock.gif
                MouseButtons button = MouseButtons.None;
InBlock.gif                
short mouseDelta = 0;
InBlock.gif                
switch (wParam)
ExpandedSubBlockStart.gif                
{
InBlock.gif                    
case WM_LBUTTONDOWN:
InBlock.gif                        
//case WM_LBUTTONUP: 
InBlock.gif                        
//case WM_LBUTTONDBLCLK: 
InBlock.gif
                        button = MouseButtons.Left;
InBlock.gif                        
break;
InBlock.gif                    
case WM_RBUTTONDOWN:
InBlock.gif                        
//case WM_RBUTTONUP: 
InBlock.gif                        
//case WM_RBUTTONDBLCLK: 
InBlock.gif
                        button = MouseButtons.Right;
InBlock.gif                        
break;
InBlock.gif                    
case WM_MOUSEWHEEL:
InBlock.gif                        
//If the message is WM_MOUSEWHEEL, the high-order word of mouseData member is the wheel delta. 
InBlock.gif                        
//One wheel click is defined as WHEEL_DELTA, which is 120. 
InBlock.gif                        
//(value >> 16) & 0xffff; retrieves the high-order word from the given 32-bit value
InBlock.gif
                        mouseDelta = (short)((mouseHookStruct.mouseData >> 16& 0xffff);
InBlock.gif                        
//TODO: X BUTTONS (I havent them so was unable to test)
InBlock.gif                        
//If the message is WM_XBUTTONDOWN, WM_XBUTTONUP, WM_XBUTTONDBLCLK, WM_NCXBUTTONDOWN, WM_NCXBUTTONUP, 
InBlock.gif                        
//or WM_NCXBUTTONDBLCLK, the high-order word specifies which X button was pressed or released, 
InBlock.gif                        
//and the low-order word is reserved. This value can be one or more of the following values. 
InBlock.gif                        
//Otherwise, mouseData is not used. 
InBlock.gif
                        break;
ExpandedSubBlockEnd.gif                }

InBlock.gif
InBlock.gif                
//double clicks
InBlock.gif
                int clickCount = 0;
InBlock.gif                
if (button != MouseButtons.None)
InBlock.gif                    
if (wParam == WM_LBUTTONDBLCLK || wParam == WM_RBUTTONDBLCLK) clickCount = 2;
InBlock.gif                    
else clickCount = 1;
InBlock.gif
InBlock.gif                
//generate event 
InBlock.gif
                 MouseEventArgs e = new MouseEventArgs(
InBlock.gif                                                    button,
InBlock.gif                                                    clickCount,
InBlock.gif                                                    mouseHookStruct.pt.x,
InBlock.gif                                                    mouseHookStruct.pt.y,
InBlock.gif                                                    mouseDelta);
InBlock.gif                
//raise it
InBlock.gif
                OnMouseActivity(this, e);
ExpandedSubBlockEnd.gif            }

InBlock.gif            
//call next hook
InBlock.gif
            return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
ExpandedBlockEnd.gif        }

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

InBlock.gif
InBlock.gif                
// raise KeyPress
InBlock.gif
                if (KeyPress != null && wParam == WM_KEYDOWN)
ExpandedSubBlockStart.gif                
{
InBlock.gif                    
bool isDownShift = ((GetKeyState(VK_SHIFT) & 0x80== 0x80 ? true : false);
InBlock.gif                    
bool isDownCapslock = (GetKeyState(VK_CAPITAL) != 0 ? true : false);
InBlock.gif
InBlock.gif                    
byte[] keyState = new byte[256];
InBlock.gif                    GetKeyboardState(keyState);
InBlock.gif                    
byte[] inBuffer = new byte[2];
InBlock.gif                    
if (ToAscii(MyKeyboardHookStruct.vkCode,
InBlock.gif                              MyKeyboardHookStruct.scanCode,
InBlock.gif                              keyState,
InBlock.gif                              inBuffer,
InBlock.gif                              MyKeyboardHookStruct.flags) 
== 1)
ExpandedSubBlockStart.gif                    
{
InBlock.gif                        
char key = (char)inBuffer[0];
InBlock.gif                        
if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key)) key = Char.ToUpper(key);
InBlock.gif                        KeyPressEventArgs e 
= new KeyPressEventArgs(key);
InBlock.gif                        KeyPress(
this, e);
InBlock.gif                        handled 
= handled || e.Handled;
ExpandedSubBlockEnd.gif                    }

ExpandedSubBlockEnd.gif                }

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

InBlock.gif
ExpandedSubBlockEnd.gif            }

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





   本文转自loose_went博客园博客,原文链接: http://www.cnblogs.com/michaelxu/archive/2006/09/22/511557.html ,如需转载请自行联系原作者





相关文章
|
C#
C# WPF 中用代码模拟鼠标和键盘的操作
原文:C# WPF 中用代码模拟鼠标和键盘的操作   原文地址   C#开发者都知道,在Winform开发中,SendKeys类提供的方法是很实用的。
2145 0
|
C#
C#(Wpf)实现小键盘
原文:C#(Wpf)实现小键盘 花了一天时间小键盘基本功能已完成,先看看效果图吧!              默认:             Shift:     Caps Lock:   Button style ...
1215 0
|
物联网 C# Windows
Win10 IoT C#开发 6 - 4x4矩阵键盘扫描
原文:Win10 IoT C#开发 6 - 4x4矩阵键盘扫描 Windows 10 IoT Core 是微软针对物联网市场的一个重要产品,与以往的Windows版本不同,是为物联网设备专门设计的,硬件也不仅仅限于x86架构,同时可以在ARM架构上运行。
1492 0
|
C#
C#软件开发实例.私人订制自己的屏幕截图工具(八)添加键盘操作截图的功能
上一篇:C#软件开发实例.私人订制自己的屏幕截图工具(七)添加放大镜的功能 虽然添加了放大镜的功能,但是在进行像素级的定位时,还是不容易精确定位,在用鼠标操作时要改变一两个像素的位置还是有些困难的。
709 0
|
C#
C#中使用WinIO模拟键盘鼠标(转)
原文地址在哪忘了,对不起额。   public class WinIOLab    {        private const int KBC_KEY_CMD = 0x64;        private const int KBC_KEY_DATA = 0x60;        [DllImport("winio32.
2167 0
|
15天前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
|
15天前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。