C#全局钩子使用
最近遇到了一个需要处理键盘按键释放消息的问题,我在使用重写ProcessCmdKey之后,发现其无法响应KeyUp消息,不知是被什么东西拦截了,查阅了网上的一些资料,使用全局钩子解决了这个问题,在此把过程记录下来。首先,在使用钩子前我们先来了解一下要使用到的API函数。第一步:声明API//安装钩子[DllImport("user32.dll", Ch...
最近遇到了一个需要处理键盘按键释放消息的问题,我在使用重写ProcessCmdKey之后,发现其无法响应KeyUp消息,不知是被什么东西拦截了,查阅了网上的一些资料,使用全局钩子解决了这个问题,在此把过程记录下来。
首先,在使用钩子前我们先来了解一下要使用到的API函数。
第一步:声明API
//安装钩子
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
//卸载钩子
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
//继续下一个钩子
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
// 取得当前线程编号(线程钩子需要用到)
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
//使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);
第二步:声明一个委托,该委托在捕获键盘消息是会自动调用其对应的方法
public event KeyEventHandler KeyUpEvent;
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
static int hKeyboardHook = 0; //声明键盘钩子处理的初始值,钩子是否安装成功
public const int WH_KEYBOARD_LL = 13; //线程键盘钩子监听鼠标消息设为2,全局键盘监听鼠标消息设为13
HookProc KeyboardHookProcedure; //声明KeyboardHookProcedure作为HookProc类型
第三步:安装和卸载钩子
//安装钩子
public void Start()
{
if (hKeyboardHook == 0)
{
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure,
GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);
/*关于SetWindowsHookEx的第三个参数hInstance,
如果threadId标识当前进程创建的一个线程,而且子程代码位于当前进程,
hInstance必须为NULL*/
/*关于SetWindowsHookEx的第四个参数threadId,如果为全局钩子,
则应该设置为0,如果是线程钩子,则使用GetCurrentThreadId获取子线程的ID*/
//************************************
//键盘线程钩子
//SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId());//指定要监听的线程idGetCurrentThreadId(),
//************************************
//如果SetWindowsHookEx失败
if (hKeyboardHook == 0)
{
Stop();
throw new Exception("安装键盘钩子失败");
}
}
}
//卸载钩子
public void Stop()
{
bool retKeyboard = true;
if (hKeyboardHook != 0)
{
retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = 0;
}
if (!(retKeyboard)) throw new Exception("卸载钩子失败!");
}
第四步:钩子 子线程
在这之前我们先来看下子程要用的三个参数int类型的 nCode, Int32类型的 wParam, IntPtr类型的 lParam,nCode参数是钩子代码,钩子子程使用这个参数来确定任务。wParam和lParam参数包含了消息信息,我们可以从中提取需要的信息,为了将这两个参数转成我们更容易理解的消息,在这定义了一个结构体(对于键盘来说),将wParam和lParam转化成我们更加容易看懂的信息。
//键盘结构
[StructLayout(LayoutKind.Sequential)]
public class KeyboardHookStruct
{
public int vkCode; //定一个虚拟键码。该代码必须有一个价值的范围1至254
public int scanCode; // 指定的硬件扫描码的关键
public int flags; // 键标志
public int time; // 指定的时间戳记的这个讯息
public int dwExtraInfo; // 指定额外信息相关的信息
}
private const int WM_KEYUP = 0x101;//KEYUP
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
// 侦听键盘事件
if (nCode >= 0 )
{
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
// 键盘抬起
if (KeyUpEvent != null&&wParam == WM_KEYUP)
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
//if (keyData == Keys.Up)
//{
//使用EventHandler来传递数据
KeyEventArgs e = new KeyEventArgs(keyData);
KeyUpEvent(this, e);
//MessageBox.Show("捕捉到了按键释放");
//}
}
}
//如果返回1,则结束消息,这个消息到此为止,不再传递。
//如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
在上面的子程里面,我们使用EventHandler来向外部传递数据,那么我们就要先声明一个事件
public event KeyEventHandler KeyUpEvent;
至此,钩子类已经基本完成,以下是整个类的代码:
class KeyboardHook
{
public event KeyEventHandler KeyUpEvent;
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
static int hKeyboardHook = 0; //声明键盘钩子处理的初始值
public const int WH_KEYBOARD_LL = 13; //线程键盘钩子监听鼠标消息设为2,全局键盘监听鼠标消息设为13
HookProc KeyboardHookProcedure; //声明KeyboardHookProcedure作为HookProc类型
//键盘结构
[StructLayout(LayoutKind.Sequential)]
public class KeyboardHookStruct
{
public int vkCode; //定一个虚拟键码。该代码必须有一个价值的范围1至254
public int scanCode; // 指定的硬件扫描码的关键
public int flags; // 键标志
public int time; // 指定的时间戳记的这个讯息
public int dwExtraInfo; // 指定额外信息相关的信息
}
//使用此功能,安装了一个钩子
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
//调用此函数卸载钩子
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
//使用此功能,通过信息钩子继续下一个钩子
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
// 取得当前线程编号(线程钩子需要用到)
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
public void Start()
{
// 安装键盘钩子
if (hKeyboardHook == 0)
{
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);
//************************************
//键盘线程钩子
//SetWindowsHookEx(13, KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId());//指定要监听的线程idGetCurrentThreadId(),
//************************************
//如果SetWindowsHookEx失败
if (hKeyboardHook == 0)
{
Stop();
throw new Exception("安装键盘钩子失败");
}
}
}
public void Stop()
{
bool retKeyboard = true;
if (hKeyboardHook != 0)
{
retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = 0;
}
if (!(retKeyboard)) throw new Exception("卸载钩子失败!");
}
private const int WM_KEYUP = 0x101;//KEYUP
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
// 侦听键盘事件
if (nCode >= 0 )
{
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
// 键盘抬起
if (KeyUpEvent != null&&wParam == WM_KEYUP)
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
//if (keyData == Keys.Up)
//{
KeyEventArgs e = new KeyEventArgs(keyData);
KeyUpEvent(this, e);
//MessageBox.Show("捕捉到了按键释放");
//}
}
}
//如果返回1,则结束消息,这个消息到此为止,不再传递。
//如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
~KeyboardHook()
{
Stop();
}
}
第五步:接下来就是调用了。
首先我们声明数据传递事件 并且初始化钩子类
private KeyEventHandler myKeyEventHandeler = null;//按键钩子
private KeyboardHook k_hook = new KeyboardHook();
接下来,写两个函数分别负责安装和卸载钩子,其中hook_KeyUp是钩子捕捉到消息后具体需要实现的方法
public void start()
{
myKeyEventHandeler = new KeyEventHandler(hook_KeyUp);
k_hook.KeyUpEvent += myKeyEventHandeler;//钩住键按下
k_hook.Start();//安装键盘钩子
}
public void stop()
{
if (myKeyEventHandeler != null)
{
k_hook.KeyUpEvent -= myKeyEventHandeler;//取消按键事件
myKeyEventHandeler = null;
k_hook.Stop();//关闭键盘钩子
}
}
private void hook_KeyUp(object sender, KeyEventArgs e)
{
// 这里写具体实现
textBox1.Text = "捕捉到了" + e.KeyCode.ToString() + "键释放";
}
总结:突然发现没什么好说的,该说的都在上面了....
更多推荐
所有评论(0)