在实际应用中,键盘监控是一种很常见的技术,它包括按键的记录、按键的过滤、按键的修改(映射)等。比方说,我们想统计用户的击键情况,这个就是按键的记录;我们想屏蔽某些系统键(例如Alt键、Win键),这个是按键的过滤;我们想改变按键的值,例如按下A,出来的是Z,在例如按下A,出来按键的组合SDFG等(貌似这个在游戏中比较多,有些游戏的大绝招都比较难按,用这个一劳永逸),这个是按键的修改。

  键盘监控的具体实现,用的是微软的Keyboard Hook API函数。

  首先解释下,什么是Hook函数。

  WINDOW的消息处理机制为了能在应用程序中监控系统的各种事件消息,提供了挂接各种反调函数(HOOK)的功能。这种挂钩函数(HOOK)类似扩充中断驱动程序,挂钩上可以挂接多个反调函数构成一个挂接函数链。系统产生的各种消息首先被送到各种挂接函数,挂接函数根据各自的功能对消息进行监视、修改和控制等,然后交还控制权或将消息传递给下一个挂接函数以致最终达到窗口函数。WINDOW系统的这种反调函数挂接方法虽然会略加影响到系统的运行效率,但在很多场合下是非常有用的,通过合理有效地利用键盘事件的挂钩函数监控机制可以达到预想不到的良好效果。

  简单的说,就是在消息到达Window之前,系统允许你安装Hook函数来拦截消息,并对消息进行处理。Hook函数也是有类别的,不同的函数实现不同的功能。

  先看下函数的申明:

  Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" ( _

    ByVal idHook As Integer,  _       '安装的钩子的类型

    ByVal lpfn As HookProc,  _       '消息的处理函数

    ByVal hMod As IntPtr, _        '应用程序事例句柄

    ByVal dwThreadId As Integer _     '线程ID

    ) As Integer
  

  钩子卸载函数

  Private Declare Function UnhookWindowsHookEx Lib "user32" ( _

    ByVal idHook As Integer _      '要卸载的HOOK函数的句柄

    ) As Integer
  

  调用下一个HOOK函数

  Private Declare Function CallNextHookEx Lib "user32" ( _

    ByVal idHook As Integer,  _      '本HOOK函数的句柄

    ByVal nCode As Integer,  _      '消息的类型

    ByVal wParam As Integer,  _      '消息的参数

    ByVal lParam As IntPtr _        '消息的参数

    ) As Integer

  

  消息函数的委托

  Private Delegate Function HookProc( _

    ByVal nCode As Integer,  _      '消息的类型

    ByVal wParam As Integer, _      '消息的参数

    ByVal lParam As IntPtr _        '消息的参数

    ) As Integer
  

  钩子类型的常数

  Private Const  WH_KEYBOARD_LL  As  Integer= 13    '全局键盘钩子(又称为底层)
  Private Const  WH_KEYBOARD As  Integer = 2      '普通键盘钩子

  按键信息结构

  Public Structure KeyboardHookStruct
    Dim vkCode As Integer
    Dim ScanCode As Integer
    Dim Flags As Integer
    Dim Time As Integer
    Dim DwExtraInfo As Integer
  End Structure

  我们用一个类来实现键盘的监控。

  首先定义两个变量

  Private hKeyboardHook As Integer
  Private KeyboardHookProcedure As HookProc

  

  装载钩子的函数

  Public Sub Hook()
    If hKeyboardHook = 0 Then
    KeyboardHookProcedure = New HookProc(AddressOf KeyboardHookProc)
    hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly.GetModules()(0)), 0)

      If hKeyboardHook = 0 Then
        UnHook()
        Throw New Win32Exception(Marshal.GetLastWin32Error)
      End If
         End If
  End Sub

  注:函数执行后,会安装Hook,所有的按键消息在到达window前都会被函数KeyboardHookProc拦截到。我们在后面的KeyboardHookProc函数中处理拦截的消息。

  卸载钩子的函数

  Public Sub UnHook()
    If hKeyboardHook <> 0 Then
      Dim retKeyboard As Integer = UnhookWindowsHookEx(hKeyboardHook)
      hKeyboardHook = 0
      If retKeyboard = 0 Then Throw New Win32Exception(Marshal.GetLastWin32Error)
    End If
  End Sub

  按键消息的处理函数

  Private Function KeyboardHookProc(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
      Dim MyKeyboardHookStruct As KeyboardHookStruct = DirectCast(Marshal.PtrToStructure(lParam, GetType(KeyboardHookStruct)), KeyboardHookStruct)

  

  自己处理的一些代码,例如:记录、屏蔽、映射等

 

  Return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam)
  End Function

  以上就是基本的按键监控类的代码。不过要注意以下几点:

  1、Keyboard的HOOK函数分为两种,WH_KEYBOARD_LL和WH_KEYBOARD。我们一般用第一种,全局的键盘钩子,能拦截所有的键盘按键的消息。

  2、网上有人说,全局的钩子要放在单独的DLL中才能使用。我试了一下,不放在单独的DLL中,在XP+VS2005下,调试和运行都没有问题;在XP+VS2008下,调试会出错,不过编译后能运行;在WIN7+VS2010下,调试会出错,编译后能运行。这方面有研究的网友,望不吝赐教。

  3、WH_KEYBOARD_LL和WH_KEYBOARD,这是两种不同的钩子,虽然最后都是KeyboardHookProc函数处理拦截的消息,但是具体的每个参数的意义却完全不一样。

    WH_KEYBOARD钩子。KeyboardHookProc函数的各个参数意义如下:

    nCode    消息的类型,分HC_ACTION和HC_NOREMOVE

    wParam    按键的虚拟键码

    lParam    按键的相关参数信息,包括重复时间、按键的状态(按下或弹起)等

    WH_KEYBOARD_LL钩子。KeyboardHookProc函数的各个参数意义如下:

    nCode    消息的类型,有HC_ACTION

    wParam    按键的状态(按下或弹起)WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN、WM_SYSKEYUP

    lParam    指向KeyboardHookStruct结构的指针,该结构包含了按键的详细信息。     

    可以看出,这两种钩子的参数的定义是完全不一样的。而在之前的代码中:Dim MyKeyboardHookStruct As KeyboardHookStruct =DirectCast(Marshal.PtrToStructure(lParam, GetType(KeyboardHookStruct)), KeyboardHookStruct)就是将该指针指向的内容复制到指定的结构中。

转自http://grenet.cnblogs.com/

  在上文“键盘监控的实现Ⅰ——Keyboard Hook API函数”中介绍了键盘的Hook API函数。

  重点就在按键消息处理函数

  Private Function KeyboardHookProc(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
      Dim MyKeyboardHookStruct As KeyboardHookStruct = DirectCast(Marshal.PtrToStructure(lParam, GetType(KeyboardHookStruct)), KeyboardHookStruct)

  

  自己处理的一些代码,例如:记录、屏蔽、映射等

 

  Return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam)
  End Function

  

  先看看CallNextHookEx函数,从字面的理解就是调用后面一个钩子函数。若后面已经没有钩子函数呢?很多人都会错误的认为将会将消息传递给Window的消息处理函数。他们认为,消息的处理流程如下面所示:假设有4个钩子函数,分别为钩子A、钩子B、钩子C、钩子D

  物理击键

   ↓

  钩子A

   ↓

  钩子B

   ↓

  钩子C

   ↓

  钩子D

   ↓

  Window的消息处理函数

  他们认为,四个钩子函数中只要有一个返回1(非0),将会中止消息的传递。甚至在钩子函数中不调用CallNextHookEx函数也会阻止消息的传递。甚至认为,修改CallNextHookEx函数的参数就能更改按键消息的传递。

  遗憾的是,这个思路是不对的。

  你可以在钩子函数中删除CallNextHookEx函数的调用,会发现Window还是得到了按键的消息。你也可以尝试修改CallNextHookEx函数的参数,看看会有什么效果。我这样尝试后,直接报错(甚至有莫名的退出)。

  再回过头来看看CallNextHookEx函数,发现它仅仅是调用下一个钩子函数,只是在钩子函数间传递信息。

  正确的消息处理流程应该如下:还是以上面的事例为例。

  物理击键

   ↓

  钩子管理函数←→钩子A←→钩子B←→钩子C←→钩子D

   ↓

  Window消息处理函数

  在钩子A函数中,如果调用CallNextHookEx函数,则会将按键消息传给钩子B;如果不调用CallNextHookEx函数,则钩子B不会得到按键消息,换句话说,钩子B失效了,当然此时的钩子C和钩子D也失效了。为了钩子间和平相处,还是应该在钩子函数里添加CallNextHookEx函数的调用。

  再说说钩子函数的返回值的问题。在上面的事例中,钩子A的返回值决定按键消息是否丢弃。返回值0,告诉系统,消息继续传递给Window消息处理函数;返回值1(非0),告诉系统,消息将丢弃,Window消息处理函数得不到按键的消息。

  所以说,如果只是统计按键的信息

  在钩子函数中的最后直接调用

  Return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam)

  由后面的钩子函数来决定是否丢弃该消息。(大家和平相处)

  如果是屏蔽按键

  在钩子函数中进行判断,满足要求后直接

  CallNextHookEx(hKeyboardHook, nCode, wParam, lParam)  

  Return 1

  告诉系统,丢弃该消息。当然出于礼貌,在之前还是调用CallNextHookEx函数,以便其他的钩子函数处理该消息

  至于修改按键(映射按键),修改参数,调用CallNextHookEx函数是没有用的。因为原本的消息根本就没有修改,你改的只是传给其他钩子函数的消息。而且还非常容易出错。

在“ 键盘监控的实现Ⅰ——Keyboard Hook API函数 ”中,介绍了基本的Key Hook API函数

  在“键盘监控的实现Ⅱ——容易产生误解的CallNextHookEx函数”中,提到按键消息的修改是不能通过更改参数调用CallNextHookEx函数来实现的。

  本文就是要解决这个问题,如何来实现按键消息的修改。这里我们要引入一个函数

  Private Declare Sub keybd_event Lib "user32" (ByVal bvk As Byte, ByVal scan As Byte, ByVal dwflags As Integer, ByVal dwextrainfo As Integer)

  这个函数的目的是模拟按键消息,就像真的在点击键盘似的。

  keybd_event(Keys.Z, 0, 0, 0)  表示模拟按下Z键

  keybd_event(Keys.Z, 0, 2, 0)  表示模拟弹起Z键

  但是不管是真的点击键盘还是模拟按键消息。这个按键消息都会被我们的消息处理函数拦截到。我们该如何区分呢?

  先看看消息处理函数

  Private Function KeyboardHookProc(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
    Dim MyKeyboardHookStruct As KeyboardHookStruct = DirectCast(Marshal.PtrToStructure(lParam, GetType(KeyboardHookStruct)), KeyboardHookStruct)

  

    自己处理的一些代码,例如:记录、屏蔽、映射等

 

    Return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam)
  End Function

  由于安装的是WH_KEYBOARD_LL钩子。按键消息的详细信息是在lParam指向的KeyboardHookStruct中。故在上面的函数中,第一句就是获得该结构的详细信息。我们来看看该结构在MSDN中的解释

  Public Structure KeyboardHookStruct
    Dim vkCode As Integer
    Dim ScanCode As Integer
    Dim Flags As Integer
    Dim Time As Integer
    Dim DwExtraInfo As Integer
  End Structure

  其中各个参数意义如下:

    VkCode:按键的虚拟键码。键盘上的每个按键对应一个虚拟键码

    ScanCode:硬件的扫描码

    Flags:按键消息的详细信息。是一些标识位的组合

    Time:时间。

    DwExtraInfo:扩展到按键消息的信息

  参数Flags是一个八位的二进制,各个位的标识信息如下:

    0位:扩展键的标识位。1表示该键是扩展键;0表示不是。

    1位~3位:保留位,一般是0。

    4位:标识消息的类型,1表示该消息是模拟的;0表示该消息是真实的

    5位:Alt键的标识位。1表示Alt是按下的;0表示Alt键没有被按下

    6位:保留位,一般是0

    7位:按键的状态标识位。1表示按键是弹起的,0表示按键是按下的

  从上面的文字可以看出,在Flags参数中的第四位是能区分消息的类别的。当我们点击键盘的时候,这位是0,是真实的按键消息;当我们用keybd_event函数模拟按键消息时,这位是1。

  这下思路有了。我们在消息处理函数中,将模拟按键消息一律放过,只处理真实的按键消息。就能实现按键消息的修改(映射)。我们以例子来说明,键盘消息的修改,按下A,反馈的是Z。参看下面的函数

  Private Function KeyboardHookProc(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
    Dim MyKeyboardHookStruct As KeyboardHookStruct = DirectCast(Marshal.PtrToStructure(lParam, GetType(KeyboardHookStruct)), KeyboardHookStruct)

    If (MyKeyboardHookStruct.Flags And 16) = 16 Then
      Return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam)
    End If

    If MyKeyboardHookStruct.vkCode=Keys.A Then

      Dim KeyStatue As Integer=(MyKeyboardHookStruct.Flags And 128) / 64

      keybd_event(Keys.Z, 0, KeyStatue, 0)
      Return 1
    Else
      Return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam)
    End If
  End Function

  函数的第一句是获得KeyboardHookStruct结构。后面一个判断是将模拟键盘消息放过。然后紧接着判断是否是A,若是A,则模拟键盘消息Z,并丢弃A这个消息;若不是,放过消息丢给后面的程序。这样一段代码就实现了对键盘消息的修改(映射),将A改成Z。当然,也能实现其他的键盘消息的映射。

window下键盘监控api函数详解相关推荐

  1. c 语言获取系统时间并打印机,C获取打印机状态API函数详解.docx

    C获取打印机状态API函数详解 using System;using System.Collections.Generic;using System.ComponentModel;using Syst ...

  2. RHEL5.9下cacti监控的部署详解

    cacti是一套基于php.mysql.snmp以及rrdtool开发的系统监控图形分析工具,它通过snmp来获取数据,使用rrdtool绘画图形而且完全可以不需要了解rrdtool复杂的命令参数,它 ...

  3. linux下write()和read()函数详解

    最近项目中经常用到多次重复调用读写函数的情况,这篇文章给出了答案,https://www.cnblogs.com/lnlin/p/9492144.html 读函数read ssize_t read(i ...

  4. Linux系统调用八、link系列API函数详解

  5. OpenCV 图像缩放:cv.resize() 函数详解

    目录 系列前言 API 函数详解 参数列表 缩放方式其一 缩放方式其二 两种方式的优先级 关于插值方式 扩展 -- 相关函数 系列前言 这个系列是我第一个想要更下去的系列.每篇会全面介绍一个 Open ...

  6. React Native - Keyboard API使用详解(监听处理键盘事件)

    参考: React Native - Keyboard API使用详解(监听处理键盘事件) 当我们点击输入框时,手机的软键盘会自动弹出,以便用户进行输入. 但有时我们想在键盘弹出时对页面布局做个调整, ...

  7. linux下readlink函数详解

    linux下readlink函数详解 相关函数: stat, lstat, symlink 表头文件: #include <unistd.h> 定义函数:int  readlink(con ...

  8. matlab ext2int函数,Ext2 核心 API 中文详解.pdf

    Ext2 核心 API 中文详解 Andyu QQ Ext2 核心 API 中文详解 序.关于Ext2 核心API 1.关于EXT 2.02 为联合Adobe, Ext2.02 当中重要的一项便是针对 ...

  9. linux下wait函数,Linux wait函数详解

    wait和waitpid出现的原因 SIGCHLD --当子进程退出的时候,内核会向父进程SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止) --子进程退出时,内核将 ...

最新文章

  1. numpy.linspace详解
  2. Unet项目解析(1): run_training.py
  3. 如何使用_如何使用Excel播放音乐
  4. 【MySQL】redo log --- 刷入磁盘过程
  5. .net System.Web.Mail发送邮件
  6. 接口测试常用工具及测试方法(超全)干货
  7. netty java_GitHub - leihuazhe/Java11-Netty-Demo: 基于Java11 构建的 netty 服务端客户端 模块化例子...
  8. 如何将Noteepad++每一行都添加一个逗号
  9. Python 多线程7-线程通信
  10. UEFI开发探索22 – 环境搭建3(使用UDK2018搭建)
  11. DevExpress ChartControl 绘制圆滑曲线
  12. 深度学习:GCN图分类案例
  13. 海龟交易法:如何防止模型死亡
  14. 视频封面如何消重 修改视频md5 百度网盘
  15. FFT—音频频谱设计
  16. 0x03-无线局域网
  17. 一个有意思的404页面
  18. 人的顶级能量从哪里获取?
  19. DLL文件应该放在哪里?
  20. 微型计算机主机的主要包括,微型计算机的主机主要包括

热门文章

  1. autoformr7安装许可证_AutoForm R6 安装方法
  2. 目前有哪些方式训练一个领域的大语言模型? Beyond One-Model-Fits-All A Survey of Domain Specialization LLM
  3. 基于小程序的家校互动平台
  4. html中js不执行的原因
  5. Unity3D网络游戏0.1
  6. 信息服务器在哪里打开,信息服务,详细教您哪里打开Internet信息服务(IIS)管理器...
  7. 入门视频处理2:mts格式怎么转换成MP4格式
  8. 《Dive into DL Pytorch》学习笔记
  9. Oleic acid PEG NHS,Oleic acid PEG Maleimide,油酸聚乙二醇试剂大合集
  10. opencv 实现图片亮度增强和图片美白