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
-
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?
-
Guck mal hier:
http://users.bigpond.net.au/programming/
-
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).aspxDa 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..."
-
Das Framework sieht gut aus, wird mir bestimmt weiterhelfen. Danke für den Tipp.
Ich werds mir durchlesen.