6

I am attempting to create a global keyboard hook that will be used to read keystrokes for a hotkey program I am creating and I've run into a few problems. First is that whenever I activate my hook the keyboard becomes sluggish. There is almost a full second delay between each character that comes on the screen. The second issue is that any code I write in the DLL callback function never gets executed.

Here is my code:

DLL cpp file

#include<Windows.h>
#include "keyDLL.h"

using namespace std;

LRESULT CALLBACK HookProcedure(int code, WPARAM wParam, LPARAM lParam)
{
    return CallNextHookEx(0, code, wParam, lParam);
}

DLL header file

#include "windows.h"

extern "C" __declspec(dllexport) LRESULT CALLBACK HookProcedure(int code, WPARAM wParam, LPARAM lParam);

hook CPP file

#include<Windows.h>
#include<iostream>
using namespace std;

HHOOK WINAPI SetWindowsHookEx(_In_  int idHook, _In_  HOOKPROC lpfn, _In_  HINSTANCE hMod, _In_  DWORD dwThreadId
static HINSTANCE dllHandle;
HOOKPROC address;
static HHOOK keyboard_hook;

int main()
{
    dllHandle = LoadLibrary(TEXT("keyDLL.dll"));
    address = (HOOKPROC)GetProcAddress(dllHandle, "HookProcedure");
    cout << address << endl << dllHandle << endl;
    keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, address, dllHandle, 0);

    system("pause");
}

This version of the code is just a super simple cut down version that only sets up the keyboard hook. I took out all other code in my attempts to find out what was causing the issues. From my understanding of hooks (which isn't all that much apparently) this code should hook the keyboard but do nothing with the keystrokes. I'm not understanding why it slows my keyboard down so much. I used dumpbin to ensure there was no name mangling on the DLL export functions. I am running windows 8 64 bit and both my DLL and my exe files are compiled as 64 bit. When I cout the address and dllHandle variables I get memory addresses so I know at least part of the code is working. Any help would be appreciated, thanks.

Joe
  • 41,484
  • 20
  • 104
  • 125
Phil Gibson
  • 96
  • 1
  • 1
  • 11
  • 3
    A WH_KEYBOARD_LL hook requires a message loop, not a DLL. It is "sluggish" because Windows cannot call the message hook function if your program does not call GetMessage(). – Hans Passant Apr 19 '15 at 18:54
  • 3
    possible duplicate of [C++ Console app, SetWindowsHookEx, Callback is never called](http://stackoverflow.com/questions/6974702/c-console-app-setwindowshookex-callback-is-never-called) – Raymond Chen Apr 19 '15 at 19:00
  • @ Hans Passant So will it work better if instead of WH_KEYBOARD_LL I just use WH_KEYBOARD? Or will that not work for a global hook? I'm not entirely clear on all the differences between the two – Phil Gibson Apr 19 '15 at 19:48
  • Low level hook needs a message loop. Make one. – David Heffernan Apr 19 '15 at 21:31
  • 3
    You should switch to the [Raw Input API](https://msdn.microsoft.com/en-us/library/windows/desktop/ms645536.aspx) instead of using `WH_KEYBOARD_LL`. It is safer and more efficient. Even the [`LowLevelKeyboardProc` documentation](https://msdn.microsoft.com/en-us/library/windows/desktop/ms644985.aspx) says so: "*In most cases where the application needs to use low level hooks, it should monitor raw input instead. This is because raw input can asynchronously monitor mouse and keyboard messages that are targeted for other threads more effectively than low level hooks can.*". – Remy Lebeau Apr 20 '15 at 19:53

1 Answers1

11

Another approach would be this code below:

#define _WIN32_WINNT 0x0400
#pragma comment( lib, "user32.lib" )

#include <iostream>
#include <windows.h>
#include <stdio.h>

HHOOK hKeyboardHook;

__declspec(dllexport) LRESULT CALLBACK KeyboardEvent (int nCode, WPARAM wParam, LPARAM lParam)
{
    DWORD SHIFT_key=0;
    DWORD CTRL_key=0;
    DWORD ALT_key=0;


    if  ((nCode == HC_ACTION) &&   ((wParam == WM_SYSKEYDOWN) ||  (wParam == WM_KEYDOWN)))      
    {
        KBDLLHOOKSTRUCT hooked_key =    *((KBDLLHOOKSTRUCT*)lParam);
        DWORD dwMsg = 1;
        dwMsg += hooked_key.scanCode << 16;
        dwMsg += hooked_key.flags << 24;
        char lpszKeyName[1024] = {0};

        int i = GetKeyNameText(dwMsg,   (lpszKeyName+1),0xFF) + 1;

        int key = hooked_key.vkCode;

        SHIFT_key = GetAsyncKeyState(VK_SHIFT);
        CTRL_key = GetAsyncKeyState(VK_CONTROL);
        ALT_key = GetAsyncKeyState(VK_MENU);

            printf("Keycode = %c\n",key);

        if (key >= 'A' && key <= 'Z')   
        {

            if  (GetAsyncKeyState(VK_SHIFT)>= 0) key +=32;

            /*********************************************
            ***   Hotkey scope                         ***
            ***   do stuff here                        ***
            **********************************************/

            if (CTRL_key !=0 && key == 'y' )
            {
               MessageBox(NULL, "CTRL-y was pressed\nLaunch your app here", "H O T K E Y", MB_OK); 
               CTRL_key=0;

               //do stuff here




            }

            if (CTRL_key !=0 && key == 'q' )
            {
                MessageBox(NULL, "Shutting down", "H O T K E Y", MB_OK); 
               PostQuitMessage(0);
            }




            SHIFT_key = 0;
            CTRL_key = 0;
            ALT_key = 0;

        }

    }
    return CallNextHookEx(hKeyboardHook,    nCode,wParam,lParam);
}

void MessageLoop()
{
    MSG message;
    while (GetMessage(&message,NULL,0,0)) 
    {
        TranslateMessage( &message );
        DispatchMessage( &message );
    }
}

DWORD WINAPI my_HotKey(LPVOID lpParm)
{
    HINSTANCE hInstance = GetModuleHandle(NULL);
    if (!hInstance) hInstance = LoadLibrary((LPCSTR) lpParm); 
    if (!hInstance) return 1;

    hKeyboardHook = SetWindowsHookEx (  WH_KEYBOARD_LL, (HOOKPROC) KeyboardEvent,   hInstance,  NULL    );
    MessageLoop();
    UnhookWindowsHookEx(hKeyboardHook);
    return 0;
}

int main(int argc, char** argv)
{
    HANDLE hThread;
    DWORD dwThread;

    printf("CTRL-y  for  H O T K E Y  \n");
    printf("CTRL-q  to quit  \n");

    hThread = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)   my_HotKey, (LPVOID) argv[0], NULL, &dwThread);

       /* uncomment to hide console window */
    //ShowWindow(FindWindowA("ConsoleWindowClass", NULL), false);

    if (hThread) return WaitForSingleObject(hThread,INFINITE);
    else return 1;

}
Software_Designer
  • 8,490
  • 3
  • 24
  • 28