WinAPI Wrapper: Messages
-
Hallo!
ich will gerade das WinAPI-Message-System etwas wrappen. Dabei muss man eine Funktion erstellen. Rückgabetyp ist void und Parameter ist (leider) ein HWND. Dann macht man noch eine Const int ID und übergibt die ID und die Funktionsadresse einer Bind-Funktion. Diese packt das einfach nur in eine std::map, key ist die ID, value der Funktionszeiger:// Deklaration static std::map<const int, void (*)(iZWindow)> _evList; // static wegen der statischen WndProc // Bekommt ID und Funktionsadresse und fügt ein void iZWindow::BindAction(const int ID, void (*fptr)(iZWindow)) { iZWindow::_evList[ID] = fptr; }Soweit sogut. z.B. will man jetzt ein Menu erstellen. Das Menu hat eine eigene Klasse, aber am Ende, wenn man es in das Fenster ausgeben will muss man Fensterklasse.SetMenu(Menubarklasse); aufrufen:
iZMenubar menu; iZPopup popup(100); iZPopup popup2(200); popup.AppendMenu(105, "Drucken\tCTRL+P"); // Popups mit Einträgen füllen popup.AppendMenu(111, "Beenden\tALT+F4"); popup2.AppendMenu(666, "Ausschneiden"); menu.AppendMenubar(popup, "Datei"); // Popups in die Menubar menu.AppendMenubar(popup2, "Bearbeiten"); wnd.SetMenu(menu); // Menubar in das Fenster-Objekt wndDie Funktion zur Menuerstellung sollte also besser die Fensterklasse und nicht HWND als Parameter besitzten, ich rufe ja schließlich wnd.SetMenu(menu) auf.
Zu dumm, dass ja dann am Ende WndProc die Aufgabe hat, die Funktion bei der übergebenen ID als Message aufzurufen und dabei ihr einen Parameter zu übergeben. WndProc bietet aber nur das HWND:LRESULT CALLBACK iZWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { for(std::map<const int, void (*)(iZWindow)>::const_iterator iter = iZWindow::_evList.begin(); iter != iZWindow::_evList.end(); iter++) { // event-map auslesen if((*iter).first == WM_CREATE) { // wenn key (=message) == WM_CREATE iter->second(hWnd); // value (=funktionszeiger) aufrufen und Parameter übergeben } } // usw...Mir wäre lieber, ich könnte das Fensterobjekt übergeben, um HWND zu wrappen, aber davon weiß WndProc nichst, da diese Funktion static ist, da gibts kein this.

Wie kann WndProc vom Fensterobjekt erfahren, es ist eine statische Methode der Klasse.
-
Wieso baust Du dir die Message-Loop nicht einfach selbst und startest sie innerhalb einer Member-Funktion deiner Klasse?
void iZWindow::run () { while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg); switch (msg.message) { // ... } } }
-
Wie kommst du auf void? Es muss doch LRESULT zurückgegeben werden?!
-
Indem Du den Zeiger auf Deine Klasse in GWL_USERDATA reinpackst.
Suchmal im Netz danach.
Oder Du baust einen Thunk wie die ATL, die die Fensterfunktion wrappt...Warum verwendest Du nicht die ATL/MFC/WTL?
-
Ad aCTa schrieb:
Wie kommst du auf void? Es muss doch LRESULT zurückgegeben werden?!
Das soll doch keine
WndProc-Funktion darstellen, sondern irgend eine (nicht-statische) Memberfunktion, die irgendwo im Code aufgerufen werden kann.
-
An Martin Richter:
Danke, dein Stichwort "GWL_USERDATA" hat mein Message-Handling-Problem gelöst, hoffe ich. WndProc bleibt bei mir eine statische Memberfunktion der Fensterklasse, damit ich Zugriff auf die Attribute behalte.
Die relevanten Codestellen:// this, das Fensterobjekt an WM_CREATEs LPARAM übergeben _hWnd = CreateWindowEx(0, _appName, _appName, WS_OVERLAPPEDWINDOW, x, y, width, height, NULL, NULL, _appInfo._hInstance, this); // ... LRESULT CALLBACK iZWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_CREATE: { // Adresse der Klasse aus dem LParam über CREATESTRUCT abholen LPCREATESTRUCT cs = (LPCREATESTRUCT)(lParam); if(!cs->lpCreateParams) { MessageBox(hWnd, "An unexpected error occured passing the window object.", "Runtime Error", MB_ICONERROR | MB_OK); SendMessage(hWnd, WM_CLOSE, wParam, lParam); } else { // In einen Long-Pointer die gecasteten Klassenadresse schreiben und mit jeder Message versenden SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)(cs->lpCreateParams)); } Adresse abholen, casten und in lokales Objekt lasen iZWindow *lpWndHandle = (iZWindow*)(GetWindowLongPtr(hWnd, GWLP_USERDATA)); // benutzerdefinierte Funktion, gespeichert in einer std::map aufrufen lpWndHandle->_evList[WM_CREATE](*lpWndHandle); return 0; } case WM_CLOSE: PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, message, wParam, lParam); }Lässt sich wunderbar kompilieren, keine RT-Errors. Erstklassig.

Die MFC verwende ich nicht, weil ich mich wirklich mit Win beschäftigen will
Die schnöde API durchzugehen ist zu langweilig, dann kapsele ich sie als Übungszweck lieber gleich in Klassen.
Außerdem will ich momentan nicht noch mehr Geld für C++ ausgeben, die Bücher sind schon teuer genug.
Ein anderes Problem:
Was ich jetzt produziert habe, ist am Ende doch recht schlechtes C++. Der Nutzer der Klasse muss Funktionen oder statische Member erstellen, um Messages zu bearbeiten, das ist nicht gerade schönes OOP.Ich habe überlegt, eventuell für alle Messages virtuelle Methoden in der Window-Klasse anzulegen, für WM_CREATE einfach virtual void OnCreate(), die werden dann bei Standard-Messages aufgerufen. In der Window-Klasse werden in den Definitionen der Message-Methoden nichts drinstehen, aber sie sind ja virtuell und können von Subklassen erweitert werden, darauf will ich hinaus, das man seine eigene Fensterklasse erstellt, von der Hauptklasse erbt und die Message-Funktionen implementiert, die man braucht, der Rest bleibt leer und DefWindowProc() macht den Rest.
Ist das "schöner"?
Wenn ich so überlege, Attribute der Windowklasse wie hWnd, message, windowclass usw. müssen ja dann auch in den Subklassen verfügbar sein ---> also sind die protected. Das ist auch nicht schön.
Gibt's da eine schöne Möglichkeit?
@ devkid
Ja, entschuldige, ich habe die Augen (oder das Gehirn) zu der Uhrzeit nicht mehr richtig koordinieren können.
-
Ad aCTa schrieb:
Ja, entschuldige, ich habe die Augen (oder das Gehirn) zu der Uhrzeit nicht mehr richtig koordinieren können.
selbes gilt auch für mich jetzt, also kann sich was in sachen rechtschreibung
einschleichen.in meiner windowsklasse habe ich das ganze ebenfalls wie windows durch ein call-
back gelöst, statt durch vererbung.besonders gefallen (was ic auch aktuell verwende ) hat mir:
struct Params { UINT msg; WPARAM wParam; LPARAM lParam; LRESULT retcode; bool changed; // gibt an, ob man den returncode gesetzt hat oder ob man DefWindowProc den rest machen lässt }; void Func(Window *w, Params *p);Window hat verschiedene methoden über die man an das handle z.b. kommt.
die emthode mit der map eignet sich für einen keyhandler ganz gut.wenn dir der returncode egal ist, kannst du auch eine std::queue verwalten,
die deine eigene nachrichtenschleife istvon der fensterklasse ableiten finde ich persönlich nicht so schön
-
> von der fensterklasse ableiten finde ich persönlich nicht so schön
Funktioniert merkwürdigerweise auch bei mir nicht richtig, die WNDCLASSEX lässt sich registieren, (im Basisklassenkonstruktor, der von der Subklasse aufgerufen wird), aber CreateWindowEx scheitert. Naja, suche bessere Vorschläge.

Mir gehts einfach nur darum, dass der Nutzer nicht prozedural, sondern (auch) objektorientiert programmieren kann, wie es in richtigem C++ auch eine gute Mischung der Paradigmen ausmacht. wxWidgets hat an der Stelle keine schöne Lösung, finde ich.