Definition von BOOL CALLBACK MyDlgProc(...) innerhalb einer Klasse? [erledigt]



  • Hallo allerseits!
    ich hab hier ein problem mit der definition einer BOOL CALLBACK DlgProc() funktion, die einen dialog steuert, innerhalb einer klasse...

    stark vereinfacht sieht es so aus: ich hab eine klasse, die ein paar wichtige schnittstellen initialisieren soll, und dafür evtl einen dialog starten muss, damit der benutzer alles einstellen kann. Die callback fktion muss immer auf member variablen der classe zugreifen können. ich hab es in etwa so versucht:

    Das .hpp:

    #include .... //alles nötige, hiermit gibts 100% kein problem
    
    class MyClass{
    public:
       int m_iVeryImportantVariable;
    
       //constructoren destruktoren, und anderes wichtiges zeug
    
       //initialisierungsfunktion, die den dialog aufruft:
       bool Init();
    
       //hier des problem: dlgProc als memberfunktion= warum nicht??
       BOOL CALLBACK MyDlgProc(/*die ganzen standartparameter*/); 
    };
    

    und dann des eigentliche .cpp quellcode:

    #include //alles nötige
    
    bool MyClass::Init(){
         //aufrufen des dialogs
         DialogBox(GetModuleHandle(NULL),
                   MAKEINTRESOURCE(IDD_MYDIALOG),
    	      NULL,
    	      this->MyDlgProc);
                   //hier gibts stress, aber ich weis nicht warum :(
         //sonstige tolle sachen
    }
    
    BOOL CALLBACK MyClass::MyDlgProc(/*standartparameter*/){
    
       int x=m_iVeryImportantVariable; //zugriff auf die membervariable
                          //spricht jetzt doch nichts dagegen, hoffe ich...
    
       switch(msg){
          case WM_BLA:{ //total wichtiges zeug tun
          }
       }
       return TRUE;
    }
    

    Wenn ich sowas kompiliere, bekomme ich ne fehlermeldung, dass der rückgabetyp der funktion nicht gecastet werden kann und zwar von:
    'int (struct HWND__ *,unsigned int,unsigned int,long)'
    in:
    'int (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)'

    Das eigentlich verwirrende ist, dass der fehler NICHT auftritt, wenn ich die callback funktion AUSSERHALB der klasse deklariere und definiere... 😕

    Kann mir bitte irgendjemand erklären, wo ich hier einen fehler mache?
    Vielen dank im voraus, mfg Andrey



  • Hallo,

    der Compiler erwartet eine Funktion mit der Aufrufkonvention __stdcall.
    Wenn Du die Funktion als Membermethode in einer Klasse haben willst, musst Du sie static deklarieren.



  • nummer 3 die woche steht das nicht in der FAQ?



  • @miller_m: kann sein, aber ich wusste irgendwie nicht so wirklich, wonach ich da suchen musste 🙄

    @cosmixx: dann kann ich die doch gleich ausserhalb der klasse deklarieren, mit einer statischen methode kann ich doch nicht auf den momentanen wert einer membervariable eines konkreten objektes zugreifen... (oder irr ich mich da etwa 😮 ??)

    wie soll ich denn dann überhaupt irgendwelche variablen dieser DlgProc() übergeben, ohne gleich alles global zu machen? sry, ist möglicherweise schon 1000 mal beantwortete frage... ich guk in den faq's nochma nach, falls da was ist, markir ich den ganzen thread mit [erledigt]

    schon mal danke für die tipps! 👍



  • Du kannst einen this-Pointer mit

    SetWindowLongPtr( windowhandle, GWLP_USERDATA, reinterpret_cast< LONG_PTR >( this ) );
    

    direkt im Fenster speichern, und dann in der static definierten MyDlgProc( ) darüber auf die member der Klasse zugreifen:

    INT_PTR MyClass::DialogProc( HWND window, unsigned int message, unsigned int wParam, long lParam )
    {
        MyClass *instance = reinterpret_cast< MyClass* >( GetWindowLong( window, GWLP_USERDATA ) );
    }
    

    Greetz, Swordfish



  • @swordfish: coole sache, danke 👍 😃
    ich wär niemals auf sowas gekommen, aber naja, übung macht den meister :p
    allerdings hab ich in meiner dokumentation weder irgendwas über SetWindowLongPtr() noch irgendetwas über diesen LONG_PTR gefunden, und, ehrlich gesagt, verwirren mich diese ganzen long's LONG's DWORD's nur, und wozu man für jeden pointer gleich neue datentypen erstellt raff ich auch nich, muss mich noch dran gewöhnen... 😞

    in meiner dokumentation hab ich so ähnliche methoden gefunden:

    SetWindowLong(hDlg, DWL_USER, (reinterpret_cast<LONG>(this)) );
    MyClass *myObject=reinterpret_cast<MyClass*>(GetWindowLong(hDlg, DWL_USER));
    

    damit muss es aber sicherlich auch gehen, hoffe ich doch, zuerst muss ich mir aber überlegen, wie ich meinen quellcode jetzt umstrukturier...

    vielen vielen dank nochma!



  • neeein, alles quatsch was ich da geschrieben hab 😞

    ach, swordfish, ich mach des einfach 1:1 wie du des beschrieben hast, irgendwie hab ich mich selbst mit den ganzen pointern total verwirrt, sry 😮 dank nochma...



  • Also funktioniert's? (Hm, sollte eigentlich - ist schließlich von _mir_ 😃 😃 😃 )

    Greetz, Swordfish[url]



  • @swordfish:

    Die idee ist großartig 😃 , klappt auch wunderbar, da ist nur ein kleines detail zu beachten... Und zwar: letztendlich hab ich des ganze genau so hingeschrieben, wie ich des in meinem vorletzten beitrag vorgeschlagn habe (mittlerweile hab ich des ganze chaos mit pointer behoben und code korrigiert).
    Wie gesagt, es klappt fast, so wie es da steht, wenn man GetWindowLong() direct in die DlgProc() einbaut... die DlgProc() bekommt den LONG, castet's zu einem pointer und erhält dadurch zugriff auf die membervariablen, allerdings wird diese funktion aus irgendeinem grund gleich nach "CreateDialog()" mindestens fünfzig mal aufgerufen, bevor der pointer übergeben wird, und erhält einfach "0" für den LONG wert, was zu einem absturz führt... :p
    deswegen muss man die ganze DlgProc() in eine if(){}-abfrage reinquetschen, damit es nur dann zu irgendwelchen operationen kommt, wenn der echte pointer erhalten wurde. dann klappt's 😃 👍

    vielen dank für die hilfe!
    mfg Andrey



  • Zweig das mal bitte in Codeform.

    Greetz, Swordfish



  • MyClass.hpp:

    #include <iostream>
    #include <windows.h>
    
    class MyClass{
    public:
       int m_iMyMemberVar;//variable, die im dialog benötigt wird
    
       BOOL Init();//funktion die das dialog aufruft
       static BOOL CALLBACK MyDlgProc(HWND hDlg, 
                                      MSG msg,
                                      WPARAM wParam,
                                      LPARAM lParam);
       //DlgProc, die zugriff auf m_iMyMemberVar benötigt
    };//MyClass
    

    MyClass.cpp:

    #include <windows.h>
    #include <iostream>
    #include "MyClass.hpp"
    #include "resource.h"
    
    #ifndef MYCLASS
    #define MYCLASS
    
    //--------------------------INIT-----------------------
    BOOL MyClass::Init(){
        //benutzereingaben erforderlich=dialog erstellen
        HWND hDlg=CreateDialog(GetModuleHandle(NULL),
                               MAKEINTRESOURCE(IDD_MYDIALOG),
                               NULL,
                               this->MyDlgProc);
    
        //this-pointer nach LONG casten und dem dialog übergeben:
        SetWindowLong(hDlg, DWL_USER, reinterpret_cast<LONG>(this) );
    
        //was auch immer initialisieren etc...
    }//Init()
    
    //-------------------------DlgProc---------------------
    BOOL CALLBACK MyClass::MyDlgProc(HWND hDlg,
                                     MSG msg,
                                     WPARAM wParam,
                                     LPARAM lParam){
    
       MyClass *pMyObject;
       pMyObject=reinterpret_cast<MyClass*>(GetWindowLong(hDlg,DWL_USER));
       //LONG zurück zum pointer casten  
    
       if(pMyObject!=0){ //15:18 korrektur vorgenommen: originalcode ersetzt
    
           int i= pMyObject->m_iMyMemberVar;//zugriff auf membervariablen
    
           //switch(msg)... dialog steuern
       }
       return FALSE;
    }//MyDlgProc()
    
    #endif
    

    hier, bitte schön...
    würde in den "Wie krigt man WndProc() in eine klasse"-FAQ gut passn...



  • du kannst auch schon vorher mit crateDialogParam den this-zeiger übergeben und dann in der dlgproc unter WM_CREATE SetWindowLong aufrufen, dann kommst du früher an die instanzadresse



  • @maxi: thx fürn tipp, ist auch ne möglichkeit 🙂
    Aber leider hab ich mittlerweile ein ganz anderes problem mit dieser dlgProc():
    ich hab im dialog so eine dropdown-combobox, und irgendwie reagiert die dlgProc() ein bissl zu empfindlich auf die messages, die von dieser combobox kommen, so wird zB ein messagebox 30-50 mal erstellt, wenn man auf diese combobox clickt, und des ist mir n bissl zuviel des guten^^ 😮 Frage: warum=? Wird diese message nach der verarbeitung etwa nicht gelöscht, muss ich des message irgendwie explizit entfernen, sodass es nur 1 mal ausgewertet wird?? 😕



  • eigetlihc werden die gelöscht. Du musst wahrscheinlich true zurückgeben, wenn du die nachricht bearbeitet hast. Kann aber auch sein, dass die MsgBox in der WM_Paint steht und die combobox hinter der msgbox verschwindet, und wenn dud ei dann wegklickst muss die combobox wieder neu gemalt werden, verstehst?



  • Hm... eine durchaus interessante theorie, kann ich leider nicht bestätigen, die combobox wird nicht überdeckt...
    ich amch des ma n bissl konkreter, paar zeilen code sagen mehr als tausend worte:

    BOOL CALLBACK MyClass::MyDlgProc(HWND hDlg,
                                     UINT msg,
                                     WPARAM wParam,
                                     LPARAM lParam){
    
    switch(msg){
    case WM_COMMAND:
       switch(LOWORD(wParam)){
       case IDC_MYCOMBO:
           MessageBox(NULL, "BLA", "BLA", MB_OK);
       }
    }
    
    return FALSE;
    
    }//end dlgProc()
    

    Was ich nicht verstehe: woher nimmt sich dieses programmchen das recht, die messagebox 40-45 mal anzuzeigen, obwohl ich nur ein einziges mal auf die combo clicke? 😕
    ich hab da irgendwie 0 durchblick...
    und überhaupt... warum sind es immer 40-45 mal?? warum ned zB 2 oder 5 oder ned gleich 65536? 😮 😮 😮
    Bin für jede hilfe dankbar!



  • letztendlich geht es bei der ganzen sache ja nicht über das blöde messagebox, es wird eh nicht benötigt, anstattdessen werden da 50 mal irgendwelche riesenstrukturen hinundherkopiert, irgendwelche interfaces unnötig strapaziert, und dann werden deswegen in die combobox optionsliste alle strings auch noch mehrfach eingetragen, was soll sich denn ein benutzer denken, wenn er beim clicken auf die combobox eine drei kilometer lange liste, gefüllt mit immer demselben schrott, zu sehen bekommt? = ⚠ 😮 ⚠



  • Okay... wenn man zusätzlich den lParam überprüft, kann man das problem ganz einfach umgehen. Verstehen tu ich's zwar immer noch nicht, aber das ist mir in diesem konkreten fall egal, solang's funktioniert iss alles jut! 🤡



  • ein problem umgangen=zwei neue bekommen
    *ziemlich cool, diese combobox-hydra 😮 *
    ich hab mir alles, was ich hir im forum zum thema "combobox" gefunden habe, durchgelesen, und ich schwöre, dass mein problem nichts mit der grösse der combobox zu tun hat, im theForger's tutorial oder in der MSVC++ dokumentation habe ich leider auch nichts gefunden, womit ich mein problem lösen könnte, wenn das nur daran liegt, dass ich unfähig bin, schwarz auf weiß geschriebene information wahrzunehmen=asche uf mein haupt, aber das verfluchte combobox will nun mal nicht funktionieren 😞 😞 😞

    also, es sieht momentan in etwa so aus:

    //DlgProc
    
    BOOL CALLBACK DlgProc(/*standartparameter*/){
    
    MyClass pMyObject=//pointer, der bei WM_INITDIALOG noch nicht definiert ist
    
    switch(msg){
    case WM_COMMAND:
       if(pMyObject!=NULL){
       switch((LOWORD)wParam){
    
       case IDC_MYCOMBO:
          switch((HIWORD)wParam){
          case CBN_DROPDOWN:
              FillComboBox(pMyObject); //pMyObject nötig um combobox zu füllen
          }
       }
    }
    
    }
    

    und die FillComboBox() macht in etwa folgendes:

    bool FillComboBox(MyClass *pMyObject){
    
       //combobox leeeren, strings auf dem heap deleten
       //empty combobox	
    	int iItemCount=(int)(SendDlgItemMessage(hDlg, IDC_ADAPTER,
                                                     CB_GETCOUNT,0,0));
    	void* pItemData;
    
    	if(iItemCount!=CB_ERR){
    	for(int iItem=0; iItem<iItemCount; iItem++){
    	   pItemData=(void*)(SendDlgItemMessage(hDlg, IDC_MYCOMBO, 
                                  CB_GETITEMDATA,0,0));
    	   if(pItemData!=(void*)(CB_ERR)){ delete[] pItemData; 
                                                pItemData=NULL; }	
                //auch totaler bullshit, führt zu abstürzen, ist aber momentan
                //nicht das problem N° 1
    
             }
    	}
             SendDlgItemMessage(hDlg, IDC_ADAPTER, CB_RESETCONTENT,0,0);
       //end empty combobox
    
       //combobox mit neuen items füllen
       SendDlgItemMessage(hDlg, IDC_MYCOMBO, CB_ADDSTRING, 0,
                        (LPARAM)(pMyObject->GetInfoText()));
       SendDlgItemMessage(hDlg, IDC_MYCOMBO, CB_ADDSTRING, 0,
                        (LPARAM)("THIS TEXT MUST APPEAR IN COMBOBOX!!!"));
       return true;
    }
    

    auch wenn man den ganzen mist, der zu abstürzen führt, komplett auskommentiert, und die funktion nur noch auf die letzten zwei SendDlgItemMessage()-anweisungen reduziert, dann erscheint trotztdem gar nix im combobox, und ich raff nicht warum=was will diese combobox von mir?? 😮
    muss man die liste irgendwie noch aktualisieren?
    wenn man ResetContent weglässt, dann erscheinen diese Strings, aber erst wenn man die combobox mehrmals angeclickt hat, und dann lassen die sich eh nicht entfernen, sondern werden immer weiter angehängt...

    ich weis, es nervt wahrscheinlich unheimlich, wenn da jeden tag solche noobs wie meine wenigkeit mit diesen combobox-problemen herumstressen, wäre aber trotzdem für jeden tipp und für jeden link sehr dankbar 😞

    mfg andrey


Log in to reply