Sign in to follow this  
1st_maza

WinMain, WndProc & Variable

Recommended Posts

If I have a variable declared in the WinMain function, how do I get it sent to the WndProc function so it knows about it too? I know the normal way but its not like the function is like any other... The declared variable is of a class..

Share this post


Link to post
Share on other sites
Quote:
Original post by 1st_maza
I know the normal way but its not like the function is like any other...
I don't really understand this... WndProc and WndMain are just like every other function! Or do you mean because WndProc is a callback and you don't call it directly yourself?

The normal way would be to use the SetWindowLong function to "attach" your extra data to the window handle (passing GWL_USERDATA as the nIndex parameter), and you can then use GetWindowLong in your WndProc to get it back again.

Share this post


Link to post
Share on other sites
wndProc is the message handler I assume, so You would just send a message and that is past to and thru wndProc. look up the message pump for more help. You probably want user message with user data right? well look up doing that google should have a very detailed article

Share this post


Link to post
Share on other sites
What I mean is that it cant really be changed (the parameters in the functionhead), last time my compiler whined when I did that..

edit:
LRESULT CALLBACK WndProc( HWND hwndMain, UINT msg, WPARAM wParam, LPARAM lParam );

Share this post


Link to post
Share on other sites
It depends on what the variable is and its usage. Winproc is a function like any other standalone c style function, if the variable was in relation to a window then there is a method of adding extra space in the window class otherwise you have to extend the scope in which it is defined to be known about in the function. ie make it a global or namespaced variable.

Share this post


Link to post
Share on other sites
My teacher slaps me on my fingers if I do another global variable :P

Namespace is something I would prefer staying away from :P

In winmain there is a class variable declared that is used in almost all functions (winmain, wndproc, initialize, draw).

It's just lately I've started using variables declared within classes, before that I got alot of slaps on my fingers from the teacher about using global variables, but now im reaching almost 2d programming so its time to change.

Share this post


Link to post
Share on other sites
I think a better question to the OP would be, what exactly are you trying to pass to the WndProc. You realize its sole purpose is to process messages from Windows. Having written more then my fair share of Win32 code I can honestly say I've never needed to pass a variable from WinMain to my WndProc function.

Maybe we can help you design your program better. Explain what your trying to pass and why you think you need too.

Share this post


Link to post
Share on other sites
I have a class.
That class has alot of variables, bools mostly.
In four of my functions those variables are needed for different reasons.

winmain & wndproc, everyone knows these ones..
initialize, is used to create d3d, sprite, load image..
draw, in this function things can be altered with the image that was just loaded, examles: rotate, scale, move left or right..

I was able to send the variable through to the initialize & draw functions but I have really no idea how to send it to the wndproc function. Since this is really the first time Ive ever needed to send a variable to the wndproc.

Share this post


Link to post
Share on other sites
You cant pass a variable of your own the windows procedure, atleast i dont think so, usually you would want to just have the window procedure inside the same window class but this causes even bigger problems. for now try not to use the windows procedure to much as things can get complicated when using classes with it. just handle mandatory windows stuff like quiting to program that doesnt make use of or manipulate any of your own class members/methods. For the stuff you wanted in the window proc, create another function that mimmics the message you was trying to handle in some way and call it elsewhere. Once you get a little better, you can take a look at this code, it demonstrates a few waya of getting a windows procedure in a class and working as expected.

http://www.bsdpower.com/code/2003/01/29/window-procedures-as-class-member-functions/

Share this post


Link to post
Share on other sites
I still have no idea why your WndProc needs access to variables outside of it, normally it processes messages (that give you information) that you pass on to other classes/functions. You shouldn't really be doing (example) AI in your WndProc. If you do need some kind of information, then create a messaging system and request it. Likely a much simpler and "proper" way then passing in other variables.

I can honestly say, in my situation and in my engine, I cannot ever recall not dispatching messages out from my WndProc. I cannot recall a time when I needed outside variables. I'm just trying to get you to think if you REALLY need them. Or if there might be a better/simpler way.

Can you explain what these needed variables are and why they are needed. We might (as I said earlier) be able to come up with a cleaner solution for you.

Mike

Share this post


Link to post
Share on other sites
like mike said ai should not be donethe only processing done should be is msg processing like giving control to a control or getting polls from the mouse

Share this post


Link to post
Share on other sites
Did anybody read my first response? The solution is to use SetWindowLong and GetWindowLong. Look those functions up in MSDN (the link I provided is a good start). Usage is quite simple.

Share this post


Link to post
Share on other sites
yeah i seen it but that can be used anywhere. Make use of static in a class does the same thing without tying it to a window.

you could then use classname::staticfunc();

Share this post


Link to post
Share on other sites
Except then you still don't have access to instance variables in that class. I'd personally use Codeka's solution, and it's already commonly done when you have an instanced object that WinProc needs access to.

~Jonathan

Share this post


Link to post
Share on other sites
I have a few ifs in the wndproc that becomes true if you press a key like R for rotate or when you press an arrow key:

...
case VK_LEFT:
{
variables.moveleft = true;
break;
}
...

I dont have much else in the wndproc function except what is needed.

Share this post


Link to post
Share on other sites
The solution I use in my WndProc/Window/Application class setup is that the WndProc class is passed a pointer to the Application class when the main window is created.

From there, the WndProc class only contains code for dealing with the window, and any other messages that need to induce application logic will simply call into the Application class through the pointer it was given.

I'm sure you can use SetWindowLongPtr much more frequently, but it strikes me as error-prone and difficult to manage it's state in relation to the application state.


All that said, the class you are making is silly. It's, at best, a marginally better solution than the globals you had before. Its certainly not going to be very scalable to continue this way, so you would be wise to work towards a more sensible, correctly encapsulated design.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ravyne
The solution I use in my WndProc/Window/Application class setup is that the WndProc class is passed a pointer to the Application class when the main window is created.

From there, the WndProc class only contains code for dealing with the window, and any other messages that need to induce application logic will simply call into the Application class through the pointer it was given.

I'm sure you can use SetWindowLongPtr much more frequently, but it strikes me as error-prone and difficult to manage it's state in relation to the application state.


All that said, the class you are making is silly. It's, at best, a marginally better solution than the globals you had before. Its certainly not going to be very scalable to continue this way, so you would be wise to work towards a more sensible, correctly encapsulated design.


Quoted as its exactly what I was trying to get at. I read Codeka's solution and honestly it sounds hackish to me. Thats why I was trying to really get more information so we could actually help the OP develop a "proper" solution to his issues.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mike2343
Quoted as its exactly what I was trying to get at. I read Codeka's solution and honestly it sounds hackish to me. Thats why I was trying to really get more information so we could actually help the OP develop a "proper" solution to his issues.


I was mostly responding to the people saying "put the WndProc in your class" or "post a special message", "make it a global" or whatever. My second response did come off a bit harsh, and I apologize for that :-)

Anyway, I agree that in many cases, there might be a better way to do things, and certainly in this particular instance that may very well be the case.

Having said that, my solution is quite common if you're writing a Win32 wrapper and want to pass an instance of your "window" class to the WndProc.

Share this post


Link to post
Share on other sites
This is what I do:


// engine.h

class Engine
{
public:
Engine(HWND Hw);
~Engine();

void Update();
void Render();

void KeyDown();
};

// engine.cpp

Engine::Engine(HWND Hw)
{
SetWindowLongPtr(Hw,GWLP_USERDATA,reinterpret_cast<LONG>(this));
}

Engine::~Engine()
{
SetWindowLongPtr(Hw,GWLP_USERDATA,NULL);
}

// main.cpp

#include "engine.h"

int WinMain(yada yayda)
{
try
{
HWND Hw=CreateWindow(yada yada);

Engine E(Hw);

while(yada yada)
{
MessagePumpStuff();

E.Update();
E.Render();
}
}

catch(...)
{
}
}

LRESULT WnnProc(yada yada)
{
Engine *E=reinterpret_cast<Engine*>(GetWindowLongPtr(Hw,GWLP_USERDATA));

switch(Msg)
{
case WM_KEYDOWN: if(E) E->KeyDown(wParam); break;

default: return DefWndProc(yada yada);
}

return 0;
}





No globals, Engine E() exists as a local variable so obeys all the usual rules regarding initialisation/destruction and the WndProc only ever gets called during the message pump, so no chance of calling it while the user data is in an indeterminate state.

Some people pass the user data to CreateWindow and respond to the WM_NCCREATE message to assign it to the HWND user data but I find my approach simpler - as long as you don't need to call the engine back during the window creation or destruction process, it's fine.

The other advantage is that I can instantiate my Engine class after Window creation (which is necessary as Engine creates the GraphicsDevice in its constructor) and avoid an Engine::Initialise() method.

[Edited by - EasilyConfused on March 13, 2009 12:36:01 PM]

Share this post


Link to post
Share on other sites
*All that said, the class you are making is silly. It's, at best, a marginally better solution than the globals you had before. Its certainly not going to be very scalable to continue this way, so you would be wise to work towards a more sensible, correctly encapsulated design.*

Well, its either putting variables in a class or getting slapped on the fingers :P

I would prefer neither, but what to do?

*Engine *E=reinterpret_cast<Engine*>(GetWindowLongPtr(Hw,GWLP_USERDATA));*

I've never written code like that so could you please tell me alittle about it?

*case WM_KEYDOWN: if(E) E->KeyDown(wParam);*
I hope you dont mind if I continue with this kind of code? I see new ways of doing code and some I would never have thought about..

Share this post


Link to post
Share on other sites
Quote:
Original post by 1st_maza
*Engine *E=reinterpret_cast<Engine>(GetWindowLongPtr(Hw,GWLP_USERDATA));*


I've never written code like that so could you please tell me alittle about it?

SetWindowLongPtr stores a long datatype. The thing is, an 'Engine*' is not a 'long'. The reinterpret_cast - which, by the way, is best to avoid except when doing things like this - takes a variable and re-interprets it as an entirely new datatype, no questions asked (or, at least, very few). The value it outputs is exactly the same... except it's considered as something other than what it was.

Basically, we have a 'long' doppelganger that's really an Engine*, that SetWindowLongPtr stores. GetWindowLongPtr then returns that long value, but we need to cast it back to an Engine*.

~Jonathan

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this