Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GetMessageW is blocking the calling thread, receiving no messages

I've been struggling with this for a day now and I can't figure out what is wrong with my code. I'm coding in Rust but this is more of a Windows' api related problem.

// first, I'm installing a keyboard hook for the current thread
let hook = SetWindowsHookExW(
    WH_KEYBOARD_LL,
    Some(low_level_keyboard_proc), // just forwards the call with CallNextHookEx
    ptr::null_mut(),
    0,
);

assert!(!hook.is_null(), "Failed to install the hook");

let mut message: MSG = mem::zeroed();
GetMessageW(&mut message, ptr::null_mut(), 0, 0);

// The GetMessageW function is known to block the calling thread until a new message is sent.
// The thing is: my `low_level_keyboard_proc` handle *does get* called, so I know events are being received.
// I don't understand why the GetMessageW function never returns even though events are being processed.
// Note that my handler does not get called when I remove the GetMessageW function.

println!("Unreachable code...");

UnhookWindowsHook(hook);

I tried to use the PeekMessageW function instead but the problem is the same: the function always return FALSE (no events received) even though the handler is getting properly called.

If I remove the SetWindowsHookExW part, GetMessageW is still blocking the thread BUT if I remove the GetMessageW part and put an infinite loop it its place, the handler does not get called anymore.

... so here is the question: why does the GetMessageW function never return? And if this behaviour is normal, how am I supposed to use the message that I provide to GetMessageW.

I'm assuming I don't understand well the relationship between GetMessageW and SetWindowsHookExW.

EDIT: I understand that I can't catch the messages sent to the keyboard hook I created. Now, what would the "right" way to retrieve keyboard messages look like? Because it would be real handy to be able to get those messages directly from the message loop instead of having to send them back from the callback function to my main code using static structures.

I'm trying to create an event loop that can be used regardless of a context or the focus of a window. The idea is retrieving those messages directly from a message loop and dispatch them using a user-defined custom handler that can be used through safe rust code.

like image 689
Gymore Avatar asked May 11 '26 00:05

Gymore


1 Answers

There are no window messages or thread messages being posted to the message queue of the thread that is installing the keyboard hook, so there are no messages for GetMessageW() to return TO YOU.

However, SetWindowsHookEx() uses its own messages internally when a low-level keyboard hook crosses thread/process boundaries. That is why you don't need to implement your hook in a DLL when hooking other applications. When a keyboard action occurs, a private message is sent TO THE SYSTEM targeting the thread that installed the hook.

That is why the installing thread needs a message loop. The simple act of performing message retrieval in your code is enough to get those internal messages dispatched properly, which is why your callback function is being called. You just won't see those private messages, which is why GetMessageW() blocks your code.

The same thing happens when you SendMessage() to a window across thread boundaries. The receiving thread needs a message loop in order for the message to be dispatched to the target window, even though the message doesn't go through the receiving thread's message queue. This is described in the SendMessage() documentation:

If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code.

So, what happens with SetWindowsHookEx() is that it creates a hidden window for itself to receive its private messages, sent via SendMessage(), when keyboard activity is detected in a different thread/process and needs to be marshaled back to your installing thread. This is described in the LowLevelKeyboardProc documentation:

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.

like image 119
Remy Lebeau Avatar answered May 13 '26 15:05

Remy Lebeau



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!