Jump to content
  • Advertisement
Sign in to follow this  
Tispe

Processing a WM_CHAR as input without Globals

This topic is 2628 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello

I have trouble getting input for my game without resorting to global variables. This is because I can't pass arguments or get a return value from the Windows Procedure. The only way I can think of is using PeekMessage() and do the handling there, but "The TranslateMessage function generates a WM_CHAR message", so I can't do it there it seems. How do you guys do it?


LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
} break;

case WM_CHAR:
switch (wParam)
{
case 0x08:

// Process a backspace.

break;

case 0x0A:

// Process a linefeed.

break;

case 0x1B:

// Process an escape.

break;

case 0x09:

// Process a tab.

break;

case 0x0D:

// Process a carriage return.

break;

default:

// Process displayable characters.

break;
} break;
}

return DefWindowProc (hWnd, message, wParam, lParam);
}

Share this post


Link to post
Share on other sites
Advertisement
The usual method is to associate extra data with the window handle via GetWindowLongPtr()/SetWindowLongPtr() with a GWLP_USERDATA argument. Store a pointer to whatever data structure you want to associate with your input. This is often done as part of window creation. The stored pointer is passed as the lpParam argument of the CreateWindowEx() call and then extracted as part of the CREATESTRUCT during the initial window messages.

Share this post


Link to post
Share on other sites
That read got away from me quickly. Basically as soon as I get a window handle I can associate a void pointer to that right?

so would this be correct?

WinMain:

//A data struct to be shared with WinProc and input handler
MyClass *pSomeData;

//create window
hWnd = CreateWindowEx(//....);

//Associate data to handle
SetWindowLongPtr(hWnd, GWLP_USERDATA,(long)pSomeData);


WinProc:

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
MyClass *pSomeData = (MyClass)GetWindowLongPtr(hWnd, GWLP_USERDATA);

switch(message)
{
case WM_CHAR:
switch (wParam)
{
case VK_BACK:

pSomeData->SomeVector.push_back(VK_BACK);
break;

case VK_RETURN:

pSomeData->SomeVector.push_back(VK_RETURN);
break;

case VK_ESCAPE:

// Process an escape.
PostQuitMessage(0);
break;

default:

// Process displayable characters.

break;
} break;
}
}


Input handler:

.
.
.
MyClass *pSomeData = (MyClass)GetWindowLongPtr(hWnd, GWLP_USERDATA);
for(int i=0;i<SomeVector.size();i++)
{
InputData = pSomeData->SomeVector;
}

Share this post


Link to post
Share on other sites
With regards to the article, is it only nessecary to pass the lpParam argument of the CreateWindowEx() when wrapping. Is my above example valid as I don't wrap my window creation in to one class (I only need the one window for my game)?<br>

Share this post


Link to post
Share on other sites

so would this be correct?
[/quote]
Well, ignoring the obvious casting flaws, it looks like roughly the correct "shape". If your input handler is outside the window procedure, you can pass the pointer to it in other ways, rather than relying on GetWindowLongPtr().

It is considered good style to use the c++ casts, in this case reinterpret_cast<>(), instead of C style casting.


With regards to the article, is it only nessecary to pass the lpParam argument of the CreateWindowEx() when wrapping. Is my above example valid as I don't wrap my window creation in to one class (I only need the one window for my game)?
[/quote]

As the article explains, some messages could be sent before your code gets to call SetWindowLongPtr():

You see, some messages are sent before CreateWindowEx returns - WM_NCCREATE, WM_NCCALCSIZE and WM_CREATE (in that order). If the value does not exist by the time WM_NCCREATE is called then, by some mysterious Windows behavior that I still don't understand, the value never gets inserted.
[/quote]
I'm not a Win32 programmer, so I cannot confirm or deny this behaviour, and whether the proposed workaround is necessary. Perhaps it is safe to skip it. However, one prominent windows programmer uses the same approach when wrapping, so I would probably go with that one.

Share this post


Link to post
Share on other sites

As the article explains, some messages could be sent before your code gets to call SetWindowLongPtr():

You see, some messages are sent before CreateWindowEx returns - WM_NCCREATE, WM_NCCALCSIZE and WM_CREATE (in that order). If the value does not exist by the time WM_NCCREATE is called then, by some mysterious Windows behavior that I still don't understand, the value never gets inserted.
[/quote].
[/quote]

i'm confused

Is SetWindowLongPtr() actually dependant on those messages though? CreateWindowEx returns the handle which is all I need to SetWindowLongPtr().

Also, why would it matter that some messages are sent before CreateWindowEx return? We are still in the same thread and WinProc arent run before the return, is it?

Share this post


Link to post
Share on other sites
I assume the people who are smarter and more experienced than me aren't jumping through hoops for no reason. You'd need to read the documentation closely to figure out what is actually going on, or you could trust their tried-and-tested advice.


Also, why would it matter that some messages are sent before CreateWindowEx return? We are still in the same thread and WinProc arent run before the return, is it?
[/quote]
Although the quoted text says sent, it probably meant recieved. The documentation for CreateWindowEx says:

Return value

Type: HWND

If the function succeeds, the return value is a handle to the new window.

If the function fails, the return value is NULL. To get extended error information, call GetLastError.

This function typically fails for one of the following reasons:

  • an invalid parameter value
  • the system class was registered by a different module
  • The WH_CBT hook is installed and returns a failure code
  • if one of the controls in the dialog template is not registered, or its window window procedure fails WM_CREATE or WM_NCCREATE

    [/quote]
    In order to know this, CreateWindowEx must invoke the window procedure. From the light Googling I've been doing, it appears your window procedure does not handle these messages correctly, as it does not call DefWindowProc().

    Again, I am not a Win32 programmer, you'll need to do some research on this yourself.

Share this post


Link to post
Share on other sites
I found on codeguru somone saying:

CreateWindow(), ShowWindow(), and UpdateWindow() also causes the window procedure to be called[/quote]

Which if true means that CreateWindowEx() sends WM_NCCREATE, WM_NCCALCSIZE and WM_CREATE and then(or before?) calls WinProc internally, before returning.

It is said by the author of the atricle that if the value (i.e the pointer to insert) does not exist by the time WM_NCCREATE is called then the value never gets inserted. WM_NCCREATE would in this case be handled before CreateWindowEx() returns. And thus SetWindowLongPtr() no longer functions after that?

The "value" does exist as it is created before CreateWindowEx(), but does he mean it must exist as added to the handle by SetWindowLongPtr() before DefWindowProc() handles the WM_NCCREATE?

also:

Certain window data is cached, so changes you make using SetWindowLongPtr will not take effect until you call the SetWindowPos function.
[/quote]

confusing indeed.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!