Wie die Callback Routine in Klasse integrieren?



  • Außerdem: Wie soll ich wissen wenn du nur schreibst "'ProcWndMsg': Ist kein Element von 'CWndEvents'", dass du CWndUnknown in CWndEvents umbenannt hast? das kommt bei dir ein posting später. Willst du Beiträge sammeln oder uns hier verarschen?
    Sry aber das regt mich grad ziemlich auf was du hier abziehst... man hilft dir, du verwendest den Code völlig falsch und machst dir nicht einmal die mühe, selbst EINMAL noch über deinen Code zu fliegen. So wirst du hier schnell keine freunde mehr haben, mir reichts nun.



  • Entschuldige mal, du musst ja nicht gleich beleiddigend werden. Der Code ist bereits stark abgespeckt. In andern Sektionen dieses Forums hiess es eher das man mal etwas mehr zeigen soll um Zusammenhänge sehen zu können.

    Ich habe bereits deine Variante ausprobiert und sie funktionierte (was ich auch bereits weiter oben beschrieben hatte). Nur wollte ich halt eine Variante wo ich direkt von CEngine auf die Routine zugreifen kann und nicht von der abgeleiteten Klasse. Aus diesem Grund habe ich diese auch nach CEngine verlegt, sonst wäre sie ja doppelt deklariert.



  • kokunze schrieb:

    Entschuldige mal, du musst ja nicht gleich beleiddigend werden.

    Gleich?

    Der Code ist bereits stark abgespeckt.

    lange nicht auf WESENTLICHE

    In andern Sektionen dieses Forums hiess es eher das man mal etwas mehr zeigen soll um Zusammenhänge sehen zu können.

    am besten du postest noch dein ganzes Projekt mit resource files, compiler-spezifische header files etc um die Zusammenhänge zu zeigen 🙄 .

    Ich habe bereits deine Variante ausprobiert und sie funktionierte (was ich auch bereits weiter oben beschrieben hatte).

    wo?

    Nur wollte ich halt eine Variante wo ich direkt von CEngine auf die Routine zugreifen kann und nicht von der abgeleiteten Klasse. Aus diesem Grund habe ich diese auch nach CEngine verlegt, sonst wäre sie ja doppelt deklariert.

    wie ich bereits sagte: lerne erst die basics und komm dann wieder. Informier dich am besten auch über das Wort "virtual". Dann wirst du sehen dass du schwachfug postest.



  • Hier:

    kokunze schrieb:

    also hab das versucht, allerdings meldet er jetzt immer:

    'ProcWndMsg': Ist kein Element von 'CWndEvents'

    Wenn ich diese Methode mit als Mitglied von der Unknown Klasse mache geht es, allerdings kann ich dann nicht von dieser auf die Variablen von CEngine zugreifen.

    ich wollte lediglich ein wenig Hilfe wie ich das ganze so umschreiben kann, das ich von CEngine darauf zugreifen kann. Was du jetzt hier für Hasstiraden loslässt ist mir schleierhaft. Ich dachte das ist ein Forum zum gegenseitigen Helfen und nicht im sich zuzuflamen.



  • Worauf willst du von CEngine zugreifen? Deine Sätze ergeben keinen Sinn.
    Außerdem was soll das "Hier"? Du quotest dich selbst ohne Bezug.
    Und wo sagtest du, dass mein code funktioniert? diesen quote schuldest du mir immernoch.
    Natürlich kannst du aus ProcessWindowMessage auf die Variablen von CEngine zugreifen, dazu hast du es ja VIRTUAL deklariert 🙄 .
    LRESULT CEngine::ProcessWindowMessage(blabla)
    {
    // blablablabla hier kannst du die message behandeln
    }
    Und wo du hasstiraden siehst ist mir - wie du sagtest - auch schleierhaft.
    Das war mein letzter post zu diesem Thema, cu.



  • Ich quote mich von Seite 1:

    LRESULT CEngine::ProcessWindowMessage(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
    {
        switch(uiMsg)                // nach Nachrichtentyp unterscheiden
        {
            case WM_KEYDOWN:
            {
                switch(wParam)
                {
                    case VK_ESCAPE:
                    {                   
                        DestroyWindow(hWnd);
                        break;
                    }
                }
    
            }
    
        }    // ~switch(uiMsg);
    
        // wurde kein passender Nachrichtentyp gefunden, Nachricht an Windows weiterleiten
        return DefWindowProc(hWnd, uiMsg, wParam, lParam);
    }
    

    kannst du mir jetzt mal bitte erklären warum du nicht von CEngine auf .... was auch immer zugreifen kannst?????



  • mit "hier" war gemeint das ich da bereits geschrieben hab, das es in besagter Variante läuft.

    "Wenn ich diese Methode mit als Mitglied von der Unknown Klasse mache geht es, allerdings ..."

    Ich hab es inzwischen hinbekommen durch überschreiben der Methode in der abgeleiteten Klasse. Problem ist allerdings das ich nun das Hauptprogramm nicht mehr kompilieren kann, weil besagtes ja in einer Klasse einer DLL war. Muss das nun also erstmal irgendwie umschreiben das der Starter weiß, das er die abgeleitete Klasse zum initialisieren aufrufen muss. Aber das ist ein anderer Schuh...



  • kokunze schrieb:

    mit "hier" war gemeint das ich da bereits geschrieben hab, das es in besagter Variante läuft.

    "Wenn ich diese Methode mit als Mitglied von der Unknown Klasse mache geht es, allerdings ..."

    warum lässt du das allerdings weg?
    "Wenn ich diese Methode mit als Mitglied von der Unknown Klasse mache geht es, allerdings kann ich dann nicht von dieser auf die Variablen von CEngine zugreifen."
    Genau das ist schlicht und einfach falsch, weshalb man daraus nicht ableiten kann das es geht. Vielmehr suggerierst du damit dass man es compilen kann (das heißt noch lange nicht dass es klappt), was du damit bekräftigst, dass du zuvor den compiler error aufführst, wenn du das nicht so machst.
    Nochmal: du kannst, wenn du es machst so wie ich es sage, auf Membervariablen und Memberfunktionen deiner Klasse zugreifen. Und wenn du das gegenteilige behauptest, scheint es doch nicht zu gehen, oder? 🙄

    Ich hab es inzwischen hinbekommen durch überschreiben der Methode in der abgeleiteten Klasse.

    was hast du nun im vergleich zu meinem code geändert? der funktioniert würdest du nicht alles ändern wollen ohne zu wissen was du da tust.

    Problem ist allerdings das ich nun das Hauptprogramm nicht mehr kompilieren kann, weil besagtes ja in einer Klasse einer DLL war. Muss das nun also erstmal irgendwie umschreiben das der Starter weiß, das er die abgeleitete Klasse zum initialisieren aufrufen muss. Aber das ist ein anderer Schuh...

    für mich redest du wirres zeugs.



  • Ich habe keine Lust weiter mit dir zu diskutieren, der geänderte Quelltext interessiert dich ja eh nicht und auf ein Niveau von Beleidigungen brauch ich mich nicht herablassen. Da das Hauptprogramm die veränderte DLL nicht mehr akzeptiert hat und dieses wieder zusätzlichen Aufwand erzeugt hätte, hab ich nun die Nase voll und eine einfachere Methode gefunden.

    Alles beim alten wie ganz am Anfang der hreaderstellung und dann einfach die Methode static deklarieren und schon gibts keine Probleme mehr.



  • kokunze schrieb:

    Alles beim alten wie ganz am Anfang der hreaderstellung und dann einfach die Methode static deklarieren und schon gibts keine Probleme mehr.

    Aber wie greifst Du dann auf die Membervariablen zu ? Das geht doch innerhalb einer statischen Methode nicht ?!?



  • Ich brauch ja nur statische Variablen, es gibt eh nur genau eine Instanz der Klasse. Das müsste doch gehen?

    Allerdings bekomm ich nun immer nen Linkerfehler:

    error LNK2001: Nicht aufgelöstes externes Symbol ""private: static bool CEngine::m_bRunning" (?m_bRunning@CEngine@@0_NA)

    Die Klasse:

    class __declspec(dllexport) CEngine
    {
    public:
    ...
    
    private:
    
    	// Callback Routine für Windows-Nachrichten
    	static LRESULT CALLBACK WinEvents(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
    
    	static bool		m_bRunning;		// Gibt an, ob die Anwendung noch läuft
    
    ...
    
    };	// ~class CEngine;
    

    Die Methode:

    LRESULT CALLBACK CEngine::WinEvents(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
    {
    	switch(uiMsg)				// nach Nachrichtentyp unterscheiden
    	{
    
     ...
    
    		case WM_DESTROY:
    		{
    			m_bRunning = false;
    			break;
    		}
    
    	}	// ~switch(uiMsg);
    
    	// wurde kein passender Nachrichtentyp gefunden, Nachricht an Windows weiterleiten	
    	return DefWindowProc(hWnd, uiMsg, wParam, lParam);
    }
    


  • Ach ich habs grad selber, hab die Definition vergessen...



  • kokunze schrieb:

    Ich brauch ja nur statische Variablen, es gibt eh nur genau eine Instanz der Klasse. Das müsste doch gehen?

    So geht's natürlich immer. 🙂



  • kokunze schrieb:

    Ich habe keine Lust weiter mit dir zu diskutieren, der geänderte Quelltext interessiert dich ja eh nicht und auf ein Niveau von Beleidigungen brauch ich mich nicht herablassen.

    Du bist der einzige, der beleidigt. Gerade aufgrund solchen Unterstellungen.



  • Black Shadow schrieb:

    kokunze schrieb:

    Ich habe keine Lust weiter mit dir zu diskutieren, der geänderte Quelltext interessiert dich ja eh nicht und auf ein Niveau von Beleidigungen brauch ich mich nicht herablassen.

    Du bist der einzige, der beleidigt. Gerade aufgrund solchen Unterstellungen.

    wtf? *psychoattacke!!!!!!*

    bin mal Faul zum lesen, was vorher im Thread so schönes geschah, vllt. bringe ich jetzt die lösung zu nem schon gelösten problem, aber ich bin müde und leicht angetrunken - ich darf das .. Es ging doch darum, den WndProc in ne Klasse zu hauen.
    Ist doch eigentlich ganz einfach - da man dem lpfnWndProc einer WNDCLASSEX instanz nur statische Wndprocs zuweisen kann, muss man halt einen statischen Wndproc zuweisen. Und um dabei trotzdem auf die membervariablen einer einzelnen instanz zugreifen zu können, baut man halt einen nicht-statischen member-WndProc(), der dann für die einzelnen instanzen jeweils vom statischen WndProc aufgerufen wird...

    Jetzt darf das HÄ? kommen.

    So siehts aus:

    zuerst muss man in der Klasse 2 "WndProc()"s deklarieren - einen Statischen, den sich alle Instanzen teilen. und einen nichts-statischen, der dann zugriff auf die membervariablen einer Instanz hat. Ich deklarier meistens beide als private...

    ....
    static LRESULT WINAPI Static_Proc(HWND, UINT, WPARAM, LPARAM);
    LRESULT WINAPI WndProc(HWND, UINT, WPARAM, LPARAM);
    ....
    

    bevor ich jetzt zur definition der beiden hübschen komme, muss ich erstma einen coolen Trick zeigen, der Trick ist so cool, du wirst umfallen...

    na gut, zuerst kommt die WNDCLASSEX instanz, dann der Trick.

    So - wcex.lpfnWndProc will ja nen statischen WndProc, den soll er/sie/es bekommen:

    ....
    wcex.lpfnWndProc	= Static_Proc;
    ....
    

    dann kommen wir zum Trick, und zwar ist das der Letzte Parameter von
    CreateWindowEx(). muhaha. denn das, was man hierhin schreibt, landet im lParam der WM_CREATE Nachricht beim WndProc. Was kommt jetzt also hierrein? Natürlich der this-Zeiger. (jetzt mal kräftigst nachgucken, was der this Zeiger ist^^) bzw - der this Zeiger ist ein Zeiger auf das aufrufende Objekt, also in diesem Fall der Zeiger auf unsere Fensterklassen-instanz. So sieht das dann ungefähr aus:

    hWnd = CreateWindowEx(NULL, wcex.lpszClassName,
    		TEXT("Blablabla"), WS_CHILD | WS_CLIPSIBLINGS | WS_BILLGATES, //lol 
    		0, 0, 0, 0, Parent, NULL, 
    		wcex.hInstance,(LPVOID) this);
    

    das fenster ist jetzt natürlich net so schön^^, aber hauptsache der letzte Parameter ist der this zeiger (zuvor zu LPVOID konvertiert^^)

    Das war jetzt der göttliche Trick. Nun kommen wir zum Static_Proc. Ich glaub, ich bin sogar so faul, dass ich den einfach ma irgendwo klaue

    LRESULT WINAPI FensterKlasse::Static_Proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    	if(msg == WM_CREATE)
    	{
    		SetWindowLong(hWnd,GWL_USERDATA,(LONG) lParam);
    	}
    
    	static FensterKlasse *pThis;
    	pThis = (FensterKlasse*) GetWindowLong(hWnd, GWL_USERDATA);
    
    	if(pThis == 0)
    	{
    		return DefWindowProc(hWnd, msg, wParam, lParam);
    	}
    	else
    	{
    		return pThis->WndProc(hWnd, msg, wParam, lParam);
    	}
    }
    

    SetWindowLong schreibt in den Speicher vom Fenster oder sowas. keine lust, das genau zu definieren, da müsste ich den Petzold aufschlagen. Auf jeden Fall wird in Fensterspeicher unterm Index GWL_USERDATA (man kann auch 0 schreiben, wenn man zuvor so 4 byten zusätzlichen speicher reserviert hat) der Zeiger auf die jeweilige Instanz gespeichert (natürlich nur bei WM_CREATE, sonst gäbs keinen Sinn). Dann kann man nen Zeiger auf eine FensterKlasse Instanz sehen, in welche der Wert wieder ausgelesen wird. k.a warum ich das jetzt grad statisch hab, wahrscheinlich weil das so schön blau ist ^^""". dann gibts noch ne prüfung auf gültigkeit des Zeigers (hey, cool da kann man ein Assert reinmachen xDDDDD) und wenn alles stimmt, dann wird der normale WndProc der Instanz aufgerufen. Ich glaub, zu dem muss ich nix sagen, der sieht aus wie ein ganz normaler WndProc() und verarbeitet die messages auch bestens. So fertig.

    Man, bin ich gut^^". Bin halt ein göttlicher, größenwahnsinniger, schlauer, eingebildeter, arroganter, netter, hilfsbereiter und brutaler 16-jähriger, der euch alle ma versklaven wird ^^ (nachdem ich microsoft gekauft hab, natürlich^^). Und jeder, der sich mir wiedersetzt wird erstma in assembler eine 3d-engine schreiben müssen^^. so - genug herumgelabere xDDDDDD. Glaub, bin viel zu gut drauf xDDD



  • @azrael: genau das macht mein code, nur übernimmt das SetWindowLong-Zeugs bei mir alles die abgeleitete Klasse, sodass man noch weniger beachten muss.



  • aha, mist, hätte ich doch lieber lesen sollen xDDDDDDDDDDDDDD



  • Danke Azrael, genau sowas hab ich gesucht! Habs dank deiner Erklärung nu auch verstanden. Da brauch ich nicht etxra irgendwelche Klassen ableiten.



  • Also bisher funktionierte das Ganze wunderbar. Allerdings ist mir nun ein Problem aufgefallen, irgendwie schein ich nicht richtig auf das Obejtk zugreifen zu können.

    Ich zeig mal den Quelltext:
    die statische Callback Routine, wie ich sie von dir hab:

    LRESULT WINAPI CEngine::StatEvents(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
    {
    	static CEngine *pThis; 
    
    	if(uiMsg == WM_CREATE) 
    	{ 
    		SetWindowLong(hWnd, GWL_USERDATA, (LONG)lParam); 
    	} 
    
    	pThis = (CEngine*)GetWindowLong(hWnd, GWL_USERDATA); 
    
    	if(pThis == 0) 
    	{ 
    		return DefWindowProc(hWnd, uiMsg, wParam, lParam); 
    	} 
    	else 
    	{ 
    		return pThis->WinEvents(hWnd, uiMsg, wParam, lParam); 
    	} 
    
    }	// ~CEngine::StatEvents();
    

    meine Fenster-gebundene Callback Routine

    LRESULT WINAPI CEngine::WinEvents(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
    {
    	switch(uiMsg)				// nach Nachrichtentyp unterscheiden
    	{
    ...
    		case WM_DESTROY:
    		{
    			// Elternfenster eine Nachricht schicken
    			SendMessage(m_hParentWnd, WM_USER+1, 0, 0);
    			m_bRunning = false;
    
    			break;
    		}
    	}	// ~switch(uiMsg);
    	return DefWindowProc(hWnd, uiMsg, wParam, lParam);
    }	// ~CEngine::WinEvents();
    

    Im Hauptprogramm existiert nur ein Objekt der Klasse. Dieses hat eine Referenz des Elternfensters in m_hParentWnd zwischengespeichert und ändert dieses auch nicht. Wenn ich allerdings innerhalb dieser Callback Routine darauf zugreife, ist da ein völlig anderer Wert in dem Handle. Sprich die Nachricht kommt nicht beim Elternfenster an.

    Wie kann ich das beheben?

    Hier noch kurz die wichtigen Teile der restlichen Implementation ...
    in der Klasse:

    class __declspec(dllexport) CEngine : public IEngine
    {
    public:
        bool	InitEngine(HINSTANCE hInst, HWND &hParentWnd, CParameters params, CNoiseData nData);	
    ...
    private:
    ...
    	HINSTANCE	m_hInstance;	// Handle der startenden Instanz
    	MSG			m_msgEvent;		// Nachrichten Objekt des Hauptfensters
    	HWND		m_hWnd,			// das Fenster Handle
    				m_hParentWnd;	// Referenz auf das Eltern-Fenster Handle
    
    	// Callback Routine für Windows-Nachrichten
    	static LRESULT WINAPI StatEvents(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
    	LRESULT WINAPI WinEvents(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
    };	// ~class CEngine;
    

    beim Starten der Anwendung

    bool CEngine::InitEngine(HINSTANCE hInst, HWND &hParentWnd, CParameters params, CNoiseData nData)
    {
    	// Daten- und Raumobjekt sowie Instanz-Handle übernehmen
    	m_hInstance	= hInst;
    	m_Param		= params;
    	m_Data		= nData;	
    	m_hParentWnd = hParentWnd;
    
    	// Fenster erzeugen
    	if (InitWindow(hParentWnd) == false)
    	{
    		m_bRunning = false;
    		return false;
    	}
    ...
    }	// ~CEngine::InitEngine();
    

    beim Anlegen des Fenster

    bool CEngine::InitWindow(HWND hParentWnd)
    {
    	WNDCLASSEX	wndStruct;	// Struktur der Fenster-Eigenschaften
    	// Eigenschaften des Fensters festlegen
    	wndStruct.hInstance		= m_hInstance;				// Die erstellende Instanz (Prozess)
    	wndStruct.lpszClassName = "test";		// Name des Fensters
    	wndStruct.lpfnWndProc	= StatEvents;				// Callback Routine für Ereignisse
    ...
    	if (!RegisterClassEx(&wndStruct))
    	{
    		ERRORMSG("Fenster-Klasse konnte nicht registriert werden!","InitWindow();");
    		return false;
    	}
    
    	m_hWnd = CreateWindowEx(0, "test", "test", WS_OVERLAPPEDWINDOW,
    							200, 100, m_Config.GetScreenWidth(), m_Config.GetScreenHeight(), 
    							hParentWnd, NULL, m_hInstance, this);	
    
    ...
    }	// ~CEngine::InitWindow();
    


  • Och kommt ... was isn das fürn Code ... wofür ist bsw CEngine static? Und C-Style-Casts kenn ich auch nicht ^^ C++ nimmt für sowas dynamic, reinterpret oder static cast 🙂

    LRESULT WINAPI CEngine::stat_events(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	if (message == WM_CREATE)
            SetWindowLong(hWnd, GWL_USERDATA, lParam);
    
    	CEngine* pThis = reinterpret_cast<CEngine*>(GetWindowLong(hWnd, GWL_USERDATA));
    
    	return (pThis == NULL ? DefWindowProc(hWnd, message, wParam, lParam) : pThis->window_events(hWnd, message, wParam, lParam))
    }
    

    ist das selbe wie du da stehen hast in Kurzfassung.

    HWND &hParentWnd
    

    Referenz ist bei HWND nicht üblich.

    Naja usw ... korrigier mal den Rest auch noch ...


Anmelden zum Antworten