C#实现键盘钩子
前言:
因为项目中需要使用到快捷键,所以上网找资料了解关于快捷键的实现技术,于是有了键盘钩子的使用学习。在网上了解到,键盘钩子其实只是很多种钩子中的其中一种。所谓钩子:请看下面关于钩子的描述(来自百度百科):
Windows系统是建立在事件驱动的机制上的,说穿了就是整个系统都是通过消息的传递来实现的。而钩子是Windows系统中非常重要的系统接口,用它可以截获并处理送给其他应用程序的消息,来完成普通应用程序难以实现的功能。
钩子可以监视系统或进程中的各种事件消息,截获发往目标窗口的消息并进行处理。这样,我们就可以在系统中安装自定义的钩子,监视系统中特定事件的发生,完成特定的功能,比如截获键盘、鼠标的输入,屏幕取词,日志监视等等。
钩子的本质是一段用以处理系统消息的程序,通过系统调用,将其挂入系统。钩子的种类有很多,每种钩子可以截获并处理相应的消息,每当特定的消息发出,在到达目的窗口之前,钩子程序先行截获该消息、得到对此消息的控制权。此时在钩子函数中就可以对截获的消息进行加工处理,甚至可以强制结束消息的传递。
本文我们主要来谈谈全局钩子和进程钩子的使用。
全局钩子:全局钩子,能够截获所有运行在操作系统上的程序发送的消息,但是因其全局性,钩子安装之后,会比较损耗性能,在使用完毕之后,必须实时的卸载。
进程钩子:可以针对某一个进程,仅仅截获某一个应用程序的消息,比较具有针对性,适用于普通的信息管理系统。
钩子程序是封装在User32.dll中的方法,如果我们的程序需要用到钩子,首先需要将钩子对应的程序集导入到我们的系统中。代码如下:
/// <summary>
/// 获取窗体线程ID
/// </summary>
/// <param name="hwnd">窗体句柄</param>
/// <param name="ID"></param>
/// <returns></returns>
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, int ID); /// <summary>
/// 设置钩子
/// </summary>
/// <param name="idHook">钩子id</param>
/// <param name="lpfn">钩子处理方法</param>
/// <param name="hInstance">句柄</param>
/// <param name="threadId">线程id</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern int SetWindowsHookEx(int idHook, HookHandle lpfn, IntPtr hInstance, int threadId); /// <summary>
/// 取消钩子
/// </summary>
/// <param name="idHook"></param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook); /// <summary>
/// 调用下一个钩子
/// </summary>
/// <param name="idHook">本钩子id</param>
/// <param name="nCode"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam); /// <summary>
/// 获取当前线程ID
/// </summary>
/// <returns></returns>
[DllImport("kernel32.dll")]
public static extern int GetCurrentThreadId(); [DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);
钩子相关方法的导入
在系统使用中,我们对钩子进行了简单的封装,针对全局钩子和进程钩子共有的特性,抽象出钩子基类
/// <summary>
/// 键盘钩子
/// </summary>
public abstract class KeyBoardHook
{
#region 字段
/// <summary>
/// 当前钩子的id
/// </summary>
protected int hHookId = ;
/// <summary>
/// 外部调用的键盘处理事件
/// </summary>
protected ProcessKeyHandle clientMethod = null;
/// <summary>
/// 当前模块的句柄
/// </summary>
protected IntPtr hookWindowPtr = IntPtr.Zero;
/// <summary>
/// 勾子程序处理事件
/// </summary>
protected HookHandle keyBoardHookProcedure;
protected int hookKey;
#endregion #region 属性
/// <summary>
/// 获取或设置钩子的唯一标志
/// </summary>
public int HookKey
{
get { return this.hookKey; }
set { this.hookKey = value; }
}
#endregion /// <summary>
/// 安装钩子
/// </summary>
/// <param name="clientMethod"></param>
/// <returns></returns>
public abstract bool Install(ProcessKeyHandle clientMethod);
/// <summary>
/// 钩子处理函数
/// </summary>
/// <param name="nCode"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
protected abstract int GetHookProc(int nCode, int wParam, IntPtr lParam); /// <summary>
/// 卸载钩子
/// </summary>
public virtual void UnInstall()
{
bool retKeyboard = true;
if (hHookId != )
{
retKeyboard = Win32API.UnhookWindowsHookEx(hHookId);
hHookId = ; }
//if (hookWindowPtr != IntPtr.Zero)
//{
// Marshal.FreeHGlobal(hookWindowPtr);
//}
if (!retKeyboard)
{
throw new Exception("UnhookWindowsHookEx failed.");
}
}
}
钩子基类
/// <summary>
/// 全局钩子,慎用,获取所有进程的按键信息,耗费系统资源
/// </summary>
internal class GlobalHook : KeyBoardHook
{
public override bool Install(ProcessKeyHandle clientMethod)
{
try
{
//客户端传入的委托,即截获消息之后,对消息的过滤和处理的方法
this.clientMethod = clientMethod; // 安装键盘钩子
if (hHookId == )
{
keyBoardHookProcedure = GetHookProc; hookWindowPtr = Win32API.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); hHookId = Win32API.SetWindowsHookEx(
(int)HookType.WH_KEYBOARD_LL,//调用系统方法安装钩子,第一个参数标识钩子的类型13为全局钩子
keyBoardHookProcedure,
hookWindowPtr,
); //如果设置钩子失败.
if (hHookId == ) UnInstall();
}
return true;
}
catch
{
return false;
}
} protected override int GetHookProc(int nCode, int wParam, IntPtr lParam)
{
////参数 nCode 的可选值:
//HC_ACTION = 0; {}
//HC_GETNEXT = 1; {}
//HC_SKIP = 2; {}
//HC_NOREMOVE = 3; {}
//HC_NOREM = HC_NOREMOVE; {}
//HC_SYSMODALON = 4; {}
//HC_SYSMODALOFF = 5; {} if (nCode >= && nCode != )
{
//wParam = = 0x101 // 键盘抬起
// 键盘按下
if (wParam == 0x100)
{
//触发事件把安装信息通知客户端
if (clientMethod != null)
{
KeyBoardHookStruct kbh = (KeyBoardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyBoardHookStruct));
Keys key = (Keys)kbh.VkCode;
bool handle;
clientMethod(this.hookKey, key, out handle);
if (handle)//如果处理了就直接停止 1:
{
Win32API.CallNextHookEx(hHookId, nCode, wParam, lParam);
return ;
}
}
} }
return Win32API.CallNextHookEx(hHookId, nCode, wParam, lParam);
}
} /// <summary>
/// 键盘钩子的类型
/// </summary>
public enum KeyBoardHookType
{
Global = ,//全局钩子
Process = //进程钩子
} /// <summary>
/// 键盘处理事件委托,接受返回win32的委托
/// </summary>
/// <param name="nCode"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
public delegate int HookHandle(int nCode, int wParam, IntPtr lParam); /// <summary>
/// 客户端处理钩子回调的键盘处理事件
/// </summary>
/// <param name="hookKey">钩子的唯一标志</param>
/// <param name="key">按下的键</param>
/// <param name="handle">客户端是否处理了这个值</param>
public delegate void ProcessKeyHandle(int hookKey, Keys key, out bool handle); internal enum HookType
{
WH_KEYBOARD = ,//私有钩子
WH_KEYBOARD_LL = //全局钩子
} /// <summary>
/// 全局钩子时,转化的结构体
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal class KeyBoardHookStruct
{
/// <summary>
/// 表达一个在1到254间的虚拟键盘码
/// </summary>
public int VkCode { get; set; }
public int ScanCode { get; set; }
public int Flags { get; set; }
public int Time { get; set; }
public int DwExtraInfo { get; set; }
}
全局钩子代码
上面的代码,对全局钩子的使用进行了封装,只需要创建GlobalHook类,将需要监听处理的委托传递给构造函数,即可创建和使用钩子。再来看看进程钩子的代码,类似于全局钩子:
/// <summary>
/// 进程钩子,只能捕捉本进程的按键信息
/// </summary>
internal class ProcessHook : KeyBoardHook
{
public override bool Install(ProcessKeyHandle clientMethod)
{
try
{
this.clientMethod = clientMethod; // 安装键盘钩子
if (hHookId == )
{
keyBoardHookProcedure = GetHookProc; hookWindowPtr = Win32API.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); hHookId = Win32API.SetWindowsHookEx(
(int)HookType.WH_KEYBOARD,
keyBoardHookProcedure,
IntPtr.Zero,
Win32API.GetCurrentThreadId()
); //如果设置钩子失败.
if (hHookId == ) UnInstall();
}
return true;
}
catch
{
return false;
}
} protected override int GetHookProc(int nCode, int wParam, IntPtr lParam)
{
if (nCode == && nCode != )
{
bool isKeyDown = false;
if (IntPtr.Size == )
{
isKeyDown = (((lParam.ToInt32() & 0x80000000) == ));
}
if (IntPtr.Size == )
{
isKeyDown = (((lParam.ToInt64() & 0x80000000) == ));
}
// 键盘按下
if (isKeyDown)
{
//Debug.WriteLine("key down_________________________");
//触发事件把安装信息通知客户端
if (clientMethod != null)
{
//进程钩子,按键值在这里
Keys keyData = (Keys)wParam;
bool handle;
clientMethod(this.hookKey, keyData, out handle);
if (handle)//如果处理了就直接停止 1:
{
Win32API.CallNextHookEx(hHookId, nCode, wParam, lParam);
return ;
}
}
} }
return Win32API.CallNextHookEx(hHookId, nCode, wParam, lParam);
}
}
进程钩子
再来看看如何在客户端使用钩子。只需在需要使用到钩子的窗体中,按如下方式编写代码即可:
private KeyBoardHook hook;
/// <summary>
/// 创建进程键盘钩子
/// </summary>
protected void CreateProcessHook()
{
if (hook != null)
{
hook.UnInstall();
}
//使用工厂类创建出对应的钩子。
hook = KeyBoardHookHelper.CreateHook(KeyBoardHookType.Process);
if (hook.Install(ClientProcessKeyHandle))
{
hook.HookKey = this.GetHashCode();
}
} //客户端传给钩子的监听方法。
private void ClientProcessKeyHandle(int hookKey, Keys key, out bool handle)
{
handle = false; if (hookKey == hook.HookKey)
{
OnClientProcessKeyHandle(key, out handle);
}
return;
} /// <summary>
/// 子类重写键盘钩子处理方法(系统中存在多个窗体,可将该代码放入到窗体基类中,子类只需重写该方法即可。)
/// </summary>
/// <param name="key"></param>
/// <param name="handle"></param>
protected virtual void OnClientProcessKeyHandle(Keys key, out bool handle)
{ handle = false;
//截获消息并进行处理。
if ((int)key == (int)Keys.F2)//保存,
{
OnSaveOrder(this.tsbtn_Save);
handle = true;
}
}
客户端调用
C#实现键盘钩子的更多相关文章
- C#键盘钩子 鼠标钩子
		
最新对C#模拟键盘按键,鼠标操作产生了兴趣.特从网上收集了一些常用的API用来调用键盘,鼠标操作. class Win32API { #region DLL导入 /// <summary> ...
 - 基于C#实现的HOOK键盘钩子实例代码
		
本文所述为基于C#实现的HOOK实例,该实例可用来屏蔽系统热键.程序主要实现了安装钩子.传递钩子.卸载钩子等功能.在传递钩子中:<param name="pHookHandle&quo ...
 - 2.添加键盘钩子。向进程中注入dll
		
学习笔记 1.首先要建立mfc的动态链接库.在def文件中放入要导出的函数名. 2.添加函数如下 //安装钩子 //HHOOK SetWindowsHookEx( // int idHook,//钩子 ...
 - C#鼠标键盘钩子
		
using System;using System.Collections.Generic; using System.Reflection; using System.Runtime.Interop ...
 - C# 键盘钩子
		
p{ text-align:center; } blockquote > p > span{ text-align:center; font-size: 18px; color: #ff0 ...
 - 在WPF中快速实现键盘钩子
		
大部分的时候,当我们需要键盘事件的时候,可以通过在主窗口注册KeyBinding来实现,不过,有的时候我们需要的是全局键盘事件,想在任何一个地方都能使用,最开始的时候我是通过键盘钩子来实现的, 不过键 ...
 - WPF 利用键盘钩子来捕获键盘,做一些不为人知的事情...完整实例
		
键盘钩子是一种可以监控键盘操作的指令. 看到这句话是不是觉得其实键盘钩子可以做很多事情. 场景 当你的程序需要一个全局的快捷键时,可以考虑使用键盘钩子,如大家常用qq的截图快捷键,那么在WPF里怎么去 ...
 - 钩子编程(HOOK) 安装进程内键盘钩子 (1)
		
摘要:钩子能够监视系统或进程中的各种事件消息.截获发往目标窗体的消息并进行处理.这样,我们就能够在系统中安装自己定义的钩子,监视系统中特定事件的发生.完毕特定的功能,比方截获键盘.鼠标的输入.屏幕取词 ...
 - 键盘钩子监测按键后,获取键码及按键名称(MFC)
		
LRESULT CALLBACK LowLevelKeyboardProc(int nCode,WPARAM wParam,LPARAM lParam){ if(nCode ==HC_ACTION & ...
 
随机推荐
- 日志切割logrotate和定时任务crontab详解
			
1.关于日志切割 日志文件包含了关于系统中发生的事件的有用信息,在排障过程中或者系统性能分析时经常被用到.对于忙碌的服务器,日志文件大小会增长极快,服务器会很快消耗磁盘空间,这成了个问题.除此之外,处 ...
 - GoF23种设计模式之行为型模式之责任链模式
			
一.概述 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并且沿着这条链传递请求,直到有一个对象处理它为止.其设计思想是:给对多个对象处理一个请求的机会, ...
 - python文件打包为exe可执行文件的方法
			
我自己常用Pyinstaller库打包 第一步: 安装pyinstaller库 pip install pyinstaller 第二步: 在py文件所在目录输入 mydemo.py是自己写的py文 ...
 - leetcode-23-DynamicProgramming-1
			
357. Count Numbers with Unique Digits 解题思路: 用arr[i]存放长度为i时,各位互不相同的数字的个数,所以arr[1]=10,arr[2]=9*9.(第一位要 ...
 - 实现hadoop自动安装包
			
最近研究hadoop,需要安装多个dadanode,想从重复劳动解脱出来,只能自己实现自动安装包,开始考虑使用shell.python等实现,感觉比较费时间,用installshield又有点牛刀小试 ...
 - BZOJ 4425: [Nwerc2015]Assigning Workstations分配工作站
			
难度在于读题 #include<cstdio> #include<algorithm> #include<queue> using namespace std; p ...
 - Django two
			
http://www.cnblogs.com/yuanchenqi/articles/6083427.html Django: 1.安装Django pip install django 2.创建p ...
 - 总结:PHP值得注意的几个问题
			
1.除了变量和常量区分大小写外,其他的标识符不区分大小写(例如关键字,类名,函数名等): 2. >>>是无符号右移,不管第一位是0还是1,右移后前面都是补0: 3.在函数中传递数组, ...
 - socket编程了解
			
Socket 编程 Socket通讯原理描述: 套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象.它们允许程序接受并进行连 ...
 - jqery实现一个图标上下滑动效果
			
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...