在测试过程中,发现一个关于键盘钩子和模拟键盘敲击的问题。测试过程如下,
测试程序启动时,安装一个全局的键盘钩子,然后模拟键盘敲击。
分别模拟敲击空格键和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也不清楚原因。
敬请各位兄弟姐妹不吝赐教

解决方案 »

  1.   

    你用低级键盘消息钩子。
    SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, glhInstance, 0);
      

  2.   

    用低级键盘钩子和用键盘钩子的现象差不多,
    点击一下button1,DebugView会收到2条消息。补充:点击button2, DebugView会不断的收到消息,此时,在button2的任意位置点击一下,DebugView就不会再收到消息了。总结:安装键盘钩子后,用keybd_event模拟按下空格键,会造成钩子函数不断的被调用。
      

  3.   

    下面这段代码是截获按键的钩子,出现的一个问题和楼主反应的差不多。正常按键没有问题,如果安装设定的键不放(大约超过0.5秒)就会出现钩子不听向外发送消息的情况,而且不停止的,很快就会出现CPU 95%以上。
    忘高手顺便看看。//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;
    }
      

  4.   

    你用热键不就好了RegisterHotKey