在测试过程中,发现一个关于键盘钩子和模拟键盘敲击的问题。测试过程如下,
测试程序启动时,安装一个全局的键盘钩子,然后模拟键盘敲击。
分别模拟敲击空格键和F3。测试平台Windows XP SP2
IDE VS2005+SP1测试解决方案由两个工程组成
KBHook和TestHook
KBHook是一个Windows DLL工程。
导出两个函数,
BOOL InstallHooK();
void UninstallHook();
KBHook的代码如下
static HHOOK s_hHook=NULL;
static HINSTANCE s_hInstance=NULL;BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
s_hInstance=hModule;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}static LRESULT CALLBACK KeyboardHooKProc(int code,
WPARAM wParam,
LPARAM lParam
)
{
OutputDebugStringW(L"HOOK");
return CallNextHookEx(s_hHook,code,wParam,lParam);
}BOOL InstallHooK()
{
s_hHook=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardHooKProc,s_hInstance,0);
if(s_hHook)
{
OutputDebugStringW(L"InstallHooK OK");
}
else
{
OutputDebugStringW(L"InstallHook failed");
}
return s_hHook!=NULL?TRUE:FALSE;
}void UninstallHook()
{
UnhookWindowsHookEx(s_hHook);
}TestHook是一个MFC Dialog工程,
在InitInstance()开始处调用InstallHooK()
在ExitInstance()中调用UninstallHook()Dialog上有两个按钮,button1和button2,两个按钮的消息处理函数都是模拟键盘敲击。
两个消息处理函数如下
void CTestHookDlg::OnBnClickedButton1()
{
keybd_event(VK_F3,0,0,0);
keybd_event(VK_F3,0,KEYEVENTF_KEYUP,0);
}
void CTestHookDlg::OnBnClickedButton2()
{
keybd_event(VK_SPACE,0,0,0);
keybd_event(VK_SPACE,0,KEYEVENTF_KEYUP,0);
}点击button1模拟点击F3,点击button2模拟点击空格键。用DebugView接收钩子函数的输出。现象如下,
点击button1一次,DebugView输出四条信息。
每条消息的内容都是HOOK
点击button2一次,在DebugView中看到是的,DebugView不停的收到HOOK。如果把OnBnClickedButton2中的VK_SPACE换成其他的Virtual Key Code,点击Button2一次,DebugView收到四条消息查看MSDN也不清楚原因。
敬请各位兄弟姐妹不吝赐教
测试程序启动时,安装一个全局的键盘钩子,然后模拟键盘敲击。
分别模拟敲击空格键和F3。测试平台Windows XP SP2
IDE VS2005+SP1测试解决方案由两个工程组成
KBHook和TestHook
KBHook是一个Windows DLL工程。
导出两个函数,
BOOL InstallHooK();
void UninstallHook();
KBHook的代码如下
static HHOOK s_hHook=NULL;
static HINSTANCE s_hInstance=NULL;BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
s_hInstance=hModule;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}static LRESULT CALLBACK KeyboardHooKProc(int code,
WPARAM wParam,
LPARAM lParam
)
{
OutputDebugStringW(L"HOOK");
return CallNextHookEx(s_hHook,code,wParam,lParam);
}BOOL InstallHooK()
{
s_hHook=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardHooKProc,s_hInstance,0);
if(s_hHook)
{
OutputDebugStringW(L"InstallHooK OK");
}
else
{
OutputDebugStringW(L"InstallHook failed");
}
return s_hHook!=NULL?TRUE:FALSE;
}void UninstallHook()
{
UnhookWindowsHookEx(s_hHook);
}TestHook是一个MFC Dialog工程,
在InitInstance()开始处调用InstallHooK()
在ExitInstance()中调用UninstallHook()Dialog上有两个按钮,button1和button2,两个按钮的消息处理函数都是模拟键盘敲击。
两个消息处理函数如下
void CTestHookDlg::OnBnClickedButton1()
{
keybd_event(VK_F3,0,0,0);
keybd_event(VK_F3,0,KEYEVENTF_KEYUP,0);
}
void CTestHookDlg::OnBnClickedButton2()
{
keybd_event(VK_SPACE,0,0,0);
keybd_event(VK_SPACE,0,KEYEVENTF_KEYUP,0);
}点击button1模拟点击F3,点击button2模拟点击空格键。用DebugView接收钩子函数的输出。现象如下,
点击button1一次,DebugView输出四条信息。
每条消息的内容都是HOOK
点击button2一次,在DebugView中看到是的,DebugView不停的收到HOOK。如果把OnBnClickedButton2中的VK_SPACE换成其他的Virtual Key Code,点击Button2一次,DebugView收到四条消息查看MSDN也不清楚原因。
敬请各位兄弟姐妹不吝赐教
SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, glhInstance, 0);
点击一下button1,DebugView会收到2条消息。补充:点击button2, DebugView会不断的收到消息,此时,在button2的任意位置点击一下,DebugView就不会再收到消息了。总结:安装键盘钩子后,用keybd_event模拟按下空格键,会造成钩子函数不断的被调用。
忘高手顺便看看。//ActiveKey.cpp:DefinestheinitializationroutinesfortheDLL.
//
// 所有的全局变量都放在一个共享段中,定义如下:
//
// #pragma data_seg("shareddata")
// HHOOK hHook =NULL; //钩子句柄
// #pragma data_seg()
//
// 关于共享段,有几点重要的说明:
// 必须在链接选项里指定该段为共享,project->settings->link->object/library,加上/section:shareddata,rws
// 第二种指定共享段的方法:在def文件的sections里加上一句shareddata read write shared
// 第三种指定共享段的方法:在程序里加上一句#pragma comment(linker,"section:shareddata,rws")
// 所有的变量必须初始化,否则链接程序会把它放到普通数据段
// 如果不初始化变量,需要在段外用“__declspec(allocate("shareddata")) 变量类型 变量名”的方式定义#include "stdafx.h"
#include <afxdllx.h>
#include "ActiveKey.h"
#include "LogFile.h"#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#endif//Shareddatasection
#pragma data_seg("sharedata")
HHOOK glhHook=NULL;//钩子句柄。
HINSTANCE glhInstance=NULL;//DLL实例句柄。
HWND ghWnd = NULL;
CSystemFile IniFile;
static char g_szCDCTitle[1024] = {0}; //客户端标题
int gi_CDCKey = 0; //客户端设置的键值(ASC码)
#pragma data_seg()
static AFX_EXTENSION_MODULE ActiveKeyDLL={NULL,NULL};extern "C" int APIENTRY
DllMain(HINSTANCE hInstance,DWORD dwReason,LPVOID lpReserved)
{
//如果使用lpReserved参数则删除下面这行
UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("NOtePadHOOK.DLL Initializing!\n");
//扩展DLL仅初始化一次
if (!AfxInitExtensionModule(ActiveKeyDLL, hInstance))
return 0;
new CDynLinkLibrary(ActiveKeyDLL);
//把DLL加入动态MFC类库中
glhInstance = hInstance;
//插入保存DLL实例句柄
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("NotePadHOOK.DLL Terminating!\n");
//终止这个链接库前调用它
AfxTermExtensionModule(ActiveKeyDLL);
}
return 1;
}
extern "C" LRESULT
WINAPI KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
if(nCode>=0 && lParam < 0) //按键抬起发送消息
{
CString strValue;
strValue.Format("%s",g_szCDCTitle);
if (ghWnd == NULL)
{
ghWnd = FindWindow(NULL,strValue);
}
if (ghWnd != NULL)
{
//一次按键,分按下和弹起,这里指处理弹起
if ((int)wParam == gi_CDCKey)
{
::PostMessage(ghWnd,WM_USER+77,wParam,0);
}
}
}
return CallNextHookEx(glhHook,nCode,wParam,lParam);
}CKeyHook::CKeyHook()
{
}
CKeyHook::~CKeyHook()
{
if(glhHook)
Stop();
}HHOOK CKeyHook::Start() //设置键盘钩子
{
//钩子程序初始化时就要把全局变量赋值好
IniFile.Init();
CString strValue;
IniFile.ReadKeyValue("LWindowName","",strValue);
strValue.TrimLeft();
strValue.TrimRight();
strncpy(g_szCDCTitle,(LPCTSTR)strValue,sizeof(g_szCDCTitle));
IniFile.ReadKeyValue("CTRLKEY","",strValue);
gi_CDCKey = atoi(strValue); glhHook=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,glhInstance,0);
return glhHook;
}BOOL CKeyHook::SetOption(HWND hander) //设置键盘钩子
{
ghWnd = hander;
return 0;
}BOOL CKeyHook::Stop()
{
BOOL bResult=TRUE; if(glhHook)
bResult=UnhookWindowsHookEx(glhHook);//卸载键盘钩子 return bResult;
}