Creating a bassic Window Class for windows?

Started by
10 comments, last by ANSI2000 22 years, 5 months ago
Concerning the other post I decided just to go with Win32 for now... I wrote a class just for Windows I have a concern with the callback. Is ok the way am doing it? Bassicly someone will be able to subclass G3DWindow and overide onEvent. IS there a way of abstracting more or is it ok? Also.... I have done oo before but recently I got the hagn of paterns etc... What patern woudl this fall under? class G3DWindow { public: // All methods here... static LRESULT CALLBACK windowEvents(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); virtual LRESULT onEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) = 0; private: static G3DWindow *thisWindow; }; LRESULT CALLBACK G3DWindow::windowEvents(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return(thisWindow->onEvent(message)); } LRESULT G3DWindow::onEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return(0L); } Edited by - ANSI2000 on October 24, 2001 12:36:23 AM Edited by - ANSI2000 on October 25, 2001 3:31:14 PM
Advertisement
What I have to grab attention again?
You may be the only person who reads this board more often than me or Dave :p Just be patient! There''s few post between 2AM and 3PM (a few, but the on-slaught starts later), most people check in the evening after they get home from work or school.

The only GoF pattern I see an inkling of, is the singleton. This is due to the static G3DWindow, which indicates that you only want one instance of this class to ever be made.

Chain-of-Command can be a good pattern to use with message pumps, I usually use it when I make state-machines. If you wanted to create a complex window management class you could go this way. If you want simple, don''t bother with a pattern here. Just sub-class and override OnEvent. If you want to be able to make multiple windows - if you don''t want it to be a singleton, then you can use a template to make it work a little better.

  template<typename TParent>class G3DWindow{public:	typedef LRESULT (TParent::*MessageMeth)(HWND, UINT, LPARAM, WPARAM);	HWND CreateWindow(TParent* pParent, MessageMeth pMeth, ...)		{		ms_pParent = pParent;		ms_MessageMeth = pMeth;		return CreateWindowEx(...)		}	void Pump()		{		while(...)		GetMessage(...)		}	static LRESULT MessageProc(HWND hWnd, UINT msg, LPARAM lParam, WPARAM wParam)		{		_ASSERT(ms_pParent);		_ASSERT(ms_pMeth);		return ms_pParent->*ms_pMeth(hWnd, msg, lParam, wParam);		}protected:	static TParent* ms_pParent;	static MessageMeth* ms_pMeth;};//Then to use itclass CMyWindow : public G3DWindow<CMyWindow>{public:	CMyWindow()		{		m_hWnd = G3DWindow<CMyWindow>::CreateWindow(this, OnMessage);		}protected:	LRESULT OnMessage(HWND hWnd, UINT msg, LPARAM lParam, WPARAM wParam)		{		//...		}};  

Note that this isn''t any sort of design pattern - it''s a grotesquely hack. There''s a component of the ATL, called the WTL that provides light-weight template based classes for working with windows that may be worth looking into.
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
hmm... actually I read this forum all the time...

Anyway, if you plan for the window class to be used as more than a singleton, then it would seem that what you'd like to do is use the available data in a window handle to actually store a pointer to your particular class.

This would allow for a non-hacking method of accessing your particular class-implimented messages from a callback function which otherwise would have no clue as to the existence of your classes.

What you basically do is this:
      // some window class...class CWindow{public:    // some methods here    static LRESULT CALLBACK TemplateProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);    void CreateWindow(); // this makes a special type of CreateWindowEx() call with extra dataprotected:    // your abstract window proc (note that you no longer need the HWND parameter)    virtual LRESULT WinProc(UINT Msg, WPARAM wParam, LPARAM lParam) = 0;    // maybe have your handle declared here    HWND myhandle;};// some class inherits and provides a WinProc definitionclass CNewWindow : public CWindow{...};// then in some source file// the static templating callbackLRESULT CALLBACK CWindow::TemplateProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam){	CWindow* pWindow = (CWindow*)GetWindowLong(hWnd, GWL_USERDATA);	if (!pWindow)	{	    if (Msg == WM_CREATE)	    {	        LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;	        pWindow = (CWindow*)lpcs->lpCreateParams;	        SetWindowLong(hWnd, GWL_USERDATA, (LONG)pWindow);	        return pWindow->WinProc(Msg, wParam, lParam);	    }	    else	        return DefWindowProc(hWnd, Msg, wParam, lParam);	}	else	    return pWindow->WinProc(Msg, wParam, lParam);}// for the createstruct's lpCreateParams member to have your window pointer...// ...you need this in your CreateWindowEx() callvoid CWindow::CreateWindow(){    // hopefully you've already registered a window class struct by this point...    myhandle = CreateWindowEx( // all of your                               // normal params go here                               (LPSTR)this); // this one goes at the end, reference MSDN to get more info on this}// some specific procedureLRESULT CNewWindow::WinProc(UINT Msg, WPARAM wParam, LPARAM lParam){    switch (Msg)    {    case WM_ACTIVATE:        if (!HIWORD(wParam))            Activate();        else            DeActivate();        break;    case WM_DESTROY:        PostQuitMessage(0);        break;    default:        // to send to the DefWindowProc, just send myhandle        return DefWindowProc(myhandle, Msg, wParam, lParam);    }    return 0;}      


Since the specific winproc isn't static this will obviously allow your windows to change member states as a response to receiving a message, which can be invaluable.

Hope this helps. The HWND's UserData can be a godsend.

"Don't be afraid to dream, for out of such fragile things come miracles."

Damn long comment lines making you scroll laterally while reading threads...

Edited by - Redleaf on October 25, 2001 11:58:14 PM
I do whant to be able create multiple windows.

So If I sue the singleton, all windows created will respond to the same events? Even if I overide my onEvent?

The second method looks way cleaner... Redleaf do you have a full source example of this?

Once I have the class working properley.... Here is where I want to go....

1- CReate a singleton Log class for loging errors (Unless there's better solution).
2- Create a facade Application class which will host (I guess) the: Log, Window classes also a suposed OGL class, Input, Sound, Networking etc..... classes.
3- Create a Listener patern between the OGL class and Window class, this way OGL can update it's view ports etc... when certain events occur.

From there we have the rest: 3dObjects, Physics, AI etc...

I hav a few ideas, just not to shure how it all goes together... I have the design paterns book the hard cover one (blue and white)with a forward from Booch or what's his name. It's all nice and dandy reading it and stuff but it dont mean anything to you unless you start playing with the paterns yourself and see how they fit...

Edited by - ANSI2000 on October 26, 2001 2:11:55 AM
Yes, I have a full source example, and it''s yours if you want it. Just tell me how you''d like me to send it, and I''ll tidy it up for your benefit.

"Don''t be afraid to dream, for out of such fragile things come miracles."
E-mail it to me... voodoo@videotron.ca

I should be able to read! I pretty much under stood what you did in the post, but just in case...

Thanks
The problem with that code Redleaf, is that messages are sent to the callback *before* CreateWindow returns. Which mean messages are sent *before* you could possibly set the GWL_USERDATA - and even before you know what the hWnd is!

Most notably, WM_CREATE is sent by the CreateWindow functions.

Magmai Kai Holmlor
- Not For Rent
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
And that''s why there is the condition there which forwards to the Default Proc if the USERDATA is not yet set, AND if the message is not yet WM_CREATE.

Yes WM_CREATE is sent by CreateWindow, which just happens to have the "this" pointer passed to it as a paramter for the USERDATA. Read it again if you didn''t notice it before.

If you still doubt it could work, try it for yourself. It does.

I suppose you could also have it set itself using the WM_NCCREATE message. But regardless, it works as is.

"Don''t be afraid to dream, for out of such fragile things come miracles."
Sry I must be blind
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara

This topic is closed to new replies.

Advertisement