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

FW: http://www.cnblogs.com/michaelxu/archive/2006/09/22/511557.html


今天,有个同事问我,怎样在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中的定义:

/// <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


FW: http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b318804

How to set a Windows hook in Visual C# .NET

<script type="text/javascript">function loadTOCNode(){}</script>
Article ID:318804
Last Review:December 11, 2006
Revision:3.1
This article was previously published under Q318804
For a Microsoft Visual Basic version of this article, see 319524 (http://support.microsoft.com/kb/319524/).
<script type="text/javascript"> var sectionFilter = "type != 'notice' && type != 'securedata' && type != 'querywords'"; var tocArrow = "/library/images/support/kbgraphics/public/en-us/downarrow.gif"; var depthLimit = 10; var depth3Limit = 10; var depth4Limit = 5; var depth5Limit = 3; var tocEntryMinimum = 1; </script> <script type="text/javascript" src="/common/script/gsfx/kbtoc.js?9"></script>

Introduction

<script type="text/javascript">loadTOCNode(1, 'summary');</script>
This article describes how to set a hook that is specific to a thread and to a hook procedure by using the mouse hook as an example. You can use hooks to monitor certain types of events. You can associate these events with a specific thread or with all the threads in the same desktop as a calling thread.

Back to the top

MORE INFORMATION

<script type="text/javascript">loadTOCNode(1, 'moreinformation');</script>

Set a mouse hook

<script type="text/javascript">loadTOCNode(2, 'moreinformation');</script> To set a hook, call the SetWindowsHookEx function from the User32.dll file. This function installs an application-defined hook procedure in the hook chain that is associated with the hook.

To set a mouse hook and to monitor the mouse events, follow these steps:
1.Start Microsoft Visual Studio .NET.
2.On the File menu, point to New, and then click Project.
3.In the New Project dialog box, click Visual C# Projects under Project Types, and then click Windows Application under Templates. In the Name box, type ThreadSpecificMouseHook. By default, a form that is named Form1 is created.
4.Add the following line of code in the Form1.cs file after the other using statements.
using System.Runtime.InteropServices;
5.Add following code in the Form1 class.
public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);

//Declare the hook handle as an int.
static int hHook = 0;

//Declare the mouse hook constant.
//For other hook types, you can obtain these values from Winuser.h in the Microsoft SDK.
public const int WH_MOUSE = 7;
private System.Windows.Forms.Button button1;

//Declare MouseHookProcedure as a HookProc type.
HookProc MouseHookProcedure;

//Declare the wrapper managed POINT class.
[StructLayout(LayoutKind.Sequential)]
public class POINT
{
public int x;
public int y;
}

//Declare the wrapper managed MouseHookStruct class.
[StructLayout(LayoutKind.Sequential)]
public class MouseHookStruct
{
public POINT pt;
public int hwnd;
public int wHitTestCode;
public int dwExtraInfo;
}

//This is the Import for the SetWindowsHookEx function.
//Use this function to install a thread-specific hook.
[DllImport("user32.dll",CharSet=CharSet.Auto,
CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn,
IntPtr hInstance, int threadId);

//This is the Import for the UnhookWindowsHookEx function.
//Call this function to uninstall the hook.
[DllImport("user32.dll",CharSet=CharSet.Auto,
CallingConvention=CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);

//This is the Import for the CallNextHookEx function.
//Use this function to pass the hook information to the next hook procedure in chain.
[DllImport("user32.dll",CharSet=CharSet.Auto,
CallingConvention=CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode,
IntPtr wParam, IntPtr lParam);
6.Add a Button control to the form, and then add the following code in the Button1_click procedure.
private void button1_Click(object sender, System.EventArgs e)
{
if(hHook == 0)
{
// Create an instance of HookProc.
MouseHookProcedure = new HookProc(Form1.MouseHookProc);

hHook = SetWindowsHookEx(WH_MOUSE,
MouseHookProcedure,
(IntPtr)0,
AppDomain.GetCurrentThreadId());
//If the SetWindowsHookEx function fails.
if(hHook == 0 )
{
MessageBox.Show("SetWindowsHookEx Failed");
return;
}
button1.Text = "UnHook Windows Hook";
}
else
{
bool ret = UnhookWindowsHookEx(hHook);
//If the UnhookWindowsHookEx function fails.
if(ret == false )
{
MessageBox.Show("UnhookWindowsHookEx Failed");
return;
}
hHook = 0;
button1.Text = "Set Windows Hook";
this.Text = "Mouse Hook";
}
}
7.Add the following code for the MouseHookProc function in the Form1 class.
public static int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
//Marshall the data from the callback.
MouseHookStruct MyMouseHookStruct = (MouseHookStruct) Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));

if (nCode < 0)
{
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
else
{
//Create a string variable that shows the current mouse coordinates.
String strCaption = "x = " +
MyMouseHookStruct.pt.x.ToString("d") +
" y = " +
MyMouseHookStruct.pt.y.ToString("d");
//You must get the active form because it is a static function.
Form tempForm = Form.ActiveForm;

//Set the caption of the form.
tempForm.Text = strCaption;
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
}
8.Press F5 to run the project. Click the button on the form to set the hook. The mouse coordinates appear on the form caption bar when the pointer moves on the form. Click the button again to remove the hook.

Back to the top

Global hooks are not supported in the .NET Framework

<script type="text/javascript">loadTOCNode(2, 'moreinformation');</script> Except for the WH_KEYBOARD_LL low-level hook and the WH_MOUSE_LL low-level hook, you cannot implement global hooks in the Microsoft .NET Framework. To install a global hook, a hook must have a native DLL export to inject itself in another process that requires a valid, consistent function to call into. This behavior requires a DLL export. The .NET Framework does not support DLL exports. Managed code has no concept of a consistent value for a function pointer because these function pointers are proxies that are built dynamically.

Low-level hook procedures are called on the thread that installed the hook. Low-level hooks do not require that the hook procedure be implemented in a DLL.

Back to the top

REFERENCES

<script type="text/javascript">loadTOCNode(1, 'references');</script>
For more information about hooks, visit the following Microsoft Developer Network (MSDN) Web site:
http://msdn.microsoft.com/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/Hooks/AboutHooks.asp (http://msdn.microsoft.com/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/Hooks/AboutHooks.asp)

Back to the top


 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值