Win32 Callback als NICHT-Statische Elementfunktion



  • @reima
    Ich halte die dort beschriebene Vorgehensweise für ziemlich "dirty". Vermutlich ist das nichtmal Compiler portabel. Zudem versteh ich nicht so ganz, wofür das sein soll? MFC? Dort steht zwar was von Win32, aber normal haben in WinAPI die Nachrichtenfunktionen für Fenster und Dialoge 4 Parameter und nicht 2.
    Schau mal hier, das ist eine deutlich saubere Lösung.



  • reima schrieb:

    ........ schrieb:

    Es geht nur als static.

    Ich zitiere aus der Microsoft FAQ:

    M$ FAQ schrieb:

    Meist reicht das aber nicht aus. Hilfe bietet der Parameter, den Win32 für die meisten Callback-Funktionen anbietet, oft ein LPARAM oder LPVOID, der weitergegeben wird und den die Anwendung für eigene Zwecke benutzen kann. In diesem Fall übergeben wir einen this-Zeiger auf die eigene Instanz. Die bei dieser Lösung notwendigen unschönen Typumwandlungen reduzieren wir mit folgender relativ eleganten Lösung auf ein Minimum; dabei wird eine rohe statische Hilfsfunktion als technischer Callback verwendet, welche die Typumwandlung leistet und die eigentliche nicht-statische Arbeitsfunktion aufruft.

    gruss reima

    gut das du mich nochmal bestätigt hast. 😉



  • Ich halte die dort beschriebene Vorgehensweise für ziemlich "dirty".

    So wie es dort in dem Beispiel auf der FAQ-Seite gemacht wurde ist es gut. Nur kann man das nicht direkt auf die WndProc übertragen. Dort befindet sich nicht immer der Dialog Zeiger in lParam.



  • ............ schrieb:

    So wie es dort in dem Beispiel auf der FAQ-Seite gemacht wurde ist es gut. Nur kann man das nicht direkt auf die WndProc übertragen. Dort befindet sich nicht immer der Dialog Zeiger in lParam.

    Und woher weisst du, dass this genau auf lParam gemapped wird? Mag vielleicht beim MSC so sein, bei anderen Compilern sieht das aber uU ganz anders aus. Und WinAPI kann man mit mehr als nur dem MSC nutzen. Wie ein Zeiger auf eine Memberfunktion intern aussieht, ist nunmal compiler-abhängig. Seihe dazu auch "Implementations of Member Function Pointers" in diesem Artikel.



  • wc.lpfnWndProc = MenuWndProc;
        //...
        hMenu_ = CreateWindowEx(WS_EX_TOOLWINDOW, TEXT("bs_menu"), 0, WS_POPUP, 0, 0, 100, 100, 0,    0, 0, (CREATESTRUCT*)this))
    

    und wie mac ich den das bei der CreateDialog, DialogBox funktion???

    k.a. aber ich weiss nicht wo diesen Pointer übergeben...

    CreateDialog(hInst,MAKEINTRESOURCE(DialogNr),hWnd,RohDialogProc);
    

    ein kleinen tipp, respektive pseudoe code wäre sehr nett, danke

    gruss reima



  • hmm...

    CreateDialogParam(..., (LPARAM)this)
    

    sollte gehn, kanns aber gerade nicht testen 😕



  • k1ro schrieb:

    hmm...

    CreateDialogParam(..., (LPARAM)this)
    

    sollte gehn, kanns aber gerade nicht testen 😕

    achso du nimmst dafür CreateDialogParam 😉

    danke erstmals komischerweise stürzt das programm immer noch ab:

    //Datei: CDialog.cpp
    #include "CDialog.h"
    
    // E X T E R N  D E K L A R I E R T /////////////////////////////////////////////////////////////
    
    /////////////////////////////////////////////////////////////////////////////////////////////////
    
    // K o n s t r u c k t o r e n /////////////////////////////////////////////////////////////////
    CDialog::CDialog(HINSTANCE hInst,HWND hWnd,int DialogNr,bool IsModal)
    {
    	Init(hInst,hWnd,DialogNr,IsModal);
    	count = 0;
    
    }
    
    //Sandard Konstruktor(Leere Klasse)
    CDialog::CDialog()
    {
    	count = 0;
    }	
    ////////////////////////////////////////////////////////////////////////////////////////////
    
    //Destruktor////////////////////////////////////////////////////////////////////////////////
    CDialog::~CDialog(){}
    ////////////////////////////////////////////////////////////////////////////////////////////
    
    //Initialisiert Die DialogBox//////////////////////////////////////////////////////////////
    void CDialog::Init(HINSTANCE hInst,HWND hWnd,int DialogNr,bool IsModal)
    {
    
    	//Modalless//
    	if(IsModal == 0)
    
    		hDlg  = CreateDialogParam(hInst,MAKEINTRESOURCE(DialogNr),hWnd,RohDialogProc,
    									(LPARAM)(this));
    
    	else
    	{
    		DialogBoxParam(hInst,MAKEINTRESOURCE(DialogNr),hWnd,RohDialogProc,(LPARAM)(this));
    		hDlg = 0;
    	}
    }
    ////////////////////////////////////////////////////////////////////////////////////////
    
    void CDialog::SetData(UINT message,WPARAM wParam,LPARAM lParam)
    {
    
    	msg.resize(msg.size()+1);
    	wPar.resize(msg.size()+1);
    	lPar.resize(msg.size()+1);
    
    	msg[count] = message;
    	wPar[count]= wParam;
    	lPar[count]= lParam;
    
    	count++;
    }
    
    //Prüfung der Nachrichten/////////////////////////////////////////////////////////////////
    bool CDialog::CheckMSG(UINT Status,WPARAM wParam,LPARAM lParam)
    {
    
    	/*
    	for(int x = 0; x < count; x++)
    	{
    
    		if((Status == msg[x])||(msg[x]== 0))
    		{
    			if((wPar[x] == 0)||(wParam == wPar[x]))
    			{
    				if((lPar[x] == 0)||(lParam == lPar[x])){MessageBox(0,"gut","gut",0);return 1;}
    			}
    		}
    	}*/		
    	return 0;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////	
    
    //Die Callback funktion der Dialogbox///////////////////////////////////////////////////////////
    long CDialog::DialogProc(HWND hDlg,UINT message,WPARAM wParam, LPARAM lParam)
    {
    
    	switch(message)
    	{
    
    	case WM_INITDIALOG:
    
    		return 1;
    
    	case WM_CLOSE:
    		EndDialog(hDlg,0);
    		return 0;
    
    	case WM_DESTROY:
    		PostQuitMessage(0);
    
    	default:
    		CheckMSG(message,wParam,lParam);
    	}
    	return (0);
    }
    /////////////////////////////////////////////////////////////////////////////////////////////////
    CALLBACK CDialog::RohDialogProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
    {
    
    	CDialog* wnd = (CDialog*)GetWindowLong(hWnd,GWL_USERDATA);
    	switch(message)
    	{
    	case WM_INITDIALOG:
    		wnd  = (CDialog*)((CREATESTRUCT*)lParam)->lpCreateParams;
    		SetWindowLong(hWnd,GWL_USERDATA,(long)wnd);
    		break;
    
    	}
    
    	return wnd->DialogProc(hWnd,message,wParam,lParam);
    }
    

    die CALLBACK wurde hier in der klasse als Static deklariert.

    sobald ich irgendwas von der Klasse benutze in der CheckMsg (sei es Varialben, oder Funktionen) stürtz das Programm ab sobald das erste mal diese Funktion aufgeruft wird.

    mich würde wirlich interssieren was ich jetzt noch falsch gemacht habe, danke im voraus gruss reima



  • groovemaster schrieb:

    ............ schrieb:

    So wie es dort in dem Beispiel auf der FAQ-Seite gemacht wurde ist es gut. Nur kann man das nicht direkt auf die WndProc übertragen. Dort befindet sich nicht immer der Dialog Zeiger in lParam.

    Und woher weisst du, dass this genau auf lParam gemapped wird? Mag vielleicht beim MSC so sein, bei anderen Compilern sieht das aber uU ganz anders aus. Und WinAPI kann man mit mehr als nur dem MSC nutzen. Wie ein Zeiger auf eine Memberfunktion intern aussieht, ist nunmal compiler-abhängig. Seihe dazu auch "Implementations of Member Function Pointers" in diesem Artikel.

    Es wird doch nur ein Zeiger auf ein Objekt übergeben und in der Callback Funktion wieder in den Original-Typ zurückgecastet.
    Ich seh da nichts mit Memberfunktion.



  • irgenwie stürtz es "nur" ab wenn in der klasse global deklarierte Variablen (public, private) verwendent werden, komisch. amsonsten läuft es ohne Fehler, liegt da irgend ein zugriffverletzung vor, oder "findet" die dialog Proc funktion die Variablen nicht?

    gruss reima



  • ............. schrieb:

    Es wird doch nur ein Zeiger auf ein Objekt übergeben und in der Callback Funktion wieder in den Original-Typ zurückgecastet.
    Ich seh da nichts mit Memberfunktion.

    Yep, hast Recht. Hatte den Artikel nur schnell überflogen, und etwas anders verstanden. Naja, praktikabel ist es trotzdem nicht, da in lParam je nach Message irgendwas drin stehen kann. Die einzige Konstante ist das Fenster Handle, und damit der vermutlich einfachste Weg. Und GetWindowLongPtr/SetWindowLongPtr hilft einem, einen benutzer-spezifischen Zeiger zu verwalten.

    @reima
    Diese Funktion könnte ein Problem sein

    CALLBACK CDialog::RohDialogProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
    {
    
        CDialog* wnd = (CDialog*)GetWindowLong(hWnd,GWL_USERDATA);
        switch(message)
        {
        case WM_INITDIALOG:
            wnd  = (CDialog*)((CREATESTRUCT*)lParam)->lpCreateParams;
            SetWindowLong(hWnd,GWL_USERDATA,(long)wnd);
            break;
    
        }
    
        return wnd->DialogProc(hWnd,message,wParam,lParam);
    }
    

    Ähnliches hab ich selbst schon mal gemacht, allerdings mit einer Nachrichtenfunktion für ein Fenster und nicht für einen Dialog. Dort war es dann so, dass nicht WM_CREATE bzw WM_NCCREATE die erste Message war (wie man vielleicht denken könnte), sondern noch 1 oder 2 Messages davor kamen. Ein ähnliches Problem könnte es bei Dialogen geben. Insofern würde wnd bei der ersten Message null werden und beim Member Zugriff

    return wnd->DialogProc(hWnd,message,wParam,lParam);
    

    gibts dann undefiniertes Verhalten. Ist jetzt nur so eine Idee. Du solltest aber mal testen, ob vor WM_INITDIALOG noch andere Messages kommen.



  • groovemaster schrieb:

    gibts dann undefiniertes Verhalten. Ist jetzt nur so eine Idee. Du solltest aber mal testen, ob vor WM_INITDIALOG noch andere Messages kommen.

    da kommen nicht nur 1-2 Nachrichten rein vor WM_INITDIALOG sondern ca. +100 Nachrichen, also ganz eine Menge, doch wie fange ich die am Besten ab?? Am besten wäre wenn ich von der funktion aus neue nachrichten anfordern lasse könnte, doch leider habe ich keine Ahung wie diese Funktion heisst(falls sie existiert)
    dann könnte ich dies in einer While schleife überprüfen bis WM_INITDIALOG kommmt

    danke für jede hilfe

    gruss reima



  • gibt es keine Api Funktion mit welcher ich "Nachrichten" aus der DialogProc Anfordern kann, schlussendlich sollte die DialogProc solange auf nachrichten waren bis die Nachricht WM_INITDIALOG kommt, bin für jede Hilfe sehr dankbar

    gruss james



  • if(wnd)
    {
        return wnd->DialogProc(hWnd,message,wParam,lParam);
    }
    else
    {
       return FALSE;
    }
    

    oder so ähnlich



  • hmm extrem strange:

    jetzt kommen die nachrichten überhaubt nicht mehr ( nur noch wm_initdialog)
    Damit meine ich das keine Nachrichen mehr zur Funktion CheckMSG(UINT,WPARAM,LPARAM) gelangen

    CALLBACK CDialog::RohDialogProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
    {
    
    	static int x = 0;
    	CDialog* wnd;
    	LPCREATESTRUCT lcs;
    
    	wnd = (CDialog*)GetWindowLong(hWnd,GWL_USERDATA);
    	lcs = (LPCREATESTRUCT)lParam;
    
    	SetWindowLong(hWnd,GWL_USERDATA,(long)wnd);
    
    	if(wnd)
    	{
    
    		return wnd->DialogProc(hWnd,message,wParam,lParam);	
    	}
    	else
    	{
    
    		if(message == WM_INITDIALOG)
    		{
    			wnd  = (CDialog*)(lcs->lpCreateParams);
    			return 1;
    		}
    
    		return FALSE;
    	}
    
    }
    
    long CDialog::DialogProc(HWND hDlg,UINT message,WPARAM wParam, LPARAM lParam)
    {
    
    	switch(message)
    	{
    	case WM_INITDIALOG:
    		return 1;
    
    	case WM_CLOSE:
    		EndDialog(hDlg,0);
    		return 0;
    
    	case WM_DESTROY:
    		PostQuitMessage(0);
    
    	default:
    		CheckMSG(message,wParam,lParam);
    
    	}
    	return 0;
    }
    

    danke schon im voraus für jede hilfe

    thx

    gruss reima



  • reima schrieb:

    da kommen nicht nur 1-2 Nachrichten rein vor WM_INITDIALOG sondern ca. +100 Nachrichen, also ganz eine Menge, doch wie fange ich die am Besten ab??

    machst du einfach ein flag in die dlgproc (static!!), das mit 'false' initialiseirt wird und wenn dann WM_INITDIALOG ankommt setzt du das flag einfach auf 'true'.

    long CDialog::DialogProc (....)
    {
       static bool flag = false;
    
       if (message == WM_INITDIALIOG)
          flag = true;
    
       if (flag == false)
       {
           // messages vor WM_INITDIALIOG 
       }
       else
       {
          // messages nach (und einschliesslich) WM_INITDIALIOG 
       }
    }
    


  • keine Ahnug aber irgenwie bin ich unfähig.

    habs jetz so eingebaut wie es net gesagt hat:

    CALLBACK CDialog::RohDialogProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
    {
    
    	static bool flag = false;
    	CDialog* wnd;
    	LPCREATESTRUCT lcs;
    
    	wnd = (CDialog*)GetWindowLong(hWnd,GWL_USERDATA);
    
    	if(message ==WM_INITDIALOG)
    		wnd  = (CDialog*)(lcs->lpCreateParams);
    		flag = true;
    
    	if(flag == false)
    	{
    
    		return FALSE;	
    	}
    	else
    	{
    		lcs = (LPCREATESTRUCT)lParam;
    		SetWindowLong(hWnd,GWL_USERDATA,(long)wnd);
    		return wnd->DialogProc(hWnd,message,wParam,lParam); 				
    	}
    
    }
    

    aber nun kackt es wieder ab sobald die Dialogbox erstellt worden ist...
    woran kann das liegen???

    gruss reima



  • der zeiger steht in LPARAM.



  • reima schreib den code nochmal komplett neu.



  • @reima
    Mach den Vorschlag von net wieder raus, das ist absoluter Unsinn. Dieses "Flag" hast du ja schon mit GWL_USERDATA. Benutze übrigens GetWindowLongPtr/SetWindowLongPtr/GWLP_USERDATA statt GetWindowLong/SetWindowLong/GWL_USERDATA, falls GWL_USERDATA ein Zeiger ist (was bei dir der Fall ist).

    Ansonsten würde ich es erstmal mit folgender Funktion probieren

    CALLBACK CDialog::RohDialogProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
    {
        CDialog* wnd = (CDialog*)GetWindowLong(hWnd,GWL_USERDATA);
        switch(message)
        {
        case WM_INITDIALOG:
            wnd  = (CDialog*)((CREATESTRUCT*)lParam)->lpCreateParams;
            SetWindowLong(hWnd,GWL_USERDATA,(long)wnd);
            break;
    
        }
    
        if (wnd)
            return wnd->DialogProc(hWnd,message,wParam,lParam);
        else
            return FALSE;
    }
    

    edit:
    Man sollte halt nicht soviel copy&pasten ohne nachzudenken. 😃



  • DefDlgProc darf man nicht dort aufrufen.


Anmelden zum Antworten