I have a UI thread and another thread, I want to send a user-defined message using PostMessage() from the other trhead to the UI thread.
If the UI thread displays a message box, and then I send the user-defined message, will the message loop of the message box retrieve the user-defined message from the UI thread message queue, and hence the user-defined message will be lost?!
PostMessage() is not a problem. MessageBox() runs its own message loop to make the message box modal, but makes a call to DispatchMessage() and that function makes sure that the correct window procedure gets called for any message that was posted/sent to any window.
You could only make this mistake if you were calling PostThreadMessage() instead. That is never safe if the receiving thread displays dialogs or enters modal loops. Raymond Chen wrote a couple of blog articles about that:
Thread messages are eaten by modal loops
Why do messages posted by PostThreadMessage disappear?
A possible corner-case is posting a message that requires the message loop to process the message instead of the window procedure. Like a shortcut keystroke.
Related
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.
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.
While handling WM_TIMER, I called MessageBox. As a result, a message box popped up with the frequency of the timer. So I believe that the application was trying to continue to process queued/non-queued messages even during MessageBox.
Am I right?
I know that according to MSDN, while an application is sending a message to a different thread from the sending thread, the sending thread will try to process non-queued messages it receives before SendMessage returns --- i.e. before the target thread replies.
Are there any other functions that could try to continue to process queued/non-queued messages before they return, besides MessageBox and SendMessage? I need to know about that to determine whether any functions called in the Window procedure should be reentrant.
Another two relevant questions are
1) Does DispatchMessage not return until the window procedure has returned?
2) Will GetMessage not be called again if the current DispatchMessage hasn't returned yet?
A modal dialog runs its own message loop internally, using the calling thread's message queue. You are calling MessageBox() inside your WM_TIMER handler, so the message loop inside of MessageBox() is receiving and dispatching subsequent WM_TIMER messages while the message box is running.
WM_PAINT messages are not posted to the message queue, but rather when the message queue is empty, the WM_PAINT message is sent to the window procedure (if some area of the window is invalid).
However, are WM_ERASEBKGND messages sent in some similar way, or are they simply posted to the message queue (the documentation don't say anything about this).
It is both, not untypical for Windows messages. It will be sent when the program executes a command like UpdateWindow() or processes a message like WM_SYSCOMMAND that moves or resizes the window. It will be posted when the program has called InvalidateRect().
Same is true for WM_PAINT, normally a "posted" message but only returned by GetMessage() when the message queue is empty. It however will be sent when you call UpdateWindow(), ensuring the window is painted when it returns.
Not taking a dependency on those implementation details is pretty important.
I want to send a message in WinAPI to window, created in other thread and then wait for process this message.
Is this possible? Thanks in advance.
Use SendMessage() to send a message to a window.
SendMessage() blocks the calling thread until the message had been delivered and processed by the target window's message dispatcher.
From SendMessage()'s documentation:
Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
I have a problem with WM_SIZE. I want to capture it using PeekMessage (not WndProc). PeekMessage never receives WM_SIZE, so I post extra user message to window to capture it with PeekMessage like this (code from WnProc) :
case WM_SIZE:
PostMessageW(hwnd, WM_USER + 1, wParam, lParam);
break;
The problem is I receive WM_USER + 1 using PeekMessage only when window is maximized or restored. No message when window is resized by its thick frame (I receive WM_SIZE in WndProc though).
PeekMessage() can only see messages that were posted to the message queue. That excludes WM_SIZE, it is sent, not posted. Or in other words, it is delivered by SendMessage(), it calls the window procedure directly and bypasses the message queue. So yes, your code starts working because you now repost the message with PostMessage, it is put on the message queue so PeekMessage can see it.
Something different happens when the user resizes the window. That's reported by another message: WM_SIZING. It is generated, at a pretty high rate, when Windows starts a modal message loop to implement the resizing operation. It gives due notice of this, you'll get the WM_ENTERSIZEMOVE when the modal loop starts (user clicks a window corner), WM_EXITSIZEMOVE when it is complete (user releases the button). You'll get a bunch of WM_SIZING messages, sent to your window procedure. Not posted. And one WM_SIZE to give the final size. One way to not see these reflected versions of these messages is when you call PeekMessage() in your own message loop. It won't be called when the Windows modal resize loop is active.
Hard to give better advice, it is really unclear why you are doing this. The "doctor, it hurts, don't do it then" medical answer is highly likely to be relevant. I suspect you might want to reflect the WM_SIZING message as well. The largest issue is that by the time you retrieve these messages from the queue, the window size has already changed and the notification is just plain stale. Which is why the message is sent and not posted.
I believe this is applicable:
PeekMessage not getting the message?
You need to pass your class pointer to the last parameter of your call
to CreateWindowEx, then retrieve that pointer from the LPCREATESTRUCT
passed to you in the LPARAM of WM_CREATE, your class pointer will be
in the lpCreateParmas feild of the struct. Set your class pointer to
the GWLP_USERDATA of your window, and on any other message calls ,
call GetWindowsLong , retrieve your class pointer, then pass the
message, wparam, and lparam all off to your internal class message
handler.
http://msdn.microsoft.com/en-us/library/ff381400%28v=VS.85%29.aspx