源自: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. COGS1871 [国家集训队2011]排队(魏铭)

    bzoj:http://www.lydsy.com/JudgeOnline/problem.php?id=2141 cogs:http://cogs.pro:8080/cogs/problem/pro ...

  2. IDEA 出现 updating indices 卡进度条问题的解决方案并加快索引速度

    缺点: 这样的话,前端的接口(也就是字符串)就搜索不到了. C:\Users\Administrator\.IntelliJIdea2017.3\system  删除里面的caches文件夹(这里的 ...

  3. Security Permissions Caching

    Security Permissions Caching Security permission caching is implemented in Security Adapters - class ...

  4. SpringBoot实现监听redis key失效事件

    需求: 处理订单过期自动取消,比如下单30分钟未支付自动更改订单状态 解决方案1: 可以利用redis天然的key自动过期机制,下单时将订单id写入redis,过期时间30分钟,30分钟后检查订单状态 ...

  5. oracle的多表合并查询-工作心得

    本随笔文章,由个人博客(鸟不拉屎)转移至博客园 发布时间: 2018 年 11 月 29 日 原地址:https://niaobulashi.com/archives/oracle-select-al ...

  6. Linux命令的那些事(二)

    回顾Linux(一) 学习了以下命令: mkdir/rmdir/ls/rm/pwd/cd/touch/tree/man/--help 想具体了解请看上一篇文章跳转 在Linux中推荐大家使用subli ...

  7. python 网页转pdf

    主要使用的是wkhtmltopdf的Python封装——pdfkit centos环境 安装:Install python-pdfkit pip install pdfkit 安装:Install w ...

  8. Linux系列——安装双系统Ubuntu

    作为一个穷人,电脑破得不行却没钱换,怎么办呢,不如换个Ubuntu吧,没有Windows那么多后台应用,在我这台古董上稍微流畅一点. Linux有很多发行版,比较流行和适合入门的就是Ubuntu和De ...

  9. 前端_html

    目录 HTML介绍 标签说明 常用标签 <!DOCTYPE>标签 <head>内常用标签 <body>内常用标签 特殊字符 其他:各种各样的标签 HTML的规范 H ...

  10. Visual Studio AI 离线模型训练(window 7)

    本篇博客用tensorflow训练自带的数据mnist,参考自博客. 背景: 搭建好AI环境:查看 window 7 64位 准备工作: 在搭建AI环境过程中下载的samples-for-ai不是最新 ...