-2

I am trying to understand how to init a LowLevelKeyboardProc hook.

In my minimal approach for a key logger as shown below, why do I need the message-loop to get the hook working even if it just hangs within the first loop and never prints "MSG\r\n" ?

And how could I ever Unhook if there is no chance to recheck the while-condition?

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

LRESULT CALLBACK LLKBProc(int nCode, WPARAM wParam, LPARAM lParam) {
    KBDLLHOOKSTRUCT *keyMetaP = (KBDLLHOOKSTRUCT *) lParam;
    printf("\nkey = %x", keyMetaP->vkCode);
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}
int main() {
    // get handle to process
    HINSTANCE hExe = GetModuleHandle(NULL);

    // set hook
    HHOOK keyHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC) LLKBProc, hExe, 0);

    // msg loop
    MSG msg;
    while ( GetMessage(&msg, 0, 0, 0) != -1 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        printf("MSG\r\n");
    }
    UnhookWindowsHookEx(keyHook);

    return 1;
}
hurm
  • 11
  • 2
  • Where does this behaviour of putting a LF before a print string come from, (rather than CRLF after it)? – Martin James Jan 05 '18 at 22:38
  • Updated. Any help? – hurm Jan 05 '18 at 22:52
  • `And how could I ever Unhook` - but based on what event you want unhook ? for this need handle this event and have more complex logic – RbMm Jan 05 '18 at 23:01
  • 1
    and really simply read msdn - *This hook is called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop.* – RbMm Jan 05 '18 at 23:05
  • @RbMm: Thanks for the answer! How exactly is the control transferred to the system? According to the msdn page it only waits for a msg in the threads queue and then forwards it. I want to Unhook based on a variable, set by another thread for example (sth. like "bool logging = false") – hurm Jan 05 '18 at 23:11
  • so simply send message via `PostThreadMessage` to your thread – RbMm Jan 05 '18 at 23:12
  • Ok. So that means the calling thread only needs to init the message-loop, but it is never run through it ? I understood the msdn page more like the calling thread will wait for the msg's and then forward it to the callback-proc but assuming this, the loop must be run through doesn't it? – hurm Jan 05 '18 at 23:18
  • @RbMm 'PostThreadMessage' thanks for that hint! – hurm Jan 05 '18 at 23:20
  • Pay attention as to whether the verb is "send" or "post". Messages that are sent get automatically dispatched when you call `GetMessage` or `PeekMessage`. These two functions only return a message if it's posted to the thread's queue. In this case the message is sent and Windows automatically calls your hook when `GetMessage` is called. If this is all you're doing, you don't need `TranslateMessage` and `DispatchMessage`. Also, you should be checking whether the return value is 0 in case a `WM_QUIT` was posted. – Eryk Sun Jan 07 '18 at 16:25
  • @eryksun: So that means in this case the message is not posted to the threads message queue but just forwarded to the hook? Why then is checked if the message-loop is present or not? – hurm Jan 08 '18 at 00:03
  • @hurm, sent messages are processed during `GetMessage` and `PeekMessage` calls (i.e. `NtUserGetMessage` and `NtUserPeekMessage` system calls). Once a message is posted (e.g. `WM_QUIT`), the system call returns. The thread usually loops until `WM_QUIT` is posted (i.e. `GetMessage` returns 0). The loop typically handles some messages and dispatches the rest to window procedures or timer procedures. In this case you don't need to translate and dispatch posted messages, since you have no windows or timers. – Eryk Sun Jan 08 '18 at 03:14
  • @eryksun, I got that point thank you for clarifying. But even tough there is nothing to dispatch the function will run through the loop won't it? So why isn't the `printf("MSG\r\n")`executed? – hurm Jan 08 '18 at 18:59
  • @hurm, when you call `GetMessage`, the system does not return until a posted message is available in the thread's message queue. However, the call isn't doing nothing while waiting. The thread responds to *sent* messages by automatically dispatching them to window procedures and hook procedures. When a message is posted, `GetMessage` returns, and then the code in *your* loop can decide how to handle it. It may call `TranslateMessage` and `DispatchMessage` to window procedures and timer procedures, and it may loop to call `GetMessage` again, or call `printf("MSG\r\n")` if it wants. – Eryk Sun Jan 08 '18 at 20:58
  • @eryksun, thanks again. So as long the messages are addressed to windows and hooks, `GetMessage` automatically dispatches them **without** returning and thats why in case of a message addressed to a _hook_ it will not return and therefor not pass the _cals_ within the while-loop? Do you have any hints for sites where I can read about? Google didn't gave me anything... – hurm Jan 09 '18 at 00:24
  • @hurm, have you read [About Messages and Message Queues](https://msdn.microsoft.com/en-us/library/ms644927)? I consider that mandatory reading for programming in Windows. Most of the API is about 30 years old. There's no shortage of books, online classes, and tutorials that teach Windows programming in C++. – Eryk Sun Jan 09 '18 at 07:18

2 Answers2

0

The message loop is present so that the program doesn't simply die. Right now it doesn't have any termination condition, but without some kind of loop in the main thread the program would simply proceed to the UnhookWindowsHookEx call and then terminate.

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23
  • no, if we call `Sleep(INFINITE)` for example ? hook callback will be not called. `GetMessage` need for another reason – RbMm Jan 05 '18 at 22:58
0

From MSDN (WH_KEYBOARD_LL):

This hook is called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop.

To abort the message loop from a different thread you can use PostThreadMessage to post WM_NULL or WM_QUIT, or use MsgWaitForMultipleObjects instead of GetMessage if you want to use a event as the stop signal.

You can probably just use PostQuitMessage if you want to abort from inside the hook.

Anders
  • 97,548
  • 12
  • 110
  • 164