Thread: Skeleton Window: Message Handler in a Class?

  1. #1
    Registered User
    Join Date
    Jun 2003
    Posts
    361

    Skeleton Window: Message Handler in a Class?

    Hello hello

    I'm on my quest to incorporate classes and skeleton windows into one handy, uh, class I'm one error message away from a functional app

    Here's the layout (my beautiful Main.cpp file):
    Code:
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include "Game.h"
    
    int APIENTRY WinMain(HINSTANCE MyInstance, HINSTANCE PrevInstance, LPSTR kposzArgs, int nWinMode)
    {
    	MSG Msg;
    	Game MyGame(MyInstance);
    	
    	while(GetMessage(&Msg, NULL, 0, 0))
    	{
    		TranslateMessage(&Msg);
    		DispatchMessage(&Msg);
    	}
    	return Msg.wParam;
    }
    That is all the code in my Main.cpp file.

    The code in my Game.h file (relevant portions) looks like:
    Code:
    #pragma once
    
    class Game
    {
    public:
    	Game(void);
    	Game(HINSTANCE hInstance);
    	~Game(void);
    	LRESULT CALLBACK WinFunc(HWND hWnd, unsigned int Msg, WPARAM wParam, LPARAM lParam);
    private:
    	HWND MainWindow;
    	HINSTANCE MainInstance;
    };
    The key thing to note here is that my LRESULT CALLBACK... function is a method in my Game Class.

    Finally, Game.cpp looks like:
    Code:
    #include "Game.h"
    #using <mscorlib.dll>
    
    Game::Game(HINSTANCE hInstance)
    {
    	MainInstance = hInstance; //I'm hoping I can do that
    	Fullscreen = GetScreenMode(); //Works fine
    	InitWindow(); //Current problem lies inside here (below)
    }
    
    void Game::InitWindow(void)
    {
    	WNDCLASS WCL;
    	DWORD Style;
    
    	WCL.cbClsExtra = 0;
    	WCL.cbWndExtra = 0;
    	WCL.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    	WCL.hCursor = LoadCursor(MainInstance, IDC_ARROW);
    	WCL.hIcon = LoadIcon(MainInstance, IDI_APPLICATION);
    	WCL.hInstance = MainInstance;
    	WCL.lpfnWndProc = WinFunc; //Have also tried Game::WinFunc
    	WCL.lpszClassName = "MyClass";
    	WCL.lpszMenuName = NULL;
    	WCL.style = CS_OWNDC;
    	if (!RegisterClass(&WCL)) exit(10);
    	if (Fullscreen)
    	{
    		Width = GetSystemMetrics(SM_CXSCREEN);
    		Height = GetSystemMetrics(SM_CYSCREEN);
    		Style = WS_POPUP;
    	}
    	else
    	{
    		Width = GetSystemMetrics(SM_CXSCREEN);
    		Height = GetSystemMetrics(SM_CYSCREEN);
    		Style = WS_OVERLAPPED|WS_SYSMENU; 
    	}
    	MainWindow = CreateWindow("MyClass", "RPG", Style, 0, 0, Width, Height, NULL, NULL, MainInstance, NULL);
    	if (!MainWindow) exit(10);
    
    	ShowWindow(MainWindow, SW_SHOW);
    	UpdateWindow(MainWindow);
    	SetFocus(MainWindow);
    }
    
    LRESULT CALLBACK Game::WinFunc(HWND hWnd, unsigned int Msg, WPARAM wParam, LPARAM lParam)
    {
    	switch(Msg)
    	{
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		break;
    	default:
    		return DefWindowProc(hWnd, Msg, wParam, lParam);
    	}
    	return 0;
    }
    Those last two functions are usually what appear in the WinMain function, but I've moved them into the class. So, my problem comes from the line:
    WCL.lpfnWndProc = WinFunc;

    I get an error saying:
    error C2440: '=' : cannot convert from 'LRESULT (__stdcall Game::* )(HWND,unsigned int,WPARAM,LPARAM)' to 'WNDPROC'

    So then, my question is:
    Is there no way I can pass a Message Handling Function that is within a Class to be the default handler?

    Even if you don't know the answer, I really appreciate you atleast reading that all and getting down here to the bottom If any ideas or advice can be given (am I doing something else wrong?) on how (if at all possible) to use my "Game::WinFunc" function, I'm open to any insight.
    Last edited by Epo; 12-28-2003 at 11:16 PM.

  2. #2
    Skunkmeister Stoned_Coder's Avatar
    Join Date
    Aug 2001
    Posts
    2,572
    The problem your having is because your wndproc has to be declared as _stdcall and member functions are called using the _thiscall declarator. This basically means that to pass a class func off as a wndproc then that func must be static.
    Free the weed!! Class B to class C is not good enough!!
    And the FAQ is here :- http://faq.cprogramming.com/cgi-bin/smartfaq.cgi

  3. #3
    Registered User
    Join Date
    Jun 2003
    Posts
    361
    How would I go about making a function static? I've seen the word but have never gotten around to using it...ever

    Would you call this a good idea? Or is this more of a roundabout way that may have reprecussions?

    Thanks so far though, you're making it sound much simpler than what I thought it would be

  4. #4
    Registered User
    Join Date
    Jun 2003
    Posts
    361
    I'm gonna answer this question myself before anyone else so that I can save atleast a bit of my dignity.

    You put the word "static" infront of the declaration.

    Hah


    Um, just wondering though, what does static mean? I haven't had an explanation yet that I can understand.

  5. #5
    Skunkmeister Stoned_Coder's Avatar
    Join Date
    Aug 2001
    Posts
    2,572
    The downside is a class member static function can only access static members.
    Free the weed!! Class B to class C is not good enough!!
    And the FAQ is here :- http://faq.cprogramming.com/cgi-bin/smartfaq.cgi

  6. #6
    Registered User
    Join Date
    Jun 2003
    Posts
    361
    It's funny you mention that, cause I was just about to ask why I was getting this error:
    error C2597: illegal reference to non-static member 'Game::bRunning'

    Which...well...you just told me....this is gonna take some messing around with, thanks for your help eh

    Edit:
    Any thoughts on why I get this though?
    New error LNK2020: unresolved token (0A00000E) ?bRunning@Game@@0_NA

    if I declare bRunning as a Private Member of my Game Class?
    static bool bRunning;
    Last edited by Epo; 12-28-2003 at 11:16 PM.

  7. #7
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    These kind of questions come up up periodically and a search of this board should give you some answers to your questions:

    static window procedures

    I attached a quick and dirty example (using dialog) to the last post in this thread that might be of some interest to you.

    Good luck.
    CProgramming FAQ
    Caution: this person may be a carrier of the misinformation virus.

  8. #8
    Registered User
    Join Date
    Jun 2003
    Posts
    361
    That's exactly what I was looking for

    I'm wondering, are there any side effects to using this method though? Like...your computer locking up into a turtle slow speed when you try to click the X button?

    I seem to still be having troubles with receiving messages...

    Main.cpp
    Code:
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include "Game.h"
    
    int APIENTRY WinMain(HINSTANCE MyInstance, HINSTANCE PrevInstance, LPSTR kposzArgs, int nWinMode)
    {
    	Game MyGame(MyInstance); //Can I use the Instance like this?
    
    	while (MyGame.bRunning)
    	{
    		MyGame.MessagePump();
    		MyGame.RenderScene();
    	}
    	return 0;
    }
    Game.h
    Code:
    #include <D3DX8.h>
    
    #pragma comment(lib,"D3D8.lib")
    #pragma comment(lib,"D3DX8.lib")
    #pragma once
    
    class Game
    {
    public:
    	Game(HINSTANCE hInstance);
    	~Game(void);
    	void MessagePump(void);
    	void RenderScene(void);
    	bool bRunning;
    	static LRESULT CALLBACK SWinFunc(HWND hWnd, unsigned int Msg, WPARAM wParam, LPARAM lParam);
    protected: //What does "Protected" mean?
    	LRESULT CALLBACK WinFunc(HWND hWnd, unsigned int Msg, WPARAM wParam, LPARAM lParam);
    private:
    	D3DFORMAT Get16BitMode(void);
    	bool GetScreenMode(void);
    	bool InitWindow(void);
    	bool InitD3D(void);
    	bool InitScene(void);
    	void KillScene(void);
    	void KillD3D(void);
    	void KillWindow(void);
    	unsigned long Width, Height;
    	bool Fullscreen;
    	HWND MainWindow;
    	HINSTANCE MainInstance;
    	LPDIRECT3D8 D3D;
    	IDirect3DDevice8 *D3DDevice;
    };
    And Finally, the Game.cpp
    Code:
    Game::Game(HINSTANCE hInstance)
    {
    	MainInstance = hInstance;
    	Fullscreen = GetScreenMode();
    	bRunning = InitWindow();
    	if (bRunning) bRunning = InitD3D();
    	if (bRunning) bRunning = InitScene();
    }
    
    Game::~Game(void)
    {
    	KillScene();
    	KillD3D();
    	KillWindow();
    }
    
    bool Game::InitWindow(void)
    {
    	WNDCLASS WCL;
    	DWORD Style;
    
    	WCL.cbClsExtra = 0;
    	WCL.cbWndExtra = 0;
    	WCL.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    	WCL.hCursor = LoadCursor(NULL, IDC_ARROW);
    	WCL.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    	WCL.hInstance = MainInstance; //The only important thing about this function is that I'm using the Private Member that
    				//had the original Instance passed into it from the Main.cpp file. Is that okay to do?
    	WCL.lpfnWndProc = SWinFunc;
    	WCL.lpszClassName = "MyClass";
    	WCL.lpszMenuName = NULL;
    	WCL.style = CS_OWNDC;
    	if (!RegisterClass(&WCL)) return false;
    
    	if (Fullscreen)
    	{
    		Width = GetSystemMetrics(SM_CXSCREEN);
    		Height = GetSystemMetrics(SM_CYSCREEN);
    		Style = WS_POPUP;
    	}
    	else
    	{
    		Width = GetSystemMetrics(SM_CXSCREEN);
    		Height = GetSystemMetrics(SM_CYSCREEN);
    		Style = WS_OVERLAPPED|WS_SYSMENU; 
    	}
    	MainWindow = CreateWindow("MyClass", "RPG", Style, 0, 0, Width, Height, NULL, NULL, MainInstance, this);
    	if (!MainWindow) return false;
    
    	ShowWindow(MainWindow, SW_SHOW);
    	UpdateWindow(MainWindow);
    	SetFocus(MainWindow);
    	return true;
    }
    
    //My Two Message Handling Functions
    LRESULT CALLBACK Game::SWinFunc(HWND hWnd, unsigned int Msg, WPARAM wParam, LPARAM lParam)
    {
    	if (Msg == WM_NCCREATE)
    	{
    		LPCREATESTRUCT LPC = (LPCREATESTRUCT)lParam;
    		SetWindowLong(hWnd, GWL_USERDATA, (long)LPC->lpCreateParams);
    		SetWindowLong(hWnd, 0, 1);
    		return DefWindowProc(hWnd, Msg, wParam, lParam);
    	}
    	
    	Game *pMyGame;
    	if (GetWindowLong(hWnd, 0))
    	{
    		pMyGame = (Game *)GetWindowLong(hWnd, GWL_USERDATA);
    		return pMyGame->WinFunc(hWnd, Msg, wParam, lParam);
    	}
    
    	return DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    
    LRESULT CALLBACK Game::WinFunc(HWND hWnd, unsigned int Msg, WPARAM wParam, LPARAM lParam)
    {
    	switch(Msg)
    	{
    	case WM_KEYDOWN:
    		bRunning = false;
    		return 0;
    		break;
    	case WM_CLOSE:
    		bRunning = false;
    		return 0;
    		break;
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		return 0;
    		break;
    	}
    	return (DefWindowProc(hWnd,Msg,wParam,lParam));
    }
    It seems that my second Message Handler (the non-static one) never fires because if I press a key once the app is running, nothing happens. I think the reason behind my lock-ups is that bRunning is always true and the loop in Main.cpp starts going ridiculously quickly once the window is closed, and hence the lockup.

    I'm wondering if anyone sees something that's causing my Message Handler #2 not to be fired?

  9. #9
    Registered User
    Join Date
    Oct 2003
    Posts
    13
    I'm working on something exactly like this but I'm using a thunk class to redirect the calls to the proper WndProcs.

    Code:
    #pragma warning(push)
    #pragma warning(disable : 4355)
    #if defined(_M_IX86)
    #pragma pack(push, 1)
    template<typename W>
    class Thunk
    {
    	const DWORD		m_mov;		/* mov dword ptr [esp+0x4], m_this */
    	const W			*m_this;
    	const BYTE		m_jmp;		/* jmp WndProc */
    	const ptrdiff_t m_relproc;		/* relative jmp */
    public:
    	Thunk(WNDPROC proc, W *obj) :
    	m_mov(0x042444C7),
    	m_this(obj),
    	m_jmp(0xE9),
    	m_relproc((int) proc - ((int) this + sizeof(Thunk)))
    	{
    		::FlushInstructionCache(GetCurrentProcess(), this, sizeof(Thunk));
    	}
    
    	operator::WNDPROC() const
    	{
    		return(WNDPROC) (this);
    	}
    };
    #pragma pack(pop)
    #else
    #error Only X86 supported
    #endif
    #pragma warning(pop)
    Code:
    class Window
    {
    protected:
    	HWND m_hWnd; /* Window handle */
    	Thunk<Window> m_thunk; /* Our thunk object */
    	WNDPROC m_oldProc;
    	virtual LRESULT CALLBACK InternalWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
    	
    	static LRESULT CALLBACK wndProc(HWND pThis, UINT uMsg, WPARAM wParam, LPARAM lParam)
    	{
    		// This is where the correct MessageHandler is called
    		Window *p = (Window *) pThis;
    		return p->InternalWndProc(uMsg, wParam, lParam);
    	}
    
    
    public:
    	Window() : m_hWnd(NULL), m_thunk(wndProc, this), m_oldProc(NULL)
    	{
    
    	}
    	const HWND GetHWND(){ return m_hWnd; }
    
    #pragma warning(push)
    #pragma warning(disable : 4355)
    	HWND Create(DWORD dwExStyle, LPCTSTR pClassName, LPCTSTR pWindowName, DWORD dwStyle, int iX, int iY, int iWidth,
    		int iHeight, HWND pParentWindow, HMENU hMenu, 
    		HINSTANCE  hInstance, LPVOID  lpParam)
    
    	{
    		/* Create the window */
    		m_hWnd = CreateWindowEx(dwExStyle, pClassName, pWindowName, 
    			dwStyle, iX, iY, iWidth, iHeight, 
    			pParentWindow, hMenu, hInstance, lpParam);
    
    		if (m_hWnd) m_oldProc = windowProcedure(m_thunk);
    		return m_hWnd;
    	}
    
    
    	/* Return the current message handler */
    	WNDPROC windowProcedure () const
    	{
    		return reinterpret_cast < ::WNDPROC > (::GetWindowLong(m_hWnd, GWL_WNDPROC));
    	}
    
    	/* Set the message handler, returning the previous. */
    	::WNDPROC windowProcedure (::WNDPROC newProc)
    	{
    		return reinterpret_cast < ::WNDPROC > (::SetWindowLong(m_hWnd, GWL_WNDPROC, reinterpret_cast < ::LONG > (newProc)));
    	}
    #pragma warning(pop)
    
    };
    now all u have to do is derive your control from Window and code the InternalWndProc function. call RegisterClassEx using DefWindowProc as the default Window procedure. the Thunk will redirect it correctly.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Just starting Windows Programming, School me!
    By Shamino in forum Windows Programming
    Replies: 17
    Last Post: 02-22-2008, 08:14 AM
  2. Screwy Linker Error - VC2005
    By Tonto in forum C++ Programming
    Replies: 5
    Last Post: 06-19-2007, 02:39 PM
  3. WM_CAPTION causing CreateWindowEx() to fail.
    By Necrofear in forum Windows Programming
    Replies: 8
    Last Post: 04-06-2007, 08:23 AM
  4. Button positioning
    By Lionmane in forum Windows Programming
    Replies: 76
    Last Post: 10-21-2005, 05:22 AM
  5. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM