源自: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. 2_C语言中的数据类型 (七)类型限定

    1.1       类型限定 1.1.1          const const是代表一个不能改变值的常量 1.1.2          volatile 代表变量是一个可能被CPU指令之外的地方改 ...

  2. 服务器路由配置--Route

    第1章 命令配置 虚拟服务器 网卡配置信息 虚拟网卡名称 虚拟网卡模式 服务器01 eth1 10.0.0.10/24 nat模式 服务器02 eth2 10.0.0.11/24 nat模式 eth3 ...

  3. Jenkins远程测试

    Jenkins远程测试 网络测试,如,selenium 测试可以通过主从和 selenium 套件插件远程安装在机器上运行.下列步骤显示了如何运行使用此配置来进行远程测试. 第1步 - 确保主从配置到 ...

  4. 打包一个传统的ASP.NET web app作为Docker镜像

    (1)针对NerdDinner应用的Dockerfile内容如下 PS E:\DockeronWindows\Chapter02\ch02-nerd-dinner> cat .\Dockerfi ...

  5. 1042 Shuffling Machine

    一.题目描述 Shuffling is a procedure used to randomize a deck of playing cards. Because standard shufflin ...

  6. string类型的常用方法

    1. 在尾部插入/删除元素 string s("hello"); // 插入/删除一个字符 s.push_back('!'); s.pop_back(); // 插入多个字符 s. ...

  7. 王者荣耀交流协会final发布文案美工展示博客

    logo: 我们的logo是蓝底白字,非常简洁大气的设计感,上面印有我们的软件名称,更好的直观的彰显了我们的主题.我们的软件就是要迎合使用者,给使用者更加方便快捷的工作体验,更好的衡量自己的时间分配. ...

  8. oracle和mysql在sql中生成uuid的方法

    1,oracle sys_guid() 2,mysql uuid()

  9. linux下创建virtualenv时指定python版本

    virtualenv是python开发中一个重要的工具,它可以帮助我们创建一个干净的python解释环境,创建虚拟环境时,这个虚拟环境的python版本往往是系统默认的2.x版本.别急,我们只需要一条 ...

  10. Android界面设计适配不同屏幕的尺寸和密度解读

    Android是运行在各种提供不同的屏幕尺寸和密度的设备.Android系统提供跨设备的统一开发环境和处理大部分的工作,以调整每个应用程序的用户界面,以在其上显示的画面. 同时,该系统提供了API,允 ...