源自: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. CF1039D You Are Given a Tree 根号分治,贪心

    CF1039D You Are Given a Tree LG传送门 根号分治好题. 这题可以整体二分,但我太菜了,不会. 根号分治怎么考虑呢?先想想\(n^2\)暴力吧.对于每一个要求的\(k\), ...

  2. [算法]用java实现0-1背包和部分背包问题

    问题描述: 0-1背包问题,部分背包问题(课本P229)实验要求: (1)实现0-1背包的动态规划算法求解 (2)实现部分背包的贪心算法求解 0-1背包问题代码: public static void ...

  3. 约束4:唯一约束,Check约束和null

    大家知道,关系型数据库的逻辑运算的结果是三值型的,TRUE,FALSE和UNKNOWN,特别是,NULL值和任何值都不相等,任何值和NULL的比较,返回的逻辑结果都是unknown.而NULL值在唯一 ...

  4. javaweb学习4——HttpServletRequest的使用

    声明:本文只是自学过程中,记录自己不会的知识点的摘要,如果想详细学习JavaWeb,请到孤傲苍狼博客学习,JavaWeb学习点此跳转 本文链接:https://www.cnblogs.com/xdp- ...

  5. dbutis事务管理

    1.在dao层用dbutils实现事务管理 //从a--->b帐户转100元 public void transfer() throws SQLException{ Connection con ...

  6. renren_fast性能测试平台的安装部署

    1.从GitHub下载源码: https://github.com/zyanycall/stressTestPlatform git clone https://github.com/zyanycal ...

  7. 利用shell连接服务器

    #应用 连接timesten 数据库 host = Linux(ip, 'user', 'pwd') # 传入Ip,用户名,密码host.connect() #主机开启cdsql = host.sen ...

  8. 《数据结构与算法图解》 分享 pdf下载

    链接:https://pan.baidu.com/s/1gOMlwU5ucHYDVazvVMk2uw提取码:bk5x

  9. Final发布用户使用报告 -- Thunder团队

    Thunder爱阅app Final发布用户使用报告 用户数量:14人 以下为用户评论:(注:为了保护用户的姓名权,以下用户名以昵称形式给出.) 序列 昵称 个人信息 获得软件途径 使用次数 用户评论 ...

  10. 冲刺One之站立会议8 /2015-5-21

    今天我们把聊天界面做了优化和改进,主要实现了聊天的功能.显示了正在进行通信的成员列表,和当前状态,是否连通和正常通信,大体完成了预期的目标. 燃尽图8