前言:

  因为项目中需要使用到快捷键,所以上网找资料了解关于快捷键的实现技术,于是有了键盘钩子的使用学习。在网上了解到,键盘钩子其实只是很多种钩子中的其中一种。所谓钩子:请看下面关于钩子的描述(来自百度百科):

Windows系统是建立在事件驱动的机制上的,说穿了就是整个系统都是通过消息的传递来实现的。而钩子是Windows系统中非常重要的系统接口,用它可以截获并处理送给其他应用程序的消息,来完成普通应用程序难以实现的功能。

钩子可以监视系统或进程中的各种事件消息,截获发往目标窗口的消息并进行处理。这样,我们就可以在系统中安装自定义的钩子,监视系统中特定事件的发生,完成特定的功能,比如截获键盘、鼠标的输入,屏幕取词,日志监视等等。

钩子的本质是一段用以处理系统消息的程序,通过系统调用,将其挂入系统。钩子的种类有很多,每种钩子可以截获并处理相应的消息,每当特定的消息发出,在到达目的窗口之前,钩子程序先行截获该消息、得到对此消息的控制权。此时在钩子函数中就可以对截获的消息进行加工处理,甚至可以强制结束消息的传递。

本文我们主要来谈谈全局钩子和进程钩子的使用。

  全局钩子:全局钩子,能够截获所有运行在操作系统上的程序发送的消息,但是因其全局性,钩子安装之后,会比较损耗性能,在使用完毕之后,必须实时的卸载。

进程钩子:可以针对某一个进程,仅仅截获某一个应用程序的消息,比较具有针对性,适用于普通的信息管理系统。

钩子程序是封装在User32.dll中的方法,如果我们的程序需要用到钩子,首先需要将钩子对应的程序集导入到我们的系统中。代码如下:

  1. /// <summary>
  2. /// 获取窗体线程ID
  3. /// </summary>
  4. /// <param name="hwnd">窗体句柄</param>
  5. /// <param name="ID"></param>
  6. /// <returns></returns>
  7. [DllImport("User32.dll", CharSet = CharSet.Auto)]
  8. public static extern int GetWindowThreadProcessId(IntPtr hwnd, int ID);
  9.  
  10. /// <summary>
  11. /// 设置钩子
  12. /// </summary>
  13. /// <param name="idHook">钩子id</param>
  14. /// <param name="lpfn">钩子处理方法</param>
  15. /// <param name="hInstance">句柄</param>
  16. /// <param name="threadId">线程id</param>
  17. /// <returns></returns>
  18. [DllImport("user32.dll")]
  19. public static extern int SetWindowsHookEx(int idHook, HookHandle lpfn, IntPtr hInstance, int threadId);
  20.  
  21. /// <summary>
  22. /// 取消钩子
  23. /// </summary>
  24. /// <param name="idHook"></param>
  25. /// <returns></returns>
  26. [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  27. public static extern bool UnhookWindowsHookEx(int idHook);
  28.  
  29. /// <summary>
  30. /// 调用下一个钩子
  31. /// </summary>
  32. /// <param name="idHook">本钩子id</param>
  33. /// <param name="nCode"></param>
  34. /// <param name="wParam"></param>
  35. /// <param name="lParam"></param>
  36. /// <returns></returns>
  37. [DllImport("user32.dll")]
  38. public static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
  39.  
  40. /// <summary>
  41. /// 获取当前线程ID
  42. /// </summary>
  43. /// <returns></returns>
  44. [DllImport("kernel32.dll")]
  45. public static extern int GetCurrentThreadId();
  46.  
  47. [DllImport("kernel32.dll")]
  48. public static extern IntPtr GetModuleHandle(string name);

钩子相关方法的导入

在系统使用中,我们对钩子进行了简单的封装,针对全局钩子和进程钩子共有的特性,抽象出钩子基类

  1. /// <summary>
  2. /// 键盘钩子
  3. /// </summary>
  4. public abstract class KeyBoardHook
  5. {
  6. #region 字段
  7. /// <summary>
  8. /// 当前钩子的id
  9. /// </summary>
  10. protected int hHookId = ;
  11. /// <summary>
  12. /// 外部调用的键盘处理事件
  13. /// </summary>
  14. protected ProcessKeyHandle clientMethod = null;
  15. /// <summary>
  16. /// 当前模块的句柄
  17. /// </summary>
  18. protected IntPtr hookWindowPtr = IntPtr.Zero;
  19. /// <summary>
  20. /// 勾子程序处理事件
  21. /// </summary>
  22. protected HookHandle keyBoardHookProcedure;
  23. protected int hookKey;
  24. #endregion
  25.  
  26. #region 属性
  27. /// <summary>
  28. /// 获取或设置钩子的唯一标志
  29. /// </summary>
  30. public int HookKey
  31. {
  32. get { return this.hookKey; }
  33. set { this.hookKey = value; }
  34. }
  35. #endregion
  36.  
  37. /// <summary>
  38. /// 安装钩子
  39. /// </summary>
  40. /// <param name="clientMethod"></param>
  41. /// <returns></returns>
  42. public abstract bool Install(ProcessKeyHandle clientMethod);
  43. /// <summary>
  44. /// 钩子处理函数
  45. /// </summary>
  46. /// <param name="nCode"></param>
  47. /// <param name="wParam"></param>
  48. /// <param name="lParam"></param>
  49. /// <returns></returns>
  50. protected abstract int GetHookProc(int nCode, int wParam, IntPtr lParam);
  51.  
  52. /// <summary>
  53. /// 卸载钩子
  54. /// </summary>
  55. public virtual void UnInstall()
  56. {
  57. bool retKeyboard = true;
  58. if (hHookId != )
  59. {
  60. retKeyboard = Win32API.UnhookWindowsHookEx(hHookId);
  61. hHookId = ;
  62.  
  63. }
  64. //if (hookWindowPtr != IntPtr.Zero)
  65. //{
  66. // Marshal.FreeHGlobal(hookWindowPtr);
  67. //}
  68. if (!retKeyboard)
  69. {
  70. throw new Exception("UnhookWindowsHookEx failed.");
  71. }
  72. }
  73. }

钩子基类

  1. /// <summary>
  2. /// 全局钩子,慎用,获取所有进程的按键信息,耗费系统资源
  3. /// </summary>
  4. internal class GlobalHook : KeyBoardHook
  5. {
  6. public override bool Install(ProcessKeyHandle clientMethod)
  7. {
  8. try
  9. {
  10. //客户端传入的委托,即截获消息之后,对消息的过滤和处理的方法
  11. this.clientMethod = clientMethod;
  12.  
  13. // 安装键盘钩子
  14. if (hHookId == )
  15. {
  16. keyBoardHookProcedure = GetHookProc;
  17.  
  18. hookWindowPtr = Win32API.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
  19.  
  20. hHookId = Win32API.SetWindowsHookEx(
  21. (int)HookType.WH_KEYBOARD_LL,//调用系统方法安装钩子,第一个参数标识钩子的类型13为全局钩子
  22. keyBoardHookProcedure,
  23. hookWindowPtr,
  24. );
  25.  
  26. //如果设置钩子失败.
  27. if (hHookId == )
  28.  
  29. UnInstall();
  30. }
  31. return true;
  32. }
  33. catch
  34. {
  35. return false;
  36. }
  37. }
  38.  
  39. protected override int GetHookProc(int nCode, int wParam, IntPtr lParam)
  40. {
  41. ////参数 nCode 的可选值:
  42. //HC_ACTION = 0; {}
  43. //HC_GETNEXT = 1; {}
  44. //HC_SKIP = 2; {}
  45. //HC_NOREMOVE = 3; {}
  46. //HC_NOREM = HC_NOREMOVE; {}
  47. //HC_SYSMODALON = 4; {}
  48. //HC_SYSMODALOFF = 5; {}
  49.  
  50. if (nCode >= && nCode != )
  51. {
  52. //wParam = = 0x101 // 键盘抬起
  53. // 键盘按下
  54. if (wParam == 0x100)
  55. {
  56. //触发事件把安装信息通知客户端
  57. if (clientMethod != null)
  58. {
  59. KeyBoardHookStruct kbh = (KeyBoardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyBoardHookStruct));
  60. Keys key = (Keys)kbh.VkCode;
  61. bool handle;
  62. clientMethod(this.hookKey, key, out handle);
  63. if (handle)//如果处理了就直接停止 1:
  64. {
  65. Win32API.CallNextHookEx(hHookId, nCode, wParam, lParam);
  66. return ;
  67. }
  68. }
  69. }
  70.  
  71. }
  72. return Win32API.CallNextHookEx(hHookId, nCode, wParam, lParam);
  73. }
  74. }
  75.  
  76. /// <summary>
  77. /// 键盘钩子的类型
  78. /// </summary>
  79. public enum KeyBoardHookType
  80. {
  81. Global = ,//全局钩子
  82. Process = //进程钩子
  83. }
  84.  
  85. /// <summary>
  86. /// 键盘处理事件委托,接受返回win32的委托
  87. /// </summary>
  88. /// <param name="nCode"></param>
  89. /// <param name="wParam"></param>
  90. /// <param name="lParam"></param>
  91. /// <returns></returns>
  92. public delegate int HookHandle(int nCode, int wParam, IntPtr lParam);
  93.  
  94. /// <summary>
  95. /// 客户端处理钩子回调的键盘处理事件
  96. /// </summary>
  97. /// <param name="hookKey">钩子的唯一标志</param>
  98. /// <param name="key">按下的键</param>
  99. /// <param name="handle">客户端是否处理了这个值</param>
  100. public delegate void ProcessKeyHandle(int hookKey, Keys key, out bool handle);
  101.  
  102. internal enum HookType
  103. {
  104. WH_KEYBOARD = ,//私有钩子
  105. WH_KEYBOARD_LL = //全局钩子
  106. }
  107.  
  108. /// <summary>
  109. /// 全局钩子时,转化的结构体
  110. /// </summary>
  111. [StructLayout(LayoutKind.Sequential)]
  112. internal class KeyBoardHookStruct
  113. {
  114. /// <summary>
  115. /// 表达一个在1到254间的虚拟键盘码
  116. /// </summary>
  117. public int VkCode { get; set; }
  118. public int ScanCode { get; set; }
  119. public int Flags { get; set; }
  120. public int Time { get; set; }
  121. public int DwExtraInfo { get; set; }
  122. }

全局钩子代码

 上面的代码,对全局钩子的使用进行了封装,只需要创建GlobalHook类,将需要监听处理的委托传递给构造函数,即可创建和使用钩子。再来看看进程钩子的代码,类似于全局钩子:

  1. /// <summary>
  2. /// 进程钩子,只能捕捉本进程的按键信息
  3. /// </summary>
  4. internal class ProcessHook : KeyBoardHook
  5. {
  6. public override bool Install(ProcessKeyHandle clientMethod)
  7. {
  8. try
  9. {
  10. this.clientMethod = clientMethod;
  11.  
  12. // 安装键盘钩子
  13. if (hHookId == )
  14. {
  15. keyBoardHookProcedure = GetHookProc;
  16.  
  17. hookWindowPtr = Win32API.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
  18.  
  19. hHookId = Win32API.SetWindowsHookEx(
  20. (int)HookType.WH_KEYBOARD,
  21. keyBoardHookProcedure,
  22. IntPtr.Zero,
  23. Win32API.GetCurrentThreadId()
  24. );
  25.  
  26. //如果设置钩子失败.
  27. if (hHookId == )
  28.  
  29. UnInstall();
  30. }
  31. return true;
  32. }
  33. catch
  34. {
  35. return false;
  36. }
  37. }
  38.  
  39. protected override int GetHookProc(int nCode, int wParam, IntPtr lParam)
  40. {
  41. if (nCode == && nCode != )
  42. {
  43. bool isKeyDown = false;
  44. if (IntPtr.Size == )
  45. {
  46. isKeyDown = (((lParam.ToInt32() & 0x80000000) == ));
  47. }
  48. if (IntPtr.Size == )
  49. {
  50. isKeyDown = (((lParam.ToInt64() & 0x80000000) == ));
  51. }
  52. // 键盘按下
  53. if (isKeyDown)
  54. {
  55. //Debug.WriteLine("key down_________________________");
  56. //触发事件把安装信息通知客户端
  57. if (clientMethod != null)
  58. {
  59. //进程钩子,按键值在这里
  60. Keys keyData = (Keys)wParam;
  61. bool handle;
  62. clientMethod(this.hookKey, keyData, out handle);
  63. if (handle)//如果处理了就直接停止 1:
  64. {
  65. Win32API.CallNextHookEx(hHookId, nCode, wParam, lParam);
  66. return ;
  67. }
  68. }
  69. }
  70.  
  71. }
  72. return Win32API.CallNextHookEx(hHookId, nCode, wParam, lParam);
  73. }
  74. }

进程钩子

再来看看如何在客户端使用钩子。只需在需要使用到钩子的窗体中,按如下方式编写代码即可:

  1. private KeyBoardHook hook;
  2. /// <summary>
  3. /// 创建进程键盘钩子
  4. /// </summary>
  5. protected void CreateProcessHook()
  6. {
  7. if (hook != null)
  8. {
  9. hook.UnInstall();
  10. }
  11. //使用工厂类创建出对应的钩子。
  12. hook = KeyBoardHookHelper.CreateHook(KeyBoardHookType.Process);
  13. if (hook.Install(ClientProcessKeyHandle))
  14. {
  15. hook.HookKey = this.GetHashCode();
  16. }
  17. }
  18.  
  19. //客户端传给钩子的监听方法。
  20. private void ClientProcessKeyHandle(int hookKey, Keys key, out bool handle)
  21. {
  22. handle = false;
  23.  
  24. if (hookKey == hook.HookKey)
  25. {
  26. OnClientProcessKeyHandle(key, out handle);
  27. }
  28. return;
  29. }
  30.  
  31. /// <summary>
  32. /// 子类重写键盘钩子处理方法(系统中存在多个窗体,可将该代码放入到窗体基类中,子类只需重写该方法即可。)
  33. /// </summary>
  34. /// <param name="key"></param>
  35. /// <param name="handle"></param>
  36. protected virtual void OnClientProcessKeyHandle(Keys key, out bool handle)
  37. {
  38.  
  39. handle = false;
  40. //截获消息并进行处理。
  41. if ((int)key == (int)Keys.F2)//保存,
  42. {
  43. OnSaveOrder(this.tsbtn_Save);
  44. handle = true;
  45. }
  46. }

客户端调用

C#实现键盘钩子的更多相关文章

  1. C#键盘钩子 鼠标钩子

    最新对C#模拟键盘按键,鼠标操作产生了兴趣.特从网上收集了一些常用的API用来调用键盘,鼠标操作. class Win32API { #region DLL导入 /// <summary> ...

  2. 基于C#实现的HOOK键盘钩子实例代码

    本文所述为基于C#实现的HOOK实例,该实例可用来屏蔽系统热键.程序主要实现了安装钩子.传递钩子.卸载钩子等功能.在传递钩子中:<param name="pHookHandle&quo ...

  3. 2.添加键盘钩子。向进程中注入dll

    学习笔记 1.首先要建立mfc的动态链接库.在def文件中放入要导出的函数名. 2.添加函数如下 //安装钩子 //HHOOK SetWindowsHookEx( // int idHook,//钩子 ...

  4. C#鼠标键盘钩子

    using System;using System.Collections.Generic; using System.Reflection; using System.Runtime.Interop ...

  5. C# 键盘钩子

    p{ text-align:center; } blockquote > p > span{ text-align:center; font-size: 18px; color: #ff0 ...

  6. 在WPF中快速实现键盘钩子

    大部分的时候,当我们需要键盘事件的时候,可以通过在主窗口注册KeyBinding来实现,不过,有的时候我们需要的是全局键盘事件,想在任何一个地方都能使用,最开始的时候我是通过键盘钩子来实现的, 不过键 ...

  7. WPF 利用键盘钩子来捕获键盘,做一些不为人知的事情...完整实例

    键盘钩子是一种可以监控键盘操作的指令. 看到这句话是不是觉得其实键盘钩子可以做很多事情. 场景 当你的程序需要一个全局的快捷键时,可以考虑使用键盘钩子,如大家常用qq的截图快捷键,那么在WPF里怎么去 ...

  8. 钩子编程(HOOK) 安装进程内键盘钩子 (1)

    摘要:钩子能够监视系统或进程中的各种事件消息.截获发往目标窗体的消息并进行处理.这样,我们就能够在系统中安装自己定义的钩子,监视系统中特定事件的发生.完毕特定的功能,比方截获键盘.鼠标的输入.屏幕取词 ...

  9. 键盘钩子监测按键后,获取键码及按键名称(MFC)

    LRESULT CALLBACK LowLevelKeyboardProc(int nCode,WPARAM wParam,LPARAM lParam){ if(nCode ==HC_ACTION & ...

随机推荐

  1. VMware安装Ubuntu配置NAT模式下静态IP,解决访问外网问题

    安装好VMware后,打开网络连接可以看到有VMware Network Adapter VMnet1和VMware Network Adapter VMnet8两个网络适配器,VMnet1是针对桥接 ...

  2. 初学Python01

    1.文本编辑器区别于交互模式的Python,它可以保存Python代码文件,再次打开还是存在.文件保存时要注意是.py的模式. 2.Windows系统下,应使用命令行模式打开.py 首先进入文件所在磁 ...

  3. how to setting a i2c driver

    How to instantiate I2C devices============================== Unlike PCI or USB devices, I2C devices ...

  4. linux学习-systemd-journald.service 简介

    过去只有 rsyslogd 的年代中,由于 rsyslogd 必须要开机完成并且执行了 rsyslogd 这个 daemon 之 后,登录文件才会开始记录.所以,核心还得要自己产生一个 klogd 的 ...

  5. loj2000 「SDOI2017」数字表格

    there #include <iostream> #include <cstring> #include <cstdio> using namespace std ...

  6. [git 学习篇] git commit原理 --实践体会

    1 现对readme.txt作出修改,增加一行内容: Git has a mutable index called stage. Git is a distributed version contro ...

  7. Spring 4.3.11.RELEASE文档阅读(二):Core Technologies_AOP

    虽然并不是每个问题都有答案,但我想了很多问题.so, just write it down , maybe one day...... AOP: 1,AOP是啥 2,AOP思想是怎么产生的 3,AOP ...

  8. PHP7异常处理

    try { // Code that may throw an Exception or Error. }catch (Exception $e) { } catch (Error $t) { } p ...

  9. Chromo开发常用插件和***工具

    地址:https://www.google.com/chrome/webstore/ ***工具:链接:http://pan.baidu.com/s/1pLakW7T 密码:2gpw Axure RP ...

  10. ACM程序设计选修课——1040: Alex and Asd fight for two pieces of cake(YY+GCD)

    1040: Alex and Asd fight for two pieces of cake Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 27   ...