源自:https://blog.csdn.net/programvae/article/details/80292076

最近碰巧要使用键盘钩子,于是在网上搜索了一番,发现大多数博客的文章都是雷同的,根本就没有讲清楚全局钩子和局部钩子的区别,于是特开一贴,讲全局钩子和局部钩子捋一捋。也供后面的人学习。
   因为大部分应用都应该采用局部钩子,所以我这儿使用的是局部钩子,而全局钩子的例子网上到处都是。
   大部分网上参考文章都只是展示了全局钩子的写法,而线程钩子的写法和介绍相对少一些,特别是关键语句上如果定义的不正确是没有任何效果的,在自己反复尝试后决定留下一个正确的版本分享出来
    代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms; namespace AssistToolSet.Util
{
/// <summary>
/// 键盘钩子类
/// </summary>
public class Hook
{
public delegate int HookProc(WH_CODE nCode, Int32 wParam, IntPtr lParam);
public enum WH_CODE : int
{
WH_JOURNALRECORD = ,
WH_JOURNALPLAYBACK = ,
/// <summary>
/// 进程钩子
/// </summary>
WH_KEYBOARD = , /// <summary>
/// 底层键盘钩子 全局钩子就是用这个 /// </summary>
WH_KEYBOARD_LL = ,
} public enum HC_CODE : int
{
HC_ACTION = ,
HC_GETNEXT = ,
HC_SKIP = ,
HC_NOREMOVE = ,
HC_NOREM = ,
HC_SYSMODALON = ,
HC_SYSMODALOFF =
} /// <summary>
/// 安装钩子
/// </summary>
[DllImport("user32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SetWindowsHookEx(WH_CODE idHook, HookProc lpfn, IntPtr pInstance, uint threadId); /// <summary>
/// 卸载钩子
/// </summary>
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(IntPtr pHookHandle);
/// <summary>
/// 传递钩子
/// </summary>
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(IntPtr pHookHandle, WH_CODE nCodem, Int32 wParam, IntPtr lParam); /// <summary>
/// 获取全部按键状态
/// </summary>
/// <param name="pbKeyState"></param>
/// <returns>非0表示成功</returns>
[DllImport("user32.dll")]
public static extern int GetKeyboardState(byte[] pbKeyState); /// <summary>
/// 获取程序集模块的句柄
/// </summary>
/// <param name="lpModuleName"></param>
/// <returns></returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName); /// <summary>
/// 获取当前进程中的当前线程ID
/// </summary>
/// <returns></returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern uint GetCurrentThreadId(); #region 私有变量 private byte[] mKeyState = new byte[];
private Keys mKeyData = Keys.None; //专门用于判断按键的状态 /// <summary>
/// 键盘钩子句柄
/// </summary>
private IntPtr mKetboardHook = IntPtr.Zero; /// <summary>
/// 键盘钩子委托实例
/// </summary>
private HookProc mKeyboardHookProcedure; #endregion #region 键盘事件 public event KeyEventHandler OnKeyDown;
public event KeyEventHandler OnKeyUp; #endregion /// <summary>
/// 构造函数
/// </summary>
public Hook()
{
GetKeyboardState(this.mKeyState);
} ~Hook()
{
UnInstallHook();
} /// <summary>
/// 键盘钩子处理函数
/// </summary>
private int KeyboardHookProc(WH_CODE nCode, Int32 wParam, IntPtr lParam)
{
/*全局钩子应该这样设定
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
*/
// 定义为线程钩子时,wParam的值是击打的按键,与Keys里的对应按键相同
if ((nCode == (int)HC_CODE.HC_ACTION) && (this.OnKeyDown != null || this.OnKeyUp != null))
{
mKeyData = (Keys)wParam;
KeyEventArgs keyEvent = new KeyEventArgs(mKeyData);
//这里简单的通过lParam的值的正负情况与按键的状态相关联
if (lParam.ToInt32() > && this.OnKeyDown != null)
{
this.OnKeyDown(this, keyEvent);
}
else if (lParam.ToInt32() < && this.OnKeyUp != null)
{
this.OnKeyUp(this, keyEvent);
}
}
if (ShortcutManagement.s_bHotkeyUsed)
{
ShortcutManagement.s_bHotkeyUsed = false;
return ;
} return CallNextHookEx(this.mKetboardHook, nCode, wParam, lParam);
}
/// <summary>
/// 安装钩子
/// </summary>
/// <returns></returns>
public bool InstallHook()
{
//线程钩子时一定要通过这个取得的值才是操作系统下真实的线程
uint result = GetCurrentThreadId(); if (this.mKetboardHook == IntPtr.Zero)
{
this.mKeyboardHookProcedure = new HookProc(this.KeyboardHookProc);
//注册线程钩子时第三个参数是空
this.mKetboardHook = SetWindowsHookEx(WH_CODE.WH_KEYBOARD, this.mKeyboardHookProcedure, IntPtr.Zero, result);
/*
如果是全局钩子应该这样使用
this.mKetboardHook = SetWindowsHookEx(WH_CODE.WH_KEYBOARD_LL, mKeyboardHookProcedure,GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);
*/
if (this.mKetboardHook == IntPtr.Zero)
{
return false;
}
}
return true;
} /// <summary>
/// 卸载钩子
/// </summary>
/// <returns>true表示成功 </returns>
public bool UnInstallHook()
{
bool result = true;
if (this.mKetboardHook != IntPtr.Zero)
{
result = UnhookWindowsHookEx(this.mKetboardHook) && result;
this.mKetboardHook = IntPtr.Zero;
}
return result;
}
} }
---------------------
作者:PGEva
来源:CSDN
原文:https://blog.csdn.net/programvae/article/details/80292076
版权声明:本文为博主原创文章,转载请附上博文链接!

通过这次认知,意识到,以后如果要做这些接触相关的api的时候,我们应该尽量去查官方文档,而不是一开始就是查看别人的博客。应该以官方文档为主。一定要记住键盘钩子事件。

32位和64位的系统不一样。

C#全局钩子和局部钩子记录的更多相关文章

  1. Django学习——分页器基本使用、分页器终极用法、forms组件之校验字段、forms组件之渲染标签、forms组件全局钩子,局部钩子

    内容 1 分页器基本使用 2 分页器终极用法 3 forms组件之校验字段 1 前端 <!DOCTYPE html> <html lang="en"> &l ...

  2. form表单钩子,局部钩子和全局钩子

    form表单源码解析: 局部钩子: 全局钩子:

  3. Django学习笔记之form组件的局部钩子和全局钩子

    本文通过注册页面的form组件,查看其中使用的全局钩子和局部钩子. # Create your views here. class RegForm(forms.Form): username = fo ...

  4. Django学习笔记(14)——AJAX与Form组件知识补充(局部钩子和全局钩子详解)

    我在之前做了一个关于AJAX和form组件的笔记,可以参考:Django学习笔记(8)——前后台数据交互实战(AJAX):Django学习笔记(6)——Form表单 我觉得自己在写Django笔记(8 ...

  5. Django12-ModelForm中创建局部钩子和全局钩子

    一.局部钩子 命名规则为clean_对象名称,例如上面定义了username.pwd对象,那么可以定义clean_username.clean_pwd的局部钩子进行规则校验 1.例子:定义一个手机号校 ...

  6. django----利用Form 实现两次密码输入是否一样 ( 局部钩子和全局钩子 )

    from django import forms # 导入表单模块 from django.core.exceptions import ValidationError class RegisterF ...

  7. (32)forms组件(渲染自建规则:局部钩子函数和全局钩子函数)

    要达成渲染自建规则 1.局部钩子函数(某个字段,自定意义规则,不如不能以sb开头,数据库已存在等) 2.全局钩子函数(校验两次密码是否一致) 3.使用css样式 register.html <! ...

  8. django基础之day09,创建一个forms表单组件进行表单校验,知识点:error_messages,label,required,invalid,局部钩子函数,全局钩子函数, forms_obj.cleaned_data,forms_obj.errors,locals(), {{ forms.label }}:{{ forms }},{{ forms.errors.0 }}

    利用forms表单组件进行表单校验,完成用户名,密码,确认密码,邮箱功能的校验 该作业包含了下面的知识点: error_messages,label,required,invalid,局部钩子函数,全 ...

  9. Django框架(十四)-- forms组件、局部钩子、全局钩子

    一.什么是forms组件 forms组件就是一个类,可以检测前端传来的数据,是否合法. 例如,前端传来的邮箱数据,判断邮件格式对不对,用户名中不能以什么开头,等等 二.forms组件的使用 1.使用语 ...

随机推荐

  1. 用C实现单隐层神经网络的训练和预测(手写BP算法)

    实验要求:•实现10以内的非负双精度浮点数加法,例如输入4.99和5.70,能够预测输出为10.69•使用Gprof测试代码热度 代码框架•随机初始化1000对数值在0~10之间的浮点数,保存在二维数 ...

  2. jvm系列 (一) ---jvm内存区域与溢出

    jvm内存区域与溢出 目录 jvm系列(一):jvm内存区域与溢出 jvm系列(二):垃圾收集器与内存分配策略 为什么学习jvm 木板原理,最短的一块板决定一个水的深度,当一个系统垃圾收集成为瓶颈的时 ...

  3. APP测试基本流程以及APP测试要点

    APP测试流程梳理 APP测试要点梳理 链接:http://pan.baidu.com/s/1gfaEZ1x 密码:07yt 1 APP测试基本流程 1.1流程图 1.2测试周期 测试周期可按项目的开 ...

  4. linux多项目分别使用不同jdk版本(tomcat版)

    此操作只针对tomcat 背景:linux服务器普通用户默认版本为jdk6,jboss项目使用jdk6版本 ,但是tomcat需要使用jdk7.当然也可以分开使用不同账户来启用这两个项目,下面主要介绍 ...

  5. 图片隐写--XOR&OR&and

    图片xor的脚本 和图片进行xor or and 运算的脚本 from PIL import Image #import pil def loadImage(filename): img = Imag ...

  6. Linux入门基础(六):Linux系统启动

    系统启动流程 BIOS MBR : Boot Code 执行引导程序-GRUB 加载内核 执行init runlevel BIOS BIOS(basic input output system),我们 ...

  7. 第1章 Linux命令行简介

    1.1 Linux命令行概述 1.2 在Linux命令行下查看命令帮助 1.3 Linux关机.重启.注销命令 1.4 老男孩的运维思想 1.1 Linux命令行概述 1.1.1 Linux命令行的作 ...

  8. TPO-19 C1 Discussing A Point Raised In A Lecture

    TPO-19 C1 Discussing A Point Raised In A Lecture 第 1 段 1.Listen to a conversation between a student ...

  9. HIVE函数的UDF、UDAF、UDTF

    一.词义解析 UDF(User-Defined-Function) 一进一出 UDAF(User- Defined Aggregation Funcation) 多进一出 (聚合函数,MR) UDTF ...

  10. python中__name__属性的使用

    python常用模块目录 1.打印出函数名字而非函数名对应的地址 )打印的是函数地址 def func(): print("我是%s函数"%func) func() ------- ...