这篇文章的题目中把“屏蔽”改成了“拦截”,显然要比以前的版本强一些了。对于以前写的那个DLL
,有一个不够理想的地方,就是仅仅能实现屏蔽。如果想在屏蔽之前加入一些“小动作”,就只能修改DLL
,在LowLevelKeyboardProc
函数中添加代码,实现新的功能。但这样显然不够灵活,这样的DLL
也不具备一般性了。所以我自然而然地想到了回调,Windows
中有很多需要回调函数的API
,我们当然也可以写出这样的API
,这样做的好处就是可以给DLL
调用程序留下足够的接口。此时,DLL
就像一个阀门,我们不关心的按键消息就把它放过去,只把我们关心的按键消息拦截下来,然后进一步处理,而这些处理的代码就写在DLL
调用程序的回调函数中,这样做是最理想不过的了。
相对于前一个版本,修改后的DLL
源代码如下:
/********************************************************************/
/*
文件名: MaskKey.cpp
*/
/*
*/
/*
功能:
标准 DLL ----
利用底层键盘钩子实现拦截键盘任意按键
*/
/*
*/
/*
作者:
卢培培 (goodname008)
时间: 2005.1.18
*/
/*
*/
/* BLOG: http://blog.youkuaiyun.com/goodname008
*/
/********************************************************************/
//
导出函数列表
// StartMaskKey
// StopMaskKey
#define
_WIN32_WINNT
0x0500
//
设置系统版本,
确保可以使用底层键盘钩子
#include
"windows.h"
//
回调函数指针
typedef
BOOL (CALLBACK* LPFNKEYBOARDPROC)(WPARAM, KBDLLHOOKSTRUCT*);
//
全局变量
LPDWORD
g_lpdwVirtualKey = NULL;
// Keycode
数组的指针
int
g_nLength = 0;
// Keycode
数组的大小
BOOL
g_bDisableKeyboard = FALSE;
//
是否屏蔽整个键盘
HINSTANCE
g_hInstance = NULL;
//
模块实例句柄
HHOOK
g_hHook = NULL;
//
钩子句柄
LPFNKEYBOARDPROC
g_lpfnKeyboardProc;
//
键盘钩子回调函数指针
// DLL
入口函数
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
//
保存模块实例句柄
g_hInstance = (HINSTANCE)hModule;
//
在进程结束或线程结束时卸载钩子
switch
(ul_reason_for_call)
{
case
DLL_PROCESS_ATTACH:
break
;
case
DLL_THREAD_ATTACH:
break
;
case
DLL_PROCESS_DETACH:
case
DLL_THREAD_DETACH:
free(g_lpdwVirtualKey);
if
(g_hHook != NULL) UnhookWindowsHookEx(g_hHook);
break
;
}
return
TRUE;
}
//
底层键盘钩子函数
LRESULT CALLBACK LowLevelKeyboardProc(int
nCode, WPARAM wParam, LPARAM lParam)
{
//
拦截键盘的某些按键,
如果 g_bDisableKeyboard
为 TRUE
则拦截整个键盘按键
if
(nCode >= HC_ACTION)
{
KBDLLHOOKSTRUCT* pStruct = (KBDLLHOOKSTRUCT*)lParam;
if
(g_bDisableKeyboard)
if
(g_lpfnKeyboardProc(wParam, pStruct))
return
CallNextHookEx(g_hHook, nCode, wParam, lParam);
else
return true
;
LPDWORD tmpVirtualKey = g_lpdwVirtualKey;
for
(int
i = 0; i < g_nLength; i++)
{
if
(pStruct->vkCode == *tmpVirtualKey++)
if
(g_lpfnKeyboardProc(wParam, pStruct))
return
CallNextHookEx(g_hHook, nCode, wParam, lParam);
else
return true
;
}
}
//
调用系统中的下一个钩子
return
CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
/********************************************************************/
/*
开始拦截键盘按键
*/
/*
*/
/*
参数:
*/
/*
lpdwVirtualKey
Keycode
数组的指针
*/
/*
nLength
Keycode
数组的大小
*/
/*
bDisableKeyboard
是否拦截整个键盘
*/
/*
*/
/*
返回值:
TRUE
成功, FALSE
失败
*/
/********************************************************************/
BOOL WINAPI StartMaskKey(LPDWORD lpdwVirtualKey, int
nLength,
LPFNKEYBOARDPROC lpfnKeyboardProc, BOOL bDisableKeyboard = FALSE)
{
//
如果已经安装键盘钩子则返回 FALSE
if
(g_hHook != NULL || nLength == 0) return
FALSE;
//
将用户传来的 keycode
数组保存在全局变量中
g_lpdwVirtualKey = (LPDWORD)malloc(sizeof
(DWORD) * nLength);
LPDWORD tmpVirtualKey = g_lpdwVirtualKey;
for
(int
i = 0; i < nLength; i++)
{
*tmpVirtualKey++ = *lpdwVirtualKey++;
}
g_nLength = nLength;
g_bDisableKeyboard = bDisableKeyboard;
g_lpfnKeyboardProc = lpfnKeyboardProc;
//
安装底层键盘钩子
g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, g_hInstance, NULL);
if
(g_hHook == NULL) return
FALSE;
return
TRUE;
}
/********************************************************************/
/*
停止拦截键盘按键
*/
/*
*/
/*
参数:
(
无)
*/
/*
*/
/*
返回值:
TRUE
成功, FALSE
失败
*/
/********************************************************************/
BOOL WINAPI StopMaskKey()
{
//
卸载钩子
if
(UnhookWindowsHookEx(g_hHook) == 0) return
FALSE;
g_hHook = NULL;
return
TRUE;
}
当然了,MaskKey.h
头文件中也要加上:
//
回调函数指针
typedef
BOOL (CALLBACK* LPFNKEYBOARDPROC)(WPARAM, KBDLLHOOKSTRUCT*);
下面是在VC
中调用的例子:(两个Dialog
的成员函数,对应两个按钮,再加上一个回调函数)
//
全局键盘钩子回调函数
//
参数: action
标识键盘消息(
按下,
弹起), keyStruct
包含按键信息
BOOL CALLBACK KeyboardProc(WPARAM action, KBDLLHOOKSTRUCT* pKeyStruct)
{
//
判断按键动作
switch
(action)
{
case
WM_KEYDOWN:
break
;
case
WM_KEYUP:
break
;
case
WM_SYSKEYDOWN:
break
;
case
WM_SYSKEYUP:
break
;
}
//
返回 true
表示继续传递按键消息
//
返回 false
表示结束按键消息传递
return false
;
}
void
CMaskKeyAppDlg::OnStartmaskkey()
{
//
屏蔽 A, B, C,
上,
下,
左,
右及两个win
键
DWORD dwVK[] = {'A', 'B', 'C', VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_LWIN, VK_RWIN};
int
nLength = sizeof
(dwVK) / sizeof
(DWORD);
StartMaskKey(dwVK, nLength, KeyboardProc);
}
void
CMaskKeyAppDlg::OnStopmaskkey()
{
StopMaskKey();
}
呵呵,这样是不是让看到这里的你很兴奋呢?!StartMaskKey
加了一个参数,是个函数指针,这是我们非常熟悉的回调函数的使用方法。DLL
中的StartMaskKey
函数收到这个函数指针后保存在了g_lpfnKeyboardProc
变量中,然后在LowLevelKeyboardProc
中一旦发现了要拦截的按键,就会通过函数指针调用回调函数,将控制权完全交回给DLL
的调用程序,由回调函数KeyboardProc
进一步处理(播放一小段音乐,还是执行个什么有意思的程序,亦或是重启关机什么的。呃,随你便了。:D
),action
参数用来标识键盘消息(按下或弹起),pKeyStruct
参数包含了丰富的按键信息,其实就是系统传给LowLevelKeyboardProc
的lParam
,我又把它原封不动地传给了KeyboardProc
,呵呵。最重要的就是回调函数的返回值了,它就像阀门的开关一样,将决定这个按键消息的命运。从DLL
中的LowLevelKeyboardProc
函数的流程可以看出,如果回调函数KeyboardProc
的返回值为true
则表示把该按键消息继续传递给系统中的下一个钩子;如果为false
则表示结束该按键消息的传递,此时将会起到拦截按键的效果。
用VB
的人可能有些不耐烦了,别着急,上篇文章在最后给出了VB
调用的例程,此篇当然不能缺少这部分了。下面是在VB
中调用的例子:(在窗体上添加2
个CommandButton
,并分别改名为cmdStartMask
和cmdStopMask
)
Option Explicit
Private Declare Function
StartMaskKey Lib
"MaskKey" (lpdwVirtualKey As Long
, ByVal
nLength As
Long
, ByVal
lpfnKeyboarProc As Long
, Optional ByVal
bDisableKeyboard As Boolean
= False
) As Long
Private Declare Function
StopMaskKey Lib "MaskKey" () As Long
Private Sub
cmdStartMask_Click()
'
屏蔽 A, B, C,
上,
下,
左,
右及两个win
键
Dim
key(8) As Long
key(0) = vbKeyA
key(1) = vbKeyB
key(2) = vbKeyC
key(3) = vbKeyLeft
key(4) = vbKeyRight
key(5) = vbKeyUp
key(6) = vbKeyDown
key(7) = &H5B
'
左边的win
键
key(8) = &H5C
'
右边的win
键
StartMaskKey key(0), UBound
(key) + 1, AddressOf
KeyboardProc
End Sub
Private Sub
cmdStopMask_Click()
StopMaskKey
End Sub
窗体模块的代码和以前的例程几乎一样,只是在调用StartMaskKey
函数时加了一个参数:AddressOf
KeyboardProc
。在VB
中用过回调函数的人对这东西绝不会陌生,AddressOf
是一个一元运算符,后面接一个函数名,它的功能就是获得指定函数的指针。但有一点必须注意,该回调函数(此例中为KeyboardProc
)必须写在VB
的标准模块中,标准模块的代码如下:
Option Explicit
Private Const
WM_KEYDOWN = &H100
Private Const
WM_KEYUP = &H101
Private Const
WM_SYSKEYDOWN = &H104
Private Const
WM_SYSKEYUP = &H105
Public Type
KBDLLHOOKSTRUCT
vkCode As Long
'
虚拟按键码(1--254)
scanCode As Long
'
硬件按键扫描码
flags As Long
' flags
time As Long
'
消息时间戳
dwExtraInfo As Long
'
额外信息
End Type
Public Enum
KEYACTION
ACTION_KEYDOWN = WM_KEYDOWN
ACTION_KEYUP = WM_KEYUP
ACTION_SYSKEYDOWN = WM_SYSKEYDOWN
ACTION_SYSKEYUP = WM_SYSKEYUP
End Enum
'
全局键盘钩子回调函数
'
参数: action
标识键盘消息(
按下,
弹起), keyStruct
包含按键信息
Public Function
KeyboardProc(ByVal
action As KEYACTION
, keyStruct As KBDLLHOOKSTRUCT
) As Boolean
Select Case
action
Case
ACTION_KEYDOWN
Debug.Print
keyStruct.vkCode, "
按下键盘按键"
Case
ACTION_KEYUP
Debug.Print
keyStruct.vkCode, "
弹起键盘按键"
Case
ACTION_SYSKEYDOWN
Case
ACTION_SYSKEYUP
End Select
'
返回 True
表示继续传递按键消息
'
返回 False
表示结束按键消息传递
KeyboardProc = False
End Function
和VC
版的调用例程差不多,只是把语法翻译成了VB
的,这个VB
标准模块中的KeyboardProc
有没有点MFC
消息映射函数的味道呢?!
:D
需要注意的是,VB
的回调函数必须写在标准模块中。细心的人还可能会发现,我对action
参数作了一点小手脚,改成了一个枚举类型,这主要是为了易于理解。
OK
,要写的就这么多了,关于全局键盘钩子的内容我也想告一段落了。利用VC
编写的DLL
,VB
也可以方便地实现全局键盘钩子了。当然,这不仅仅局限于键盘钩子,利用这种方法可以实现任何类型的钩子。
消息钩子函数(二)
最新推荐文章于 2023-10-29 18:25:49 发布