目前的程序中想要添加Hook,截获键盘按键消息,所以上网找了一下关于C#下Hook的东西。发现很多人都在说在C#中添加Hook不成功和不稳定,而建议使用C++封一个Dll给C#使用。,可喜的是最后我还是成功的在程序中使用C#添加了Hook,经过测试还是没有什么问题的。
进行Hook需要使用三个API函数
SetWindowsHookEx 进行Hook的注册
UnhookWindowsHookEx 取消已经注册的Hook
CallNextHookEx 将消息传递给下一个Hook函数
看一下定义
1 [DllImport("user32.dll")]
2 private static extern IntPtr SetWindowsHookEx(
3 HookType code, HookProc func, IntPtr instance, int threadID);
4
5 [DllImport("user32.dll")]
6 private static extern int UnhookWindowsHookEx(IntPtr hook);
7
8 [DllImport("user32.dll")]
9 private static extern int CallNextHookEx(
10 IntPtr hook, int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
2 private static extern IntPtr SetWindowsHookEx(
3 HookType code, HookProc func, IntPtr instance, int threadID);
4
5 [DllImport("user32.dll")]
6 private static extern int UnhookWindowsHookEx(IntPtr hook);
7
8 [DllImport("user32.dll")]
9 private static extern int CallNextHookEx(
10 IntPtr hook, int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
我们需要定义一个delegate来接收消息
1 private delegate int HookProc(int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
定义两个event提供给外界使用
1 public delegate void HookEventHandler(object sender, HookEventArgs e);
2 public event HookEventHandler KeyDown;
3 public event HookEventHandler KeyUp;
4
2 public event HookEventHandler KeyDown;
3 public event HookEventHandler KeyUp;
4
因为要接收的是键盘的消息所以要定义一些消息,和我们要接收的结构。
1 public class HookEventArgs : EventArgs
2 {
3 // using Windows.Forms.Keys instead of Input.Key since the Forms.Keys maps
4 // to the Win32 KBDLLHOOKSTRUCT virtual key member, where Input.Key does not
5 public Keys Key;
6 public bool Alt;
7 public bool Control;
8 public bool Shift;
9
10 public HookEventArgs(UInt32 keyCode)
11 {
12 // detect what modifier keys are pressed, using
13 // Windows.Forms.Control.ModifierKeys instead of Keyboard.Modifiers
14 // since Keyboard.Modifiers does not correctly get the state of the
15 // modifier keys when the application does not have focus
16 this.Key = (Keys)keyCode;
17 this.Alt = (System.Windows.Forms.Control.ModifierKeys & Keys.Alt) != 0;
18 this.Control = (System.Windows.Forms.Control.ModifierKeys & Keys.Control) != 0;
19 this.Shift = (System.Windows.Forms.Control.ModifierKeys & Keys.Shift) != 0;
20 }
21 }
2 {
3 // using Windows.Forms.Keys instead of Input.Key since the Forms.Keys maps
4 // to the Win32 KBDLLHOOKSTRUCT virtual key member, where Input.Key does not
5 public Keys Key;
6 public bool Alt;
7 public bool Control;
8 public bool Shift;
9
10 public HookEventArgs(UInt32 keyCode)
11 {
12 // detect what modifier keys are pressed, using
13 // Windows.Forms.Control.ModifierKeys instead of Keyboard.Modifiers
14 // since Keyboard.Modifiers does not correctly get the state of the
15 // modifier keys when the application does not have focus
16 this.Key = (Keys)keyCode;
17 this.Alt = (System.Windows.Forms.Control.ModifierKeys & Keys.Alt) != 0;
18 this.Control = (System.Windows.Forms.Control.ModifierKeys & Keys.Control) != 0;
19 this.Shift = (System.Windows.Forms.Control.ModifierKeys & Keys.Shift) != 0;
20 }
21 }
1 private enum HookType : int
2 {
3 WH_JOURNALRECORD = 0,
4 WH_JOURNALPLAYBACK = 1,
5 WH_KEYBOARD = 2,
6 WH_GETMESSAGE = 3,
7 WH_CALLWNDPROC = 4,
8 WH_CBT = 5,
9 WH_SYSMSGFILTER = 6,
10 WH_MOUSE = 7,
11 WH_HARDWARE = 8,
12 WH_DEBUG = 9,
13 WH_SHELL = 10,
14 WH_FOREGROUNDIDLE = 11,
15 WH_CALLWNDPROCRET = 12,
16 WH_KEYBOARD_LL = 13,
17 WH_MOUSE_LL = 14
18 }
19
20 public struct KBDLLHOOKSTRUCT
21 {
22 public UInt32 vkCode;
23 public UInt32 scanCode;
24 public UInt32 flags;
25 public UInt32 time;
26 public IntPtr extraInfo;
27 }
2 {
3 WH_JOURNALRECORD = 0,
4 WH_JOURNALPLAYBACK = 1,
5 WH_KEYBOARD = 2,
6 WH_GETMESSAGE = 3,
7 WH_CALLWNDPROC = 4,
8 WH_CBT = 5,
9 WH_SYSMSGFILTER = 6,
10 WH_MOUSE = 7,
11 WH_HARDWARE = 8,
12 WH_DEBUG = 9,
13 WH_SHELL = 10,
14 WH_FOREGROUNDIDLE = 11,
15 WH_CALLWNDPROCRET = 12,
16 WH_KEYBOARD_LL = 13,
17 WH_MOUSE_LL = 14
18 }
19
20 public struct KBDLLHOOKSTRUCT
21 {
22 public UInt32 vkCode;
23 public UInt32 scanCode;
24 public UInt32 flags;
25 public UInt32 time;
26 public IntPtr extraInfo;
27 }
关键的在这里 注册Hook
1 private void Install()
2 {
3 if (_hookHandle != IntPtr.Zero)
4 return;
5
6 Module[] list = System.Reflection.Assembly.GetExecutingAssembly().GetModules();
7 System.Diagnostics.Debug.Assert(list != null && list.Length > 0);
8
9 _hookHandle = SetWindowsHookEx(_hookType,
10 _hookFunction, Marshal.GetHINSTANCE(list[0]), 0);
11
12 }
哦,还有HookType _hookType = HookType.WH_KEYBOARD_LL; 因为要截获键盘消息2 {
3 if (_hookHandle != IntPtr.Zero)
4 return;
5
6 Module[] list = System.Reflection.Assembly.GetExecutingAssembly().GetModules();
7 System.Diagnostics.Debug.Assert(list != null && list.Length > 0);
8
9 _hookHandle = SetWindowsHookEx(_hookType,
10 _hookFunction, Marshal.GetHINSTANCE(list[0]), 0);
11
12 }
还有_hookFunction = new HookProc(HookCallback);
其实最关键的是Marshal.GetHINSTANCE(list[0])得到当前程序的instance,这样这个Hook就是全局的Hook,这个位置也可以是null,这样就不是全局Hook。
有个很奇怪的现象就是这个函数,在调试状态执行不能成功,而做成Release以后运行成功,所以你在做程序时,不要因为调试失败而对这个函数有怀疑,编一个Release版本的程序,独立运行再试一下。我使用的环境是VS2005 + Vista。
接收消息的函数
1 private int HookCallback(int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)
2 {
3 if (code < 0)
4 return CallNextHookEx(_hookHandle, code, wParam, ref lParam);
5
6 if ((lParam.flags & 0x80) != 0 && this.KeyUp != null)
7 this.KeyUp(this, new HookEventArgs(lParam.vkCode));
8
9 if ((lParam.flags & 0x80) == 0 && this.KeyDown != null)
10 {
11 this.KeyDown(this, new HookEventArgs(lParam.vkCode));
12 if (lParam.vkCode == 44)
13 {
14 return 1;
15 }
16 }
17
18 return CallNextHookEx(_hookHandle, code, wParam, ref lParam);
19 }
这里会区分KeyUp和KeyDown,注意一定要调用CallNextHookEx,这样会将这个消息在系统中继续传递,这很重要。除非你想阻止这个消息,也不希望其他程序再处理这个消息。2 {
3 if (code < 0)
4 return CallNextHookEx(_hookHandle, code, wParam, ref lParam);
5
6 if ((lParam.flags & 0x80) != 0 && this.KeyUp != null)
7 this.KeyUp(this, new HookEventArgs(lParam.vkCode));
8
9 if ((lParam.flags & 0x80) == 0 && this.KeyDown != null)
10 {
11 this.KeyDown(this, new HookEventArgs(lParam.vkCode));
12 if (lParam.vkCode == 44)
13 {
14 return 1;
15 }
16 }
17
18 return CallNextHookEx(_hookHandle, code, wParam, ref lParam);
19 }
下面最后的操作,释放注册。
1 private void Uninstall()
2 {
3 if (_hookHandle != IntPtr.Zero)
4 {
5 UnhookWindowsHookEx(_hookHandle);
6 _hookHandle = IntPtr.Zero;
7 }
8 }
2 {
3 if (_hookHandle != IntPtr.Zero)
4 {
5 UnhookWindowsHookEx(_hookHandle);
6 _hookHandle = IntPtr.Zero;
7 }
8 }
Ok,完成了。