Ive been struggling for a while with a basic UI thing. I have a parent window and several child windows. With child windows such as button (BS_CHECKBOX style) and edit Im not able to process any message for pressing the ESC key event. I could subclass child windows but it seems to be an overkill just to handle one event. I also have a listview child and for some reason I can handle the VK_ESCAPE correctly. I also checked spy++ and noticed there are NO messages sent to the parent window when ESC key is pressed (and the child is focused). If I set spy++ to log child messages only, the correct messages are generated for the key press - they are just not passed to the parent. Any ideas what (not) to do?
Main window loop:
MSG Msg;
while (GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage (&Msg);
DispatchMessage (&Msg);
}
Working code in parent's WndProc for handling listview key press:
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code)
{
case LVN_KEYDOWN:
if (((LPNMLVKEYDOWN)lParam)->wVKey == VK_ESCAPE)
Exit();
break;
}
break;
Thanks,
Kra
One way to do this is to catch it in your message loop before it gets dispatched to the focus window, e.g.:
MSG Msg;
while (GetMessage(&Msg, NULL, 0, 0))
{
if (Msg.message == WM_KEYDOWN && Msg.wParam == VK_ESCAPE)
{
// process escape key
}
else
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
There are other ways to do it of course, but this is a very simple solution.
Related
Could someone explain why I never receive a WM_COMMAND message in my GetMessage loop?
I've checked and the WndProc is receiving the WM_COMMAND message, so i'm very confused why this doesn't work.
while (GetMessage(&msg, NULL, 0, 0) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_COMMAND)
{
// This never happens:
MessageBox(NULL, "WM_COMMAND", "WM_COMMAND", MB_OK);
}
}
OS: Windows 8
Because WM_COMMAND is sent with SendMessage(). Which directly calls the window procedure. Only messages posted with PostMessage() get added to the queue and are retrieved by GetMessage().
You can see this back in a utility like Spy++, it annotates messages that are posted with P and messages that are sent with S.
I wonder whether there's a difference between those two snippets:
One:
void main()
{
// ...
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
ExitProcess(0);
}
// ...
void quit()
{
PostQuitMessage(0);
}
Two:
bool quit = false;
void main()
{
// ...
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if(quit)
{
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&_msg);
}
ExitProcess(0);
}
}
// Shouldn't get here
ExitProcess(1);
}
// ...
void quit()
{
quit = true;
PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0);
}
Sorry but I couldn't come up with shorter snippets.
The point of my question is whether calling PostQuitMessage and processing all messages with GetMessage is equivalent to processing all messages with PeekMessage, until it returns FALSE.
According to Raymond, WM_QUIT is "generated only when the message queue is otherwise empty", so it looks like the two methods should do the same, but perhaps there's a subtle difference.
Raymond's blog post says:
Because the system tries not to inject a WM_QUIT message at a "bad
time"; instead it waits for things to "settle down" before generating
the WM_QUIT message, thereby reducing the chances that the program
might be in the middle of a multi-step procedure triggered by a
sequence of posted messages.
So in theory no, there is no difference, because the system won't generate WM_QUIT until the queue is empty. However Raymond doesn't say that it's guaranteed that messages won't arrive after WM_QUIT is generated, only that the system tries to avoid it.
Therefore it's conceivably possible that another thread could post you a message after you've exited your main GetMessage loop, and depending on your application this may be something you have to deal with. For example, if you post messages internally with memory allocations that the receiving thread is expected to free, you may need a separate PeekMessage loop to clean them up before the thread exits completely.
In practice however no one ever writes message loops like your second example.
I don't think the accepted answer addresses the real issue.
Raymond answers this question directly here.
Basically:
You do not have control over every message loop, so you don't have that option.
Oh, and by the way, don't forget to PostQuitMessage inside your own message loops.
Note that even WTL neglects to do this!
It relies on you passing the return value of CMessageLoop::Run to PostQuitMessage.
Working with C and Win32, I have a problem where my program freezes instead of closing when a quit message is posted(Alt-F4 for example), and I have to end the process with task manager.
I have this in my main loop:(problem solved)
MSG msg;
while(1)
{
while(PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
{
terminate = 1;
while(terminate != 3) //each thread increments "terminate" by 1 before returning
{
Sleep(1);
}
return 0;
}
DispatchMessage(&msg);
}
Sleep(1);
}
It will print "OK!" in the console and then freeze.
I think this could be because I have multiple threads and they are not terminated properly (but I read that if I return from my main() function the other threads should be killed automatically). If it helps one of those threads is an OpenGL rendering thread.
The main function is only just a thread, you are terminating just that one. However, for a process to end, all threads need to be properly terminated or it will run forever. You'll need to keep a reference the threads and terminate them once you receive the WM_QUIT message.
Exit is to exit from the entire process. Your process will clean up when you call exit, e.g. Call the function registered with atexit, or call destructor of global object in case of c++. What about abort(), or terminateProcess.
If I have the following code below, how do I detect when the window has been closed, so I can quit? r never seems to get the value -1 0, and I need to process messages for the entire thread, not just the current window.
HWND hWnd = CreateWindowExW(0, L"Edit", L"My Window", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, NULL, NULL, NULL, NULL);
ShowWindow(hWnd, SW_SHOWDEFAULT);
MSG msg;
BOOL r;
while ((r = GetMessageW(&msg, NULL, 0, 0)) != 0)
{
if (r == -1) { break; }
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
Waiting for r = -1 is not the way you detect that your window has closed. A return value of -1 is not a normal condition: it indicates that an error has occurred in the message loop.
From the documentation:
Return Value
Type: BOOL
If the function retrieves a message other than WM_QUIT, the return value is nonzero.
If the function retrieves the WM_QUIT message, the return value is zero.
If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer. To get extended error information, call GetLastError.
When GetMessage retrieves a WM_QUIT message from the queue, it will return a value of 0, and you should end the loop.
If you just want to know when the window has closed, you probably want to handle either the WM_CLOSE or WM_DESTROY messages. For a discussion of these messages, see the answers to this question: What is the difference between WM_QUIT, WM_CLOSE, and WM_DESTROY in a windows program?
I found a solution for this: WM_NULL.
The message loop can handle the matter on its own independently of WndProc:
// written in C#
MSG msg = new MSG();
while (GetMessage(out msg, window, 0, 0))
{
if ((msg.message == WM_NULL) && !IsWindow(window))
break;
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
Console.WriteLine("yeah, out of loop ^^");
From my observation: When window is destroyed GetMessage retrieves WM_NULL messages without pause (1st hint) and IsWindow can check the window (affirmation).
I've tried using the "grab all of the process IDs enumerated by the desktop" method, however that doesn't work.
Is there a way to convert a handle to a window handle? -or-
Is there a way to take a process ID and find out all of the child windows spawned by the process?
I don't want to use FindWindow due to multiple process issues.
You could call EnumWindows() to iterate over all the top-level windows on the screen, then use GetWindowThreadProcessId() to find out which ones belong to your process.
For example, something like:
BOOL CALLBACK ForEachTopLevelWindow(HWND hwnd, LPARAM lp)
{
DWORD processId;
GetWindowThreadProcessId(hwnd, &processId);
if (processId == (DWORD) lp) {
// `hwnd` belongs to the target process.
}
return TRUE;
}
VOID LookupProcessWindows(DWORD processId)
{
EnumWindows(ForEachTopLevelWindow, (LPARAM) processId);
}