Fensterfunktionen in eine Klasse packen
-
Jop, stimmt. Sowas bekommste aber schnell mit dem Debugger heraus.
Hab den Code mal korrigiert (auch n bissl das Design
-Sry-)
:#include <Windows.h> class CWindow { public: CWindow(const PTCHAR pszClass, const PTCHAR pszCaption, HINSTANCE hInst = GetModuleHandle(NULL)) { WNDCLASSEX wndclex = { 0 }; wndclex.cbSize = sizeof(WNDCLASSEX); wndclex.lpfnWndProc = CWindow::CommonWndProc; wndclex.hInstance = hInst; wndclex.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclex.hCursor = LoadCursor(NULL, IDC_ARROW); wndclex.hbrBackground = reinterpret_cast<HBRUSH>(NULL_BRUSH); wndclex.lpszClassName = pszClass; wndclex.hIconSm = wndclex.hIcon; RegisterClassEx(&wndclex); _hWnd = CreateWindowEx(0L, pszClass, pszCaption, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL); // Setzen des Zeigers: SetWindowLongPtr(_hWnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>(this)); } virtual int Start(int iShowCmd) { MSG msgApp; ShowWindow(_hWnd, iShowCmd); UpdateWindow(_hWnd); while(GetMessage (&msgApp, NULL, 0, 0)) { TranslateMessage (&msgApp); DispatchMessage (&msgApp); } return (msgApp.wParam); } protected: // Die ableitbare virtuelle WndProc: virtual bool WndProc (UINT uiMessage, WPARAM wParam, LPARAM lParam) { switch(uiMessage) { case WM_CLOSE: DestroyWindow(_hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return (false); } return (true); /* * Je nachdem, ob eine Nachricht behandelt wurde oder nicht, * wird ein anderer Wert zurückgegeben. */ } private: HWND _hWnd; /* * Die eigentliche CWindowProc, die nach dem Objekt sucht und * dessen virtuelle CWindowProc aufruft */ static LRESULT CALLBACK CommonWndProc (HWND hWnd, UINT uiMessage, WPARAM wParam, LPARAM lParam) { // Holen des Zeigers: CWindow* pThis = reinterpret_cast<CWindow*>(GetWindowLongPtr(hWnd, GWL_USERDATA)); if(pThis == NULL) return (DefWindowProc(hWnd, uiMessage, wParam, lParam)); /* * Je nach Rückgabewert wird entweder 0 zurückgegeben oder * DefCWindowProc aufgerufen. */ return pThis->WndProc(uiMessage, wParam, lParam) ? 0L : DefWindowProc(hWnd, uiMessage, wParam, lParam); } }; int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, char* pszCmdLine, int iShowCmd) { CWindow cAppWnd(TEXT("Fensterklasse"), TEXT("Fenstertitel"), hInst); return (cAppWnd.Start(iShowCmd)); }
-
Vielen Dank für die Hilfe.
Eine Frage hätte ich aber noch: Kennt jemand eine vernünftige Art der virtuellen WndProc, einen Wert zurückzugeben? Ich meine, das Problem ist ja folgendes: Nur, wenn die Nachricht nicht behandelt wurde, wird DefWindowProc aufgerufen. Wäre dem nicht so, könnte man in der statischen WndProc einfach folgendes schreiben (nach allen Abfragen und Vorarbeiten etc.):pThis->WndProc (uiMessage, wParam, lParam); return DefWindowProc (hWnd, uiMessage, wParam, lParam);(WndProc könnte hier den Rückgabewert void haben.) Aber da man DefWindowProc eben nur aufrufen soll, wenn die Nachricht nicht behandelt wurde, muß es so aussehen:
if (pThis->WndProc (uiMessage, wParam, lParam)) return 0; else return DefWindowProc (hWnd, uiMessage, wParam, lParam);Nun ist das ganze aber etwas blöd, wenn man eine Klasse ableitet und die virtuelle WndProc überschreibt. Würde man wie im ersten Fall vorgehen, könnte man folgendes schreiben:
void MyWindow::WndProc (...) { //Vorgeplänkel CWindow::WndProc (...); }Die Funktion ruft einfach die gleichnamige Funktion der Basisklasse auf, wie es bei dieser Art der Redefinition ja üblich ist. Doch nun, da ja der Rückgabewert wichtig ist und dieser auch von den Ergebnissen der eigenen Funktion abhängt, müßte man eigentlich bei jeder Ableitung folgendes schreiben:
bool MyWindow::WndProc (...) { bool processed=false; switch (msg) { case WM_Irgendwas: //Mach was processed=true; break; case WM_IrgendwasAnderes: //Mach was anderes processed=true; break; } return CWindow::WndProc (...) || processed; //Wurde eine Nachricht in der Basisfunktion oder hier behandelt? Dann gib true zurück! }Und diese Art der Rückgabe (es muß ja immer geguckt werden, ob irgendwo, hier oder in einer Basisklasse, die Nachricht behandelt wurde) finde ich etwas umständlich, unelegant und einfach nicht gut. Gibt es da eine bessere Möglichkeit?
-
Ich habe zwar keine Ahnung von WinAPI (da ich seit Jahren nur noch wxWidgets benutze und zufrieden bin) aber kannst du nicht sowas schreiben?
LRESULT CAbgeleitet::WndProc(HWND Wnd, unsigned int iMessage, WPARAM wParam, LPARAM lParam) { switch(iMessage) { case WM_WasWeisIch1: //... return DenPassendenReturnWert; case WM_WasWeisIch2: //... return DenPassendenReturnWert; case WM_WasWeisIch3: //... return DenPassendenReturnWert; } return CBasis::WndProc(Wnd, iMessage, wParam, lParam); } LRESULT CBasis::WndProc(HWND Wnd, unsigned int iMessage, WPARAM wParam, LPARAM lParam) { switch(iMessage) { case WM_WasWeisIch4: //... return DenPassendenReturnWert; case WM_WasWeisIch5: //... return DenPassendenReturnWert; case WM_WasWeisIch6: //... return DenPassendenReturnWert; } return DefWindowProc(Wnd, iMessage, wParam, lParam); } LRESULT CBasis::StatischeWndProc(HWND Wnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { if(!reinterpret_cast<CBasis*>(GetWindowLongPtr(Wnd, GWL_USERDATA))) { return DefWindowProc(Wnd, iMessage, wParam, lParam); } return reinterpret_cast<CBasis*>(GetWindowLongPtr(Wnd, GWL_USERDATA))->WndProc(Wnd, iMessage,wParam,lParam); }[EDIT]Fehler o_O[/EDIT]
-
Naja, das würde wohl gehen, aber was ist, wenn ich zu einer bereits behandelten Nachricht noch etwas hinzufügen will? In Deinem Fall beendet sich die abgeleitete WndProc sofort, nachdem die Nachricht verarbeitet wurde. Aber die Basisfunktion soll ja in jedem Fall immer mit aufgerufen werden. Nehmen wir an, ich möchte, bevor sich das Fenster schließt, noch eine Message-Box anzeigen. Dann wird WM_CLOSE sowohl in meiner abgeleiteten Funktion behandelt (Message-Box anzeigen), als auch in der Basisfunktion (Schließen durchführen). (Und ich will ja nicht den eigentlichen Schließvorgang komplett neu schreiben müssen, wenn ich zu WM_CLOSE einfach nur eine Aktion hinzufüge.)
Wie macht das eigentlich die MFC? Da ist es doch auch so, daß man jegliche Nachrichten abfangen kann und danach einfach die Nachrichtenfunktion der Basisklasse aufruft. (Es gibt sogar eine eigene, ableitbare WindowProc.) Und alle Funktionen sind void. Wie erkennt das Programm da, ob es in letzter Instanz noch die DefWindowProc aufrufen muß?
-
Ich wär's mal wieder. Ich hab jetzt ein neues Problem. Doch zuerst eine Frage: Wohin packt man den Aufruf, wenn man einen Button erstellen will? Gleich unter den Aufruf, der das Fenster erstellt hat oder in die WndProc unter WM_CREATE?
Mein Problem ist folgendes:#include <windows.h> class Window { public: Window () { //Das gleiche wie immer: Fenster registrieren //... window=CreateWindowEx (...); SetWindowLong (window, GWL_USERDATA, reinterpret_cast<LONG> (this)); } virtual int Start (int showCmd) { //... } protected: virtual bool WindowProc (UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { //Das hier funktioniert nicht: case WM_CREATE: button=CreateWindow ("Button", "Button", WS_CHILD|WS_VISIBLE, 0, 0, 100, 50, window, NULL, GetModuleHandle (NULL), NULL); break; //... } return true; } private: HWND window; /*static*/ HWND button; static LRESULT CALLBACK StaticWindowProc (HWND window, UINT message, WPARAM wParam, LPARAM lParam) { //Das hier würde funktionieren: /*if (message==WM_CREATE) button=CreateWindow ("Button", "Button", WS_CHILD|WS_VISIBLE, 0, 0, 100, 50, window, NULL, GetModuleHandle (NULL), NULL);*/ Window *thisWindow=reinterpret_cast<Window *> (GetWindowLong (window, GWL_USERDATA)); return thisWindow && thisWindow->WindowProc (message, wParam, lParam)?0: DefWindowProc (window, message, wParam, lParam); } }; /*HWND Window::button=NULL;*/ int WINAPI WinMain (HINSTANCE instance, HINSTANCE, LPSTR, int showCmd) { Window w; return w.Start (showCmd); }Wenn ich in den Button in der virtuellen WindowProc erstelle, funktioniert das nicht. Gehe ich direkt in die statische Funktion und mache das gleiche, funktioniert es. Wie kommt das? Die statische Funktion ruft doch die dynamische sowieso auf. Wenn ich es dagegen in den Konstruktor schreibe, funktioniert es wieder. Doch ich habe es so gelesen, als müsse es unter WM_CREATE stehen.
-
Schau dir den Ansatz mal an: http://turing.fh-landshut.de/~jamann/IMB/IMB.html
-
hola
haett da zu der klsse mal ne frage:
muss man nicht, wenn man mehrere von den window-klassen erstellt, jeweils einen neuen ClassName der RegisterClass uebergeben? ansonsten kann er die klasse ja nur einmal registrieren oder?
wenn man nun die klasse nur einmal registrieren kann, dann kann man auch den Style von einem Window nicht aendern, da sich die aenderung auf alle beziehen wuerde. stimmt das so ?Meep Meep
-
Jo clör.
Naja: Ein Objelt der Klasse entspricht halt einem Fenster.
-
gilt dann der ClassName nur fuer den jeweiligen process oder systemweit ?
ansonsten kanns doch mal vorkommen, das 2 unterschiedliche programme den gleichen ClassName benutzen.Meep Meep
-
Meep Meep schrieb:
gilt dann der ClassName nur fuer den jeweiligen process oder systemweit ?
ansonsten kanns doch mal vorkommen, das 2 unterschiedliche programme den gleichen ClassName benutzen.Meep Meep
systemweit<<
Jops, das kann passieren, es gibt aber auch UnregisterClass(...).
-
CodeFinder schrieb:
Meep Meep schrieb:
gilt dann der ClassName nur fuer den jeweiligen process oder systemweit ?
ansonsten kanns doch mal vorkommen, das 2 unterschiedliche programme den gleichen ClassName benutzen.Meep Meep
systemweit<<
Jops, das kann passieren, es gibt aber auch UnregisterClass(...).naja ob das so gut ist, wenn man eine fremde klasse unregistriert, nur weil sie den gleichen namen hat so wie mein ?
angenommen jemand verwendet mein programm und hat ein anderes bei sich laufen das den gleichen klassennamen hat. dann kann er mein programm einfach nicht verwenden?Meep Meep
-
Schau dir den Ansatz mal an: http://turing.fh-landshut.de/~jamann/IMB/IMB.html
Uff. Also, ich hatte eigentlich nicht vor, mich durch eine komplette Klassenbibliothek zu kämpfen. Mir ging es einfach nur darum, zu wissen, wieso ich in der eigentlichen statischen WndProc einen Button erzeugen kann, das aber nicht geht, wenn ich es in einer nicht-statischen Funktion mache und diese von der statischen aufrufen lasse.
Außerdem müßte ich wissen, ob es o.k. ist, in meinem Beispiel den Button im Konstruktor zu erzeugen oder ob das immer in der WndProc bei der Nachricht WM_CREATE geschehen muß.
-
NES-Spieler schrieb:
Außerdem müßte ich wissen, ob es o.k. ist, in meinem Beispiel den Button im Konstruktor zu erzeugen oder ob das immer in der WndProc bei der Nachricht WM_CREATE geschehen muß.
Kannst du auch im Konstruktor erstellen. Klar. Wenn zu dem Zeitpunkt ein gültiger Parent-Handle zur Verfügung steht.
-
Wieso wird das dann überhaupt in WM_CREATE erstellt und nicht (bei nicht-objekt-orientierter Programmierung) direkt in der WinMain?
-
Du kannst beies machen...aber denk daran das du in deiner WndProc meistens die Handle der Child-Wnd's brauchst...und globale Variablen sollte man vermeiden
.