How to use WM_KEYDOWN properly ? - c++

I defined a few functions to process Input using WM_KEYDOWN / WM_KEYUP, but only WM_KEYUP seems to work properly. When using the function that utilizes WM_KEYDOWN nothing happens. (Debug message isnt displayed)
When initializing an instance of my Input-class inside my Framework-object, I pass it a pointer to the MSG object that my Framwork uses. (Also the ESCAPE argument inside my function is same as VK_ESCAPE)
Here is my code:
//Input.cpp
bool Input::KeyPressed( Keys key )
{
if( InputMsg->message == WM_KEYDOWN )
{
if( InputMsg->wParam == key )
return true;
}
return false;
}
bool Input::KeyReleased( Keys key )
{
if( InputMsg->message == WM_KEYUP )
{
if( InputMsg->wParam == key )
return true;
}
return false;
}
//Framework.cpp
bool Framework::Frame()
{
ProcessInput();
return true;
}
void Framework::ProcessInput()
{
if( m_Input->KeyPressed( ESCAPE ) )
{
OutputDebugString("Escape was pressed!\n");
}
}
Anyone got an idea why only the KeyReleased() function works but the KeyPressed() doesn't ?
Thanks in advance

Ok, looking at your code it seems like the problem is the message loop in your Run() function:
void Framework::Run()
{
while( msg.message != WM_QUIT && m_result)
{
if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
m_result = Frame();
}
}
}
Look at what this is doing. Every loop iteration, the process is:
If a message is in the queue, process it
Otherwise, call the Frame() function
When a message is processed it's dispatched to the window and handled by your WndProc, which actually does very little. Most of your message processing is done in Frame() or the functions called by Frame(), but that function is only called if there isn't a message in the queue.
This explains why you are seeing WM_KEYUP but not WM_KEYDOWN. The two messages generally come together, at least if you press and release the key at normal speed.
The first time through the loop, PeekMessage will retrieve WM_KEYDOWN and dispatch it to your window procedure, which does nothing with it.
The loop then repeats, and retrieves WM_KEYUP. Again this is sent to the window procedure (which does nothing with it), but with no further messages in the queue the next time around your Frame() function is called - which then processes the most recently retrieved message but none of the other messages that came before it.
To fix your code you need to refactor your message handling so that every message is processed, not just the last in any particular batch.

WM_KEYDOWN goes through the TranslateMessage block and would reach your block as WM_CHAR.
So I think you must try WM_CHAR instead.

Related

Win32 Main Message Loop for OpenGL [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
My main message loop in a Win32 OpenGL application looks like this:
// Inside wWinMain
bool bQuit = false;
while( bQuit == false )
{
DWORD dwReturn = ::MsgWaitForMultipleObjects( 0, NULL, FALSE, 12, QS_ALLINPUT );
if( dwReturn == WAIT_OBJECT_0 )
{
MSG msg;
BOOL bReturn = ::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE );
if( bReturn != FALSE )
{
if( msg.message == WM_QUIT )
bQuit = true;
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
}
if( dwReturn == WAIT_OBJECT_0 || dwReturn == WAIT_TIMEOUT )
{
RenderFrame();
::SwapBuffers( hDc );
}
}
It works almost fine, I have only one problem: if I press Alt+F4 to close the window, it does not quit right after I release the key, however, if I hover the mouse over the window, it quits instantly.
A) Why is this? How should I modify my loop?
B) The original code I found did not use MsgWaitForMultipleObjects but called RenderFrame continuously. I think this way too much CPU time is wasted on redrawing the screen. Am I right? What is the usual way, do you spend all your excess capacity on drawing?
your error that you call PeekMessage only once per WAIT_OBJECT_0 but you need run it in loop while (::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE )) because we can have several messages here. and better use MsgWaitForMultipleObjectsEx instead - try this code:
bool bQuit = false;
while( !bQuit )// for (;;)
{
MSG msg;
switch(::MsgWaitForMultipleObjectsEx( 0, NULL, 12, QS_ALLINPUT, 0))
{
case WAIT_OBJECT_0:
while (::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ))
{
if( msg.message == WM_QUIT )
bQuit = true;// return;
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
case WAIT_TIMEOUT:
RenderFrame();
::SwapBuffers( hDc );
}
}
if I press Alt+F4 to close the window, it does not quit right after I
release the key
several messages posted to your thread queue when you press Alt+F4, MsgWaitForMultipleObjects return, but you process not all it but only one
however, if I hover the mouse over the window, it quits instantly
new messages (WM_MOUSEMOVE) placed, but main MsgWaitForMultipleObjects again return and you at the end process all messages related to close process

Tell windows to process all but one message

I have a function triggered by a message(WM_ONDATA defined by me) the function will execute this code :
MSG msg;
while(::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if( !AfxGetApp()->PumpMessage() )
{
::PostQuitMessage(0);
return 0;
}
}
return 1;
The problem is that there could be on the message queue another message that could trigger the function.
I'm wondering if I can make it process all the message but WM_ONDATA?
Recall that the third and fourth parameters to PeekMessage let you specify a range of message values. Messages outside that range won't be processed.
while (PeekMessage(&msg, NULL, 0, WM_ONDATA - 1, PM_NOREMOVE)
|| PeekMessage(&msg, NULL, WM_ONDATA + 1, 0xffff, PM_NOREMOVE))
You could get the window proc to ignore the message or to queue it's execution. If you're just looking to avoid recursion, have a reentrance lock
class MyDlg : ...
{
MyDlg(...) : m_inOnData(false), ... { .... }
...
private:
BOOL m_inOnData;
};
....
void MyDlg::OnOnData(...)
{
if (m_inOnData)
return;
m_inOnData = TRUE;
....
m_inOnData = FALSE;
}
You could get fancy with a scoped RIIA struct (so things will be exception safe and slightly less verbose)
Sure - just check the message number in msg after receipt.

PostMessage() succeeds but my message processing code never receives the message

In my C++ application's GUI object I have the following in the main window procedure:
case WM_SIZE:
{
OutputDebugString(L"WM_SIZE received.\n");
RECT rect = {0};
GetWindowRect(hwnd, &rect);
if (!PostMessage(0, GUI_MSG_SIZECHANGED, w, MAKELONG(rect.bottom - rect.top, rect.right - rect.left))) {
OutputDebugString(L"PostMessage failed.\n"); // <--- never called
}
}
return 0; // break;
The GUI object also has the following getMessage() method:
int GUI::getMessage(MSG & msg) {
BOOL result = 0;
while ((result = GetMessage(&msg, 0, 0, 0)) > 0) {
if (msg.message > (GUI_MSG_BASE-1) && msg.message < (GUI_MSG_LAST+1)) {
OutputDebugString(L"GUI message received.\n");
break;
}
else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return result;
}
The application object calls this method in the following way:
while ((result = _gui.getMessage(msg)) > 0) {
switch (msg.message) {
// TODO: Add gui message handlers
case GUI_MSG_SIZECHANGED:
OutputDebugString(L"GUI_MSG_SIZECHANGED received.\n");
_cfg.setWndWidth(HIWORD(msg.lParam));
_cfg.setWndHeight(LOWORD(msg.lParam));
if (msg.wParam == SIZE_MAXIMIZED)
_cfg.setWndShow(SW_MAXIMIZE);
else if (msg.wParam == SIZE_MINIMIZED)
_cfg.setWndShow(SW_MINIMIZE);
else if (msg.wParam == SIZE_RESTORED)
_cfg.setWndShow(SW_SHOWNORMAL);
break;
}
}
The application object is interested in the window size because it stores this information in a configuration file.
When I run this in Visual Studio's debugger, the output window looks like this after resizing the window:
WM_SIZE received.
GUI message received.
GUI_MSG_SIZECHANGED received.
WM_SIZE received.
WM_SIZE received.
WM_SIZE received.
WM_SIZE received.
...etc...
The PostMessage() function never fails, but seems to only send GUI_MSG_SIZECHANGED (#defined as WM_APP + 0x000d) the first time WM_SIZE is handled, which is right after handling WM_CREATE.
I have no idea what could be causing this. I tried using SendMessage and PostThreadMessage but the result is the same. Also read through MSDN's message handling documentation but couldn't find what's wrong with my code.
Could anyone help?
Hacking a custom message loop is something you'll live to regret some day. You hit it early.
Don't post messages with a NULL window handle, they can only work if you can guarantee that your program only ever pumps your custom message loop. You cannot make such a guarantee. These messages fall into the bit bucket as soon as you start a dialog or Windows decides to pump a message loop itself. Which is the case when the user resizes a window, the resize logic is modal. Windows pumps its own message loop, WM_ENTERSIZEMOVE announces it. This is also the reason that PostThreadMessage is evil if the thread is capable of displaying any window. Even a MessageBox is fatal. DispatchMessage cannot deliver the message.
Create a hidden window that acts as the controller. Now you can detect GUI_MSG_SIZECHANGED in its window procedure and no hacks to the message loop are necessary. That controller is not infrequently the main window of your app btw.

Thread Fails to Exit On Application Exit - C++

My application creates a thread that polls for Windows messages. When it is time to close down, my application sends the WM_QUIT message.
In the application thread, this is how I am attempting to shut things down:
if ( _hNotifyWindowThread != NULL )
{
ASSERT(_pobjNotifyWindow != NULL);
::SendMessage( _pobjNotifyWindow->m_hWnd, WM_QUIT, 0, 0 );
::WaitForSingleObject( _hNotifyWindowThread, 50000L );
::CloseHandle( _hNotifyWindowThread ); // <-- PC never gets here.
_hNotifyWindowThread = NULL;
}
This is the message pump running in my thread function:
// Start the message pump...
while ( (bRetVal = ::GetMessage(
&msg, // message structure
_pobjNotifyWindow->m_hWnd, // handle to window whose messages are to be retrieved
WM_DEVICECHANGE, // lowest message value to retrieve
WM_DEVICECHANGE // highest message value to retrieve
)) != 0 )
{
switch ( bRetVal )
{
case -1: // Error generated in GetMessage.
TRACE(_T("NotifyWindowThreadFn : Failed to get notify window message.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
return ::GetLastError();
break;
default: // Other message received.
::TranslateMessage( &msg );
::DispatchMessage( &msg );
break;
}
}
delete _pobjNotifyWindow; // Delete the notify window.
return msg.wParam; // Return exit code.
The Microsoft documentation for GetMessage states:
If the function retrieves the WM_QUIT message, the return value is zero.
Note that GetMessage always retrieves WM_QUIT messages, no matter which values you specify for wMsgFilterMin and wMsgFilterMax.
If this is the case, then I would expect a call to GetMessage that retrieves the WM_QUIT message to return 0. However, debugging leaves me to believe that the message is not received properly. What is odd is that I can place a breakpoint in the WndProc function, and it seems to get the WM_QUIT message.
What am I doing wrong? Should I be using a different function for posting messages between threads? Thanks.
This the complete answer (I'm almost sure):
replace
::SendMessage( _pobjNotifyWindow->m_hWnd, WM_QUIT, 0, 0 );
with
::PostMessage( _pobjNotifyWindow->m_hWnd, WM_CLOSE, 0, 0 );
replace
( (bRetVal = ::GetMessage( &msg, _pobjNotifyWindow->m_hWnd, WM_DEVICECHANGE, WM_DEVICECHANGE )) != 0 )
with ( (bRetVal = ::GetMessage( &msg, NULL ,0 ,0 )) != 0 )
In your WindowsProcedure :
case WM_CLOSE : DestroyWindow( hWnd ); break; //can be return
case WM_DESTROY : PostQuitMessage( 0 );
While my knowledge of the WinAPI has limits, it seems WM_QUIT is special and not meant to be posted like other messages.
According to Raymond Chen:
Like the WM_PAINT, WM_MOUSEMOVE, and WM_TIMER messages, the WM_QUIT message is not a "real" posted message. Rather, it is one of those messages that the system generates as if it were posted, even though it wasn't.
.
When a thread calls PostQuitMessage, a flag in the queue state is set that says, "If somebody asks for a message and there are no posted messages, then manufacture a WM_QUIT message." This is just like the other "virtually posted" messages.
.
PostThreadMessage just places the message in the thread queue (for real, not virtually), and therefore it does not get any of the special treatment that a real PostQuitMessage triggers.
So you should probably be using PostQuitMessage.
Of course, there may be ways to work around the current odd behavior (as per other answers). But given the description of WM_QUIT being special, you may want to use PostQuitMessage anyway.
There are 2 problems with this code.
::GetMessage() doesn't stop because you're using the hWnd parameter with something else than NULL. You need to fetch the thread messages to get ::GetMessage() to return 0.
Following on the logic in (1), you need to post the message using ::PostThreadMessage() to put it in the thread's message queue.
All of this is rather well illustrated by the fact the ::PostQuitMessage(status) is a shorthand for
::PostThreadMessage(::GetCurrentThreadId(), WM_QUIT, status, 0);
EDIT:
It seems that people have been led into thinking that ::PostThreadMessage(...,WM_QUIT,...); doesn't work because it doesn't get the special treatement of setting the QS_QUIT flag that is set by ::PostQuitMessage(). If that was the case, then there would be no way to send WM_QUIT to another thread's message queue. Here is proof that it works anyways.
In particular, pay attention to the constants Use_PostQuitMessage and GetMessage_UseWindowHandle. Feel free to change the values and play around with the code. It works just as advertised in my answer, except that I mistakenly used ::GetCurrentThread() rather than ::GetCurrentThreadId() before trying it out.
#include <Windows.h>
#include <iomanip>
#include <iostream>
namespace {
// Doesn't matter if this is 'true' or 'false'.
const bool Use_PostQuitMessage = false;
// Setting this to 'true' prevents the application from closing.
const bool GetMessage_UseWindowHandle = false;
void post_quit_message ()
{
if ( Use_PostQuitMessage ) {
::PostQuitMessage(0);
}
else {
::PostThreadMessageW(::GetCurrentThreadId(), WM_QUIT, 0, 0);
}
}
::BOOL get_message ( ::HWND window, ::MSG& message )
{
if ( GetMessage_UseWindowHandle ) {
return (::GetMessageW(&message, window, 0, 0));
}
else {
return (::GetMessageW(&message, 0, 0, 0));
}
}
::ULONG __stdcall background ( void * )
{
// Allocate window in background thread that is to be interrupted.
::HWND window = ::CreateWindowW(L"STATIC", 0, WS_OVERLAPPEDWINDOW,
0, 0, 512, 256, 0, 0, ::GetModuleHandleW(0), 0);
if ( window == 0 ) {
std::cerr << "Could not create window." << std::endl;
return (EXIT_FAILURE);
}
// Process messages for this thread's windows.
::ShowWindow(window, SW_NORMAL);
::MSG message;
::BOOL result = FALSE;
while ((result = get_message(window,message)) > 0)
{
// Handle 'CloseWindow()'.
if ( message.message == WM_CLOSE )
{
post_quit_message(); continue;
}
// Handling for 'ALT+F4'.
if ((message.message == WM_SYSCOMMAND) &&
(message.wParam == SC_CLOSE))
{
post_quit_message(); continue;
}
// Dispatch message to window procedure.
::TranslateMessage(&message);
::DispatchMessageW(&message);
}
// Check for error in 'GetMessage()'.
if ( result == -1 )
{
std::cout << "GetMessage() failed with error: "
<< ::GetLastError() << "." << std::endl;
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}
}
int main ( int, char ** )
{
// Launch window & message pump in background thread.
::DWORD id = 0;
::HANDLE thread = ::CreateThread(0, 0, &::background, 0, 0, &id);
if ( thread == INVALID_HANDLE_VALUE ) {
std::cerr << "Could not launch thread." << std::endl;
return (EXIT_FAILURE);
}
// "do something"...
::Sleep(1000);
// Decide to close application.
::PostThreadMessageW(id, WM_QUIT, 0, 0);
// Wait for everything to shut down.
::WaitForSingleObject(thread, INFINITE);
// Return background thread's success code.
::DWORD status = EXIT_FAILURE;
::GetExitCodeThread(thread,&status);
return (status);
}
P.S.:
To actually test the single-threaded use of ::PostThreadMessage(::GetCurrentThreadId(),...); invoke ::background(0); in main instead of launching the thread.
Just a guess. Your quit code is called from within a message loop call from another window which has its own message pump? According to MSDN for WaitForSingleObject you block the current UI thread indefinitely and prevent processing its own messages.
From
http://msdn.microsoft.com/en-us/library/ms687032%28VS.85%29.aspx
Use caution when calling the wait
functions and code that directly or
indirectly creates windows. If a
thread creates any windows, it must
process messages. Message broadcasts
are sent to all windows in the system.
A thread that uses a wait function
with no time-out interval may cause
the system to become deadlocked. Two
examples of code that indirectly
creates windows are DDE and the
CoInitialize function. Therefore, if
you have a thread that creates
windows, use MsgWaitForMultipleObjects
or MsgWaitForMultipleObjectsEx, rather
than WaitForSingleObject.
It could be that the WM_Quit message is broadcast to your own window which does not process any messages due to your WaitForSingleObject call. Try instead MsgWaitForMultipleOjbects which tries to call your message loop from time to time.
Yours,
Alois Kraus
WM_QUIT is not a window message, so you shouldn't be sending it to a window. Try using PostThreadMessage instead:
PostThreadMessage(GetThreadId(_hNotifyWindowThread), WM_QUIT, 0, 0);
If that doesn't work, try posting a dummy message to your window:
::PostMessage( _pobjNotifyWindow->m_hWnd, WM_APP, 0, 0 );
and use it as a signal to quit in your window procedure:
case WM_APP:
PostQuitMessage(0);
Your GetMessage(...) retrieves only message of the window you supplied. However WM_QUIT does not have a window associated with it. You Need to call GetMessage without a window handle (i.e. NULL) which retrieve any message in the Message-Queue.
To elaborate on what TheUndeadFish said; there's a "secret" undocumented QS_QUIT flag set in the wake flags of the thread's message queue when PostQuitMessage is called. GetMessage looks at its wake flags in a particular order to determine what message to process next.
If it finds that QS_QUIT is set, it generates a WM_QUIT message and causes GetMessage to return FALSE.
You can get a thread's documented wake flags that are currently set with GetQueueStatus.
The details can be found in Programming Applications for Microsoft Windows, 4th Edition (unfortunately, the newest edition removed these topics).

Game loop in Win32 API

I'm creating game mario like in win32 GDI . I've implemented the new loop for game :
PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);
while (msg.message!=WM_QUIT)
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else // No message to do
{
gGameMain->GameLoop();
}
}
But my game just running until I press Ctrl + Alt + Del ( mouse cursor is rolling ).
I've always been using something like that:
MSG msg;
while (running){
if (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
try{
onIdle();
}
catch(std::exception& e){
onError(e.what());
close();
}
}
onIdle is actual game lopp implementation, onError() is an error handler (takes error description as argument), and "running" is either a global bool variable or a class member. Setting "running" to false shuts down the game.
I think this really depends on your context. Windows will only send a WM_QUIT in response to your application calling PostQuitMessage. A common (if not great) solution here is to use a bool to exit the message loop when your program wants to end.
I guess the program may ask user for continue or exit, inside GameLoop function call. On exit Post WM_QUIT message to the window.
PostMessage(hWnd, WM_QUIT, 0, 0 );
hWnd-> The handle of the game window
else make a call to
DestroyWindow(hWnd);
This will send a WM_DESTROY to your window procedure. There you can call
PostQuitMessage(0);