WndProc als Klassenmember, Fenster in Klasse erzeugen



  • Schönen guten Tag,
    gleich vorweg, nein dies wird kein "Wie packe ich eine WndProc in eine Klasse" - Thread...

    Auch wenn ich unheimlich lange danach gesucht habe, habe ich dann doch noch die Lösung gefunden.
    Das Umschreiben auf mein Vorhaben und Updaten auf 64er war auch kein Problem.

    Allerdings verstehe ich den Code nicht ganz vollständig, und ich möchte nur Code verwenden den ich auch verstehe. (sollte ja eigentlich für jeden selbstverständlich sein)

    Daher zwei, drei Fragen, aber zuvor der (Test-)Code (würde ich so nie implementieren, Thema Klassendesign, Stil...), daher bitte nur als Test ansehen:

    #include <iostream>
    #include <windows.h>
    #include <string>
    
    class Window { 
    public: 
        static bool Create;
    
    	HWND myHandle;
        std::string Text;
        int xpos, ypos;
    
        Window(int xpos, int ypos):xpos(xpos), ypos(ypos){
        }
    
        void init () { 
        	if(!Create){
            	Create=TRUE;
            	WNDCLASSEX wcx; 
            	wcx.cbClsExtra=0;
        		wcx.cbSize=sizeof(WNDCLASSEX);
        		wcx.cbWndExtra=0;
        		wcx.hbrBackground=CreateSolidBrush(RGB(255, 255, 255));		
        		wcx.hCursor=LoadCursor(GetModuleHandle(NULL),IDC_ARROW);
        		wcx.hIcon=LoadIcon(GetModuleHandle(NULL),NULL);
        		wcx.hIconSm=wcx.hIcon;
        		wcx.hInstance=GetModuleHandle(NULL);
        		wcx.lpfnWndProc=WndProc;
        		wcx.lpszClassName="Fenster";
        		wcx.lpszMenuName=NULL;
        		wcx.style=CS_VREDRAW;
    
        		if(!RegisterClassEx(&wcx)){
         		   	std::cout<<std::endl<<"-----Registrierung der Fensterklasse fehlgeschlagen!\n-----";
            		std::cin.get();
            		exit(0);
        		}
    
            }
    
            myHandle=CreateWindowEx(0, "Fenster", "Fenster", WS_VISIBLE|WS_OVERLAPPEDWINDOW, 
                                    	xpos, ypos, 500, 200, 0, 0, GetModuleHandle(NULL), 
                                    	this);  // Hier wird der Zeiger auf das erzeugte Objekt übergeben 
    
            if(myHandle==NULL){
        		std::cout<<std::endl<<"-----Fenster konnte nicht erzeugt werden!\n-----";
        		std::cin.get();
        		exit(0);
        	}   
        } 
    
        LRESULT CALLBACK MyWindowProc(UINT Message, WPARAM wParam, LPARAM lParam){
            switch (Message) { 
                case WM_DESTROY: { 
                	PostQuitMessage(0); 
                    break; 
                } 
                case WM_PAINT:{
                	PAINTSTRUCT ps;
         	       	HDC hdc=BeginPaint(myHandle, &ps);
    
    				TextOut(hdc, 50, 50, Text.c_str(), strlen(Text.c_str()));
    
         	       	EndPaint(myHandle, &ps);
         	    }return 0;
            } 
            // Nachrichten an Default-Windows-WndProc weiterreichen: 
            return ::DefWindowProc(myHandle, Message, wParam, lParam); 
       } 
    
        static LRESULT CALLBACK WndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam){ 
            if(Message == WM_CREATE) { // übergebenen this merken 
                ::SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)((CREATESTRUCT*)lParam)->lpCreateParams); 
            } 
    
            Window* pReceiver = (Window*)GetWindowLongPtr(hWnd, GWLP_USERDATA); 
    
            if (!pReceiver) {  // Windows-eigene WndProc aufrufen, falls kein Zeiger gespeichert ist 
                return ::DefWindowProc(hWnd, Message, wParam, lParam); 
            } 
            return pReceiver->MyWindowProc(Message, wParam, lParam); 
        } 
    };
    
    bool Window::Create=FALSE;
    
    int main(int argc, char** argv) {
    
    	Window a(500, 50);
    	Window b(700, 100);
    
    	a.Text="Test1";
    	b.Text="Test2";
    
    	a.init();
    	b.init();
    
    	MSG msg;
    	while(GetMessage(&msg, NULL, 0, 0) > 0) { 
    		TranslateMessage(&msg); 
    		DispatchMessage(&msg); 
    	}
    
    	return 0;
    }
    

    Ich habe das ganze jetzt als Konsolen-Anwendung, ist letztendlich ja nicht von Bedeutung.

    Zu meiner Frage, eigentlich ist es wohl nur SetWindowLongPtr() was mir Kopfzerbrechen bereitet. Damit wird der this, also der Zeiger auf das Objekt, abgespeichert und und kann über GetWindowLongPtr() wieder abgerufen werden? Aber wohin wird der abgespeichert? Und woher kommt der beim Aufruf von GetWindowLongPtr()?

    Und noch eine wohl triviale Frage, beim Aufruf der Proc` und von SetPtr was bedeuten die :: ?

    Ich hoffe es erbarmt sich jemand mir zu helfen. 🙂
    Schon im Voraus vielen Dank!
    MFG John.

    Edit: Falls es eine bessere/aktuellere/leichtere Variante gibt, ruhig bescheid sagen...



  • Wo der die Daten die du bei SetWindowLongPtr speicherst steht doch eigentlich hier: http://msdn.microsoft.com/en-us/library/windows/desktop/ms644898(v=vs.85).aspx
    Den Pointer bekommst du halt über dein Handle wieder zurück.

    Das mit den :: hat mit Namespaces zu tun. Ich bin mir nicht mehr 100% sicher, aber ich glaube dass du mit :: kennzeichnest, dass du auf den Globalnamespace zugreifen willst.



  • Hallo,
    und vielen Dank.

    Arcoth hatte auch schon gesagt, dass die :: das bedeuten (ich hatte erst den Thread im falschen Unterforum).

    Die MSDN ist die erste Adresse die man benutzt um über eine Funktion etwas zu erfahren, hat mir nur nicht wirklich weitergeholfen.

    Vielleicht habe ich die Frage nur falsch formuliert, als nochmal ganz konkret:
    Was bedeutet/macht diese Zeile?:
    ((CREATESTRUCT*)lParam)->lpCreateParams

    Schonmal Danke,
    MFG John.



  • Je nach Art des Events, werden in WPARAM und LPARAM verschiedene Typen übergeben.
    Im Beispiel von WM_MOUSEMOVE z.B. in LPARAM die Koordinaten des Zeigers.
    Man kann nicht einfach direkt x und y Werte als DWORD oder so übergeben, weil dann ja die Signatur für jedes Event anders wäre. Deswegen muss man die beidene Parameter je nach Event-Typ unterschiedlich casten.
    Im Fall von WM_CREATE ist in LPARAM ein Zeiger auf eine CREATESTRUCT. Um auf die Daten zuzugreifen, muss man den LPARAM uminterpretieren mithilfe des Casts, den du siehst, dann kann man auf den Member - in diesem Fall lpCreateParams - zugreifen.



  • Hallo. 👍
    Oh mein Gott, mein einziges Problem war, dass ich, warum auch immer, nicht daran gedacht habe, dass l/wparam unterschiedliche Bedeutung hat je nach Message...

    In der CREATESTRUCT werden die Daten von CreateWindowEx abgespeichert, also unter anderem auch als lpCreateParams der this.
    Und diesen speichert man durch SetWindowLongPtr in einem "Fenster-abhängigen-Speicher", holt sich den this durch GetWindowLongPtr und kann über den auf die eigene WndProc (die eine Memberfunktion ist) zugreifen.

    OK, Vielen Dank an euch beide, ich habe es nun begriffen...
    Schönes Wochenende,
    MFG John.


Log in to reply