Probleme bei WM_SIZE und eigener Fensterklasse



  • Hi,

    ich wollte mir eine eigene Fensterklasse schreiben, bin aber bei der Nachrichtenverarbeitung auf ein Problem gestoßen. Weil das mit der lpfnWndProc nicht gerade tolles OO-Design ist dachte ich mir, dass ich da das Strategie-Pattern verwende und man dem jeweiligen Window-Objekt ein "Verhaltens"-Objekt oder "Nachrichtenobjekt" übergibt, in dem dann die Nachricht individuell je nach gewünschten Objekt verarbeitet werden kann.
    Allerdings habe ich schnell festgelstellt, dass ich die GetMessage- Schleife nicht ohne Probleme anzapfen kann, weil es Nachrichten gibt, wie WM_SIZE die wohl nicht in den Nachrichtenpuffer eingetragen werden, sondern direkt an die lpfnWndProc(MessageHandler) Funktion geschickt werden.
    Also habe ich ein bischen rumgefrickelt und schicke in der lpfnWndProc über PostMessage die Nachricht WM_USER + WM_SIZE. Das funktioniert aber nur, beim ersten Aufruf von WM_SIZE während der Initialisierung des Fensters oder wenn ich in der lpfnWndProc Funktion bei WM_SIZE einen Breakpoint gesetzt habe 😕

    Vielleicht kann mir ja jemand sagen, warum das mit PostMessage und WM_USER nicht funktioniert oder weis eine elegantere Lösung für das Problem.

    Das zweite, dass mir aufgefallen ist, ist dass ich in der lpfnWndProc einen leeren Eintrag für WM_PAINT brauche, sonst wird mein WM_PAINT in meinem Nachrichtenobjekt nicht ausgeführt. Das wundert mich auch ein bischen. Wäre toll wenn mir jemand sagen könnte warum das so ist und wie man das umgehen kann.

    Danke schonmal für eure Antworten

    Dennis

    Hier der Code:

    // Window.h
    
    #ifndef WINDOW_DEF
    #define WINDOW_DEF
    
    #include <windows.h>
    #include <stdio.h>
    
    /*
    CWndProc
    
    Interface for the message handler class.
    
    use WM_USER + WM_SIZE instead of WM_SIZE
    */
    class CWndProc
    {
    public:
    	virtual int MessageHandler(MSG* msg) = 0;
    };
    
    class CWindow
    {
    private:
    	HWND		m_hWindow;				// Handle of the Window
    	HINSTANCE	m_hInstance;			// Handle of the Instance
    	wchar_t		m_awcClassName[256];	// Class Name of the Window
    
    	CWndProc*	m_pCWndProc;			// Pointer to the Message Handler
    
    	bool		m_bInit;				// indicates if the object is initialized
    
    public:
    	CWindow(void);
    	~CWindow(void);
    
    	bool Init(wchar_t* pcWindowName,  HINSTANCE hInstance,	// Register a WNCLASSEX structre und create a window.
    		CWndProc* pCWndProc);
    	bool Init(wchar_t* pcWindowName,  HINSTANCE hInstance,	// use the wndclassEx structure, except lpfnWndProc
    		CWndProc* pCWndProc, WNDCLASSEX wndClassEx);		// and hInstance
    
    	bool Exit(void);										// Unregister the class and destroy the window.
    
    	bool Run(void);										// run the message loop
    
    	bool SetWndProc(CWndProc* pCWndProc);				// set the message handler function
    
    	// inline methods
    	HWND GetHWND(void)	{return m_hWindow;}
    };
    
    #endif
    
    // Window.cpp
    
    #include "Window.h"
    
    LRESULT CALLBACK MessageHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    
    	switch(msg)
    	{
    	case WM_DESTROY:PostQuitMessage(0);return 0; break;
    	case WM_PAINT:
    		{
    			// do nothing
    		}
    		return 0;break;
    	case WM_COMMAND:
    		{
    			PostMessage(hWnd, WM_USER + WM_COMMAND, wParam, lParam);
    		}
    		return 0;break;
    	case WM_SIZE:
    		{
    			//SetWindowText(hWnd, L"sdfdsfdsfd");
    			PostMessage(hWnd, WM_USER + WM_SIZE, wParam, lParam);
    		}
    		return 0;break;
    	}
    
    	return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    
    CWindow::CWindow(void)
    {
    	ZeroMemory(this, sizeof(CWindow));
    }
    
    CWindow::~CWindow(void)
    {
    	Exit();
    }
    
    bool CWindow::Init(wchar_t *pcWindowName, HINSTANCE hInstance, CWndProc* pCWndProc)
    {
    	WNDCLASSEX	window;
    
    	if(m_bInit)			return false;
    	if(!pcWindowName)	return false;
    	if(!pCWndProc)		return false;
    
    	if(!SetWndProc(pCWndProc)) return false;
    
    	swprintf_s(m_awcClassName,256, L"%sWndClassName", pcWindowName);
    	m_hInstance = hInstance;
    
    	window.cbSize			= sizeof(WNDCLASSEX);
    	window.cbClsExtra		= NULL;
    	window.cbWndExtra		= NULL;
    	window.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH+1);
    	window.hCursor			= LoadCursor(NULL, IDC_ARROW);
    	window.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
    	window.hIconSm			= LoadIcon(NULL, IDI_APPLICATION);
    	window.hInstance		= hInstance;
    	window.lpfnWndProc		= MessageHandler;
    	window.lpszClassName	= m_awcClassName;
    	window.lpszMenuName		= NULL;
    	window.style			= CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    
    	if(!RegisterClassEx(&window)) return false;
    
    	m_hWindow = CreateWindowEx(0, m_awcClassName, pcWindowName, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
    		0,0,400,400,NULL,NULL,hInstance,NULL);
    
    	if(!m_hWindow) 
    	{
    		UnregisterClass(m_awcClassName, m_hInstance);
    		return false;
    	}
    
    	m_bInit = true;
    	return true;
    }
    
    bool CWindow::Init(wchar_t* pcWindowName,  HINSTANCE hInstance,
    		CWndProc* pCWndProc, WNDCLASSEX wndClassEx)
    {
    	WNDCLASSEX	window;
    
    	if(m_bInit)			return false;
    	if(!pcWindowName)	return false;
    	if(!pCWndProc)		return false;
    
    	if(!SetWndProc(pCWndProc)) return false;
    
    	swprintf_s(m_awcClassName,256, L"%s", wndClassEx.lpszClassName);
    	m_hInstance = hInstance;
    
    	window.cbSize			= wndClassEx.cbSize;
    	window.cbClsExtra		= wndClassEx.cbClsExtra;
    	window.cbWndExtra		= wndClassEx.cbWndExtra;
    	window.hbrBackground	= wndClassEx.hbrBackground;
    	window.hCursor			= wndClassEx.hCursor;
    	window.hIcon			= wndClassEx.hIcon;
    	window.hIconSm			= wndClassEx.hIconSm;
    	window.hInstance		= hInstance;
    	window.lpfnWndProc		= MessageHandler;
    	window.lpszClassName	= m_awcClassName;
    	window.lpszMenuName		= wndClassEx.lpszMenuName;
    	window.style			= wndClassEx.style;
    
    	if(!RegisterClassEx(&window)) return false;
    
    	m_hWindow = CreateWindowEx(0, m_awcClassName, pcWindowName, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
    		0,0,400,400,NULL,NULL,hInstance,NULL);
    
    	if(!m_hWindow) 
    	{
    		UnregisterClass(m_awcClassName, m_hInstance);
    		return false;
    	}
    
    	m_bInit = true;
    	return true;
    }
    
    bool CWindow::Exit(void)
    {
    	if(!m_bInit) return true;
    
    	if(m_hWindow)
    	{
    		DestroyWindow(m_hWindow);
    		m_hWindow = NULL;
    		UnregisterClass(m_awcClassName, m_hInstance);
    	}
    
    	if(m_pCWndProc)
    	{
    		delete m_pCWndProc;
    		m_pCWndProc = NULL;
    	}
    
    	m_bInit = false;
    	return true;
    }
    
    bool CWindow::Run(void)
    {
    	MSG msg;
    
    	if(!m_bInit)	return false;
    
    	//while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    	while(GetMessage(&msg, NULL, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    
    		if(WM_USER + WM_SIZE == msg.message)		// for testing
    		{
    			int iBreakpoint = 0;
    		}
    
    		m_pCWndProc->MessageHandler(&msg);
    	}
    	return true;
    }
    
    bool CWindow::SetWndProc(CWndProc* pCWndProc)
    {
    	if(!pCWndProc)	return false;
    
    	// if a previous m_pCWndProc exists it will deleted
    	if(m_pCWndProc) delete m_pCWndProc;
    
    	m_pCWndProc = pCWndProc;
    
    	return true;
    }
    
    // main.cpp
    
    #include "Window.h"
    
    class TestWndProc : public CWndProc
    {
    	int x;
    public :
    
    	TestWndProc(void)
    	{
    		x = 160;
    	}
    
    	int MessageHandler(MSG* msg)
    	{
    		switch(msg->message)
    		{
    		case WM_PAINT:
    			{
    				PAINTSTRUCT ps;
    				HDC hDC = BeginPaint(msg->hwnd, &ps);
    
    				Rectangle(hDC, 110, 40, x, 80);
    
    				EndPaint(msg->hwnd, &ps);
    			}
    			return 0; break;
    		case WM_USER + WM_SIZE:
    			{
    				SetWindowText(msg->hwnd, L"Größe");
    				x++;
    			}
    			return 0;break;
    		case WM_LBUTTONDOWN:
    			{
    				MessageBox(NULL,L"Maus",L"Links", MB_OK);
    			}
    			return 0; break;
    		case WM_RBUTTONDOWN:
    			{
    				MessageBox(NULL,L"Maus",L"Rechts", MB_OK);
    			}
    			return 0; break;
    		case WM_MOUSEMOVE:
    			{
    				wchar_t aus[100];
    				swprintf(aus, 100, L"x: %i  y: %i", LOWORD(msg->lParam), HIWORD(msg->lParam));
    				SetWindowText(msg->hwnd, aus);
    			}
    			return 0; break;
    		}
    		return 0;
    	}
    };
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
    {
    	CWindow window;
    	CWndProc* t = new TestWndProc();
    
    	if(!window.Init(L"Test",hInstance,t)) return 1;
    
    	window.Run();
    
    	return 0;
    }
    

    P.S
    Ich weis, ist nicht gerade gut komentiert 😃


  • Mod

    Dein Ansatz funktioniert nicht, weil Du in keiner Weise verstanden hast (und auch nicht nachgelesen hast) wie Windows Nachrichten funktionieren.

    1. Gibt es Nachrichten von Eingabemedien (Tastatur/Maus), diese werden die die MessageQueue eingereicht undverarbeitet. Diese selbst lösen meistens dutzende weitere Nachrichten aus, die dann direkt an die betroffenen Fenster ausgeliefert werden. (Ausnahme Nachrichten, die über Threadgrenzen gesendet werden).
    Und es gibt natürlich Nachrichten die explizit per PostMessage in der Queue landen.
    2. Gibt es Nachrichten die Grundsätzlich nie (außerbei überschreiten von Threagernzen) in der Nachrichtenschleife landen. Das sind alle Nachrichten die üblicherweise per SendMessage direkt ausgeliefert werden. Das ist der Großteil von Nachrichten.
    3. Gibt es virtuelle Nachrichten, die erzeugt werden, wenn Nachrichten abgeholt werden (WM_PAINT, WM_TIMER, ect.) Diese werden nur erzeugt, wenn eine Nachricht abgeholt wird aus der Queue und direkt zugestellt.

    Vergiss Deinen Ansatz und nimm bestehende Ansätze die eine Fensterproc in eine Klasse packen. Diese findest Du auch in der FAQ.



  • Hi,

    in der FAQ, zumindest in der für die WinAPI, hatte ich schon gekuckt, bevor ich gefragt hatte und habe ich nichts zu dem Thema gefunden. Wo also finde ich die bestehende Ansätze?




  • Mod

    Meister_Faust schrieb:

    Hi,

    in der FAQ, zumindest in der für die WinAPI, hatte ich schon gekuckt, bevor ich gefragt hatte und habe ich nichts zu dem Thema gefunden. Wo also finde ich die bestehende Ansätze?

    Du hast nicht die Doku zu SendMessage/PostMessage/GetMessage bzw. den simplen About in der MSDN gelesen.
    http://msdn.microsoft.com/en-us/library/ms644927(VS.85).aspx

    Da steht das meiste drin, was ich geschrieben habe.

    BTW: Wie kommt man da hin?
    1. Du liest die Doku zu SendMessage/GetMessage
    2. Du schast auf den Contents links und siehst es geht um "Messages and Message Queues"
    3. Du klickst das Überthme an und siehst sofort das "About..."



  • @Belli:

    Das Framework sieht gut aus, wird mir bestimmt weiterhelfen. Danke für den Tipp.

    @Martin:

    Ich werds mir durchlesen.


Anmelden zum Antworten