CreateThread - keine Memberfunktion erlaubt ?



  • xD Ja also ich kann leider so garnicht mit dem Debugger umgehen, aber per MessageBoxe's hab ich folgendes rausgefunden : Aufhängen tut sich das Programm an der stelle :

    if( wParam == ClientListBoxId ) {
                     // MessageBox(NULL,"Got smth", "In listbox" , MB_OK );
    
                    LPDRAWITEMSTRUCT lpd = reinterpret_cast<LPDRAWITEMSTRUCT>(lParam);
                    switch (lpd->itemAction) {
                           case ODA_SELECT:
                           case ODA_DRAWENTIRE:
    
                           if( ClientNames.size() < lpd->itemID) { break; }
                           SetBkMode(lpd->hDC, TRANSPARENT);
                           FillRect(lpd->hDC, &lpd->rcItem,
                                   (lpd->itemState & ODS_SELECTED)? GetSysColorBrush(COLOR_HIGHLIGHT): GetSysColorBrush(COLOR_WINDOW));
    
                           DrawText(lpd->hDC,
                                   ClientNames[lpd->itemID].c_str(),
                                   ClientNames[lpd->itemID].length(),
                                   &lpd->rcItem,
                                   DT_LEFT | DT_WORDBREAK );
                           case ODA_FOCUS:
                                break;
                           }
                    }
    

    Denn die auskommentierte MessageBox erscheint, und nach Klick auf "Ok" -> "Blabla funktioniert nicht mehr ... ".



  • Du musst den Befehl der das Problem verursacht schon genau feststellen.
    Das Switch wirds wohl nicht sein 😉



  • ;P Um genau zu sein, hängt sich das Ding direkt nachdem ich es erzeuge auf -.- Ich hab die gesammte Callback function etc. auskommentiert.

    Funktion zum erzeugen :

    BOOL GuiMaintainer::createClientList() {
    
         WNDCLASSEX windowClass;
         char szClassName[] = "ClientList";
         windowClass.hInstance = Inst;
         windowClass.lpszClassName = szClassName;
         windowClass.lpfnWndProc = WindowProcedure;      
         windowClass.style = CS_DBLCLKS;                 
         windowClass.cbSize = sizeof (WNDCLASSEX);
    
          /* Use default icon and mouse-pointer */
         windowClass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
         windowClass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
         windowClass.hCursor = LoadCursor (NULL, IDC_ARROW);
         windowClass.lpszMenuName = NULL;                 
         windowClass.cbClsExtra = 0;                     
         windowClass.cbWndExtra = 0; 
    
         windowClass.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
         if( !RegisterClassEx(&windowClass) ) {
             MessageBox(NULL,"CreateClientList() failed ! ", "Error" , MB_OK );
             return FALSE;
         }
    
         hClientList[0] = CreateWindowEx (
               0,
               szClassName,         
               "List of Chatpartner",       
               WS_VISIBLE | WS_OVERLAPPED | WS_SYSMENU ,
               CW_USEDEFAULT,       
               CW_USEDEFAULT,       
               150,                 
               350,                 
               hLoginHandles[MAINWINDOW],        
               NULL,                
               Inst,       
               NULL                 
               ); 
    
         hClientList[1] = CreateWindowEx (
               WS_EX_CLIENTEDGE,                   
               "LISTBOX",         
               "",       
               WS_VISIBLE | WS_CHILD | WS_VSCROLL | LBS_OWNERDRAWVARIABLE | LBS_NOTIFY | LBS_NODATA  , /
               3,       
               3,       
               130,                 
               309,                 
               hClientList[MAINWINDOW],        
               (HMENU)ClientListBoxId,                
               Inst,       
               NULL                 
               );
    
    }
    

    Sie wird erzeugt -> Hängt sich auf. Komisch.



  • So, ich korrigiere mich noch einmal :

    Das Fenster an sich funktioniert einwandfrei. Ich habe es testweise einfach mal so erzeugt und ich konnt es verschieben etc. ohne Probleme. Hat sich nicht aufgehängt.

    Aber sobald der Client zum Server eine Verbindung erfolgreich herstellt, hängt sich das Fenster komplett auf.

    while (1)
        {
            while( PeekMessage (&messages, NULL, 0, 0, PM_REMOVE ) ) {
                   if( messages.message == WM_QUIT )
                      return TRUE;
                   /* Translate virtual-key messages into character messages */
                   TranslateMessage(&messages);
                   /* Send message to WindowProcedure */
                   DispatchMessage(&messages);
            }
    
               if( !( gui.getConnecterClass().getSocket() == INVALID_SOCKET ) ) {
                   MessageBox(NULL,"Erfüllt ;=) ", "SUX", MB_OK);
                  FD_SET(gui.getConnecterClass().getSocket(), &set );
                  int rc = select(0,&set,NULL,NULL,NULL);
                  if( rc == SOCKET_ERROR) {
                     MessageBox( NULL , "Select failed ... Closing the programm", "Error" , MB_OK );
                     return 1;
                  }
                  if ( FD_ISSET( gui.getConnecterClass().getSocket() , &set ) ) {
                      char buf[1024];
                      // Usually the Server sends the Name of the Client first.
                      // This is not the case if he sends some kind of special message, thus we have to check if it is one
                      rc = recv ( gui.getConnecterClass().getSocket() , buf , sizeof(buf) , 0 );
                      if ( rc == 0 || rc == SOCKET_ERROR ) {
                          MessageBox(NULL,"SOCKET_ERROR","SOCKET_ERROR" , MB_OK);
                          // gui.TerminateClients();
                          return 0;
                      }
    
                      buf[rc] = 0;
                      // That happens here 
                      if( strcmp( buf , "\r\r####NEW CLIENT CONNECTED####\r\r" ) == 0 ) {
                         MessageBox(NULL,"Special messges OoOooo", "OoooooO", MB_OK);
                         memset( buf , 0 , sizeof(buf ) );
                         rc = recv ( gui.getConnecterClass().getSocket() , buf , sizeof(buf) , 0 );
                         buf[rc] = 0;
                         MessageBox(NULL, buf , "Got smth", MB_OK);
                         // gui.addClient( buf );
                      }
                      else {
                         // If the server did not send any special message, then it simply send the Client's name first
                         // Now he sends the Clients input next, which we are going to store in another buffer
                         char buf2[1024];
                         MessageBox(NULL,"NORMAL message OoooOOOoo", "OoOooOOO", MB_OK);
                         rc = recv ( gui.getConnecterClass().getSocket() , buf2, sizeof(buf2) , 0 );
                         // buf2 contains the textt o be updated, buf contains the clients name. And that's exactly what UpdateWindowOutput
                         // needs as information 
                         // gui.UpdateWindowOutput( buf2 , buf );
                      }
                  }   
               }
            }
    

    Wie man sieht : Ich habe alles, was irgendwelche Funktionen von GuiMaintainer callt auskommentiert. TROTZDEM hängt es sich noch auf ! Sobald eine Verbindung steht, und somit if( !( gui.getConnecterClass().getSocket() == INVALID_SOCKET ) erfüllt ist, hängt das Fenster sich auf. Ich schlaf jetzt mal drüber, villeicht finde ich morgen irgendwo die Logik ...



  • An deiner Stelle würde ich mir mit dem Debugger mal ansehen, wo innerhalb dieser Funktion dein Programm so lange zu tun hat, daß es keine Zeit mehr hat, auf Windows-Nachrichten zu reagieren.



  • Ok gut, dann werde ich es morgen nochmal in VSC++ compilen, denn Dev C++ hat keinen Debugger ( zmnd meint das Programm immer, dass es irgendwie nicht funktioniert ) .

    Soweit ich es mit MessageBoxen überprüft habe, hängt es sich genau hier auf :

    int rc = select(0 ,&set, NULL , NULL, NULL);
    

    Also beim select Aufruf. Unlogisch. Der Aufruf wird nie beendet, denn die MessageBox direkt nach dem Aufruf erscheint nie. Finde ich komisch, eig. sollte select doch einfach einen 0 returnen, wenn es failt, und nicht sich aufhängen.

    Auf jeden Fall mal danke schonmal fürs Lesen & Posten 😉



  • Du hast select() noch nicht ganz verstanden. Letzter Parameter ist ein Timeout 😉

    Edit:

    Dev C++

    Böse!



  • Ich habe Deinen Code nur überflogen, aber Deine Methode vom Socket zu lesen ist fehleranfällig.
    Szenario: Der Server sendet 500Byte an Deinen Client. Dein Client macht

    rc = recv ( gui.getConnecterClass().getSocket() , buf , sizeof(buf) , 0 );
    

    Wenn ich Deinen weiteren Code richtig verstehe, verlässt Du Dich darauf, dass Dein Client jetzt 500Byte gelesen hat (falls es nicht so ist, vergiss dieses Posting). Das darfst Du aber nicht. Es wäre denkbar, dass Du mehrmals recv aufrufen musst, um die gesamte Nachricht des Servers zu bekommen. Mal angenommen, Du bekommst bei Deinem recv nur 20Byte, dann würde mindestens schon mal der Vergleich mit Deiner Spezialnachricht (neuer Client) fehlschlagen ... - Wieso codierst Du die Art der Nachricht nicht einfach durch Nummern, 1 = normaler Text, 2 = neuer Client, 3 = ein anderer Client hat sich abgemeldet ...?
    Auf jeden Fall empfiehlt es sich, sich nicht darauf zu verlassen, dass recv irgendeine bestimmte Anzahl von Bytes liest.
    Besser ist es, über den sendenden Socket zuerst die Anzahl der Bytes der folgenden Nachricht zu schicken, dann die Nachricht, und beim empfangenden Socket zuerst die Größe der folgenden Nachricht zu lesen und dann in einer Schleife so lange recv aufzurufen, bis man die ganze Nachricht empfangen hat.



  • Mal sehen, was die MSDN dazu sagt:

    The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O.

    timeout [in] (Anm: der letzte Parameter)
    The maximum time for select to wait, provided in the form of a TIMEVAL structure. Set the timeout parameter to null for blocking operations.

    The parameter time-out controls how long the select can take to complete. If time-out is a null pointer, select will block indefinitely until at least one descriptor meets the specified criteria.

    Um das mal zusammenzufassen - select() wartet so lange, bis auf dem überwachten Socket tatsächlich etwas geschieht. Und da du die ganze Aktion in deiner Window-Prozedur hast, kann das Fenster in der Zwischenzeit nicht auf Eingaben von außen reagieren.



  • Hmmmm schrieb:

    int rc = select(0 ,&set, NULL , NULL, NULL);
    

    Also beim select Aufruf. Unlogisch. Der Aufruf wird nie beendet, denn die MessageBox direkt nach dem Aufruf erscheint nie.

    Mit dem TimeOut - Parameter NULL wartet Dein select so lange, bis es was zu Lesen gibt. Also entweder bekommt Dein Client hier keine Nachricht vom Server, oder Dein set ist nicht korrekt befüllt ...



  • CreateThread ist wohl der Versuch, C++ Code möglichst unportabel und hässlich zu machen, stimmts?
    VC++ 2010 würde dir unter Windows std::thread anbieten, aus dem neuen Standard. Alternativ boost::thread.

    Damit kannst du portabel, sachön und sauber deine Threads starten ...



  • Ethon schrieb:

    VC++ 2010 würde dir unter Windows std::thread anbieten, aus dem neuen Standard.

    Leider nein...



  • Achso okay ;P Erstmal danke für die Erleuchtung. Eigentlich würde das das Problem natürlich erklären, aber auch so :

    int rc = select(0 ,&set, NULL , NULL, 0 );
    

    blockiert der select()-call immernoch. Soweit ich gelesen habe sollte select() sofort returnen, falls 0 als letzter Parameter übergeben wird ?
    Und ja, DevC++ ist veraltet ;P Ich mag die IDE an sich aber und ich dachte, dass für ein eher kleines Projekt Dev C++ erstmal reicht. Aber ich sollte echt entweder auf VSC++, Code::Blocks oder wxDevC++ umsteigen. Werde ich auch in näherer Zukunft tuen.

    @ Belli : Ich werde wohl ein Limit für die Eingabe-Fenster der Clients setzten, sodass die 1024 buf auf jeden Fall groß genug ist. Momentan ist das in der Tat ein Fehler, aber nur weil ich mich damit noch nicht genauer beschäftigt habe. Ich wollte erstmal die groben sachen zum Laufen bringen bevor ich die feineren Dinge implementiere ;P
    Bei dem Vergleich mit der Special-message, der fehlschlägt wenn der Client <20 bytes schickt hast du natürlich recht, habe ich nicht bedacht ;P Da werde ich mich noch drum kümmern müssen, danke ;P
    Das Problem bei normalen Zahlen als Special-message ist, dass solche Zahlen auch sehr wahrscheinlich von normalen Usern eingegeben werden können. Die special-messages mit \r\r\n nicht.

    @ CStoll : Blockiert jetzt select() IMMER oder nicht, wenn ich 0 übergebe ?

    @ Eton Naja, das ist im Prinzip natürlich richtig, aber : Bei meinem Code wird ein nicht verwendeter CreatThread call auch nichts mehr an Portabilität verbessern können. Es ist im Moment zu 100% Windows-dependent, da ich komplett die WinApi verwende. Wenn ich an boost denke, kommen mir nurnoch endlose Stunden in den Sinn, in denen ich versuch habe es die gedownloadeten files richtig zu setzten etc. damit es klappt. Hab's dann nach ein paar Stunden einfach aufgeben.
    Hauptsächlich schreibe ich ja eh eher zur Übung um einfach bisschen Praxis zu haben etc.
    Was genau ist daran eig. so hässlich ? Der C-Style von WinApi ?

    Mfg



  • Hmmmm schrieb:

    @ CStoll : Blockiert jetzt select() IMMER oder nicht, wenn ich 0 übergebe ?

    Habe ich doch gesagt - wenn du dort NULL (oder 0, das ist identisch) übergibst, wartet select() unbegrenzt, d.h. bis tatsächlich etwas passiert. Bau dir lieber ein TIMEVAL zusammen, das du dort übergeben kannst (und werte den Rückgabewert aus, um zu erkennen, ob es etwas zu verarbeiten gibt).



  • Oh, da hab ich das falsch verstanden. Dachte wenn man den Parameter auf 0 setzt, aber eig. natürlich wenn man das entsprechende Member von dem struct auf 0 setzt. Jop, so funktioniert das auch 🙂 Thx soweit erstmal an alle. Es werden sicher noch weitere Probleme auftauchen 😉



  • Hmmmm schrieb:

    Das Problem bei normalen Zahlen als Special-message ist, dass solche Zahlen auch sehr wahrscheinlich von normalen Usern eingegeben werden können. Die special-messages mit \r\r\n nicht.

    Das ist eigentlich kein Problem. Du musst nur JEDE Message mit so einer Qualifikation beginnen lassen. Dann weißt Du am anderen Ende, dass das erste Zeichen eine Kennung dafür ist, was für eine Information (Text, oder eine besondere Special Message) folgt. Wenn nun ein User "2Hallo da" eingibt, dann schickst Du ja "12Hallo da" über die Leitung - angenommen, die 1 steht in Deinem System für normale UserMessage. Du liest dann das erste Zeichen, weißt, aha, der Rest ist die Eingabe eines Users ... usw.



  • Ahhh, das ist ne echt gute Idee 😉 So werd ich's wohl machen, wird ein paar strcpy's und cat's mehr sein, aber dafür sicherer. Danke für die Idee !



  • So, wieder ein neues Problem 😛

    Folgendes : Ich möchte in einer bestimtmen Listbox Double-Klicks abfangen und dann den doppelgeklickten Eintrag verabeiten ( konrekt : Er soll zu dem geklickten Namen connecten bzw mit ihm chatten können ). Aber irgendwie klappt das mit dem Text von dem bestimmten Index kriegen nicht. Mein buffer bleibt immer leer ;O

    if( HIWORD(wParam) == LBN_DBLCLK )  { 
       int index = SendMessage( (HWND)lParam  , LB_GETCURSEL , 0 , 0 );
    
       char buffer[400];
       SendMessage( (HWND)lParam , LB_GETTEXT , index , (LPARAM)buffer );
       if ( strcmp( buffer , "Group Chat" ) != 0 ) // Sollte ich glaube ich noch ändern, da gefährlich falls der geklickte Name < "Group Chat" ist, richtig ? 
          createChatWindow( buffer );
       else
          createGroupChatWindow();   
    } break;
    

    Jemand ne Idee ?



  • Bin mir gerade nicht 100%ig sicher, aber das strcmp kannste schon so lassen denke ich.

    Was du allerdings ändern solltest:
    Wer garantiert dir, dass der Name nicht über 399 Zeichen lang ist? Siehe LB_GETTEXTLEN.

    Ansonsten fällt mir gerade nichts auf, was meinst du genau mit "Mein buffer bleibt immer leer"? So wie er vorher war? Steht in buffer[0] dann '\0'?



  • @ strcmp Ahh gut, umso besser 😉

    Für die Namen werde ich sehr wahrscheinlich einen Limit auf 50 bytes oder so setzen, also ist das kein Problem.

    Jop, ich habe mir den buffer in ner Messagebox ausgeben lassen

    MessageBox(NULL,buffer ,"BLA",MB_OK);
    

    und diese ist einfach leer. Genauso wie der Titel des Fensters das ich danach erzeuge, welches eig. den übergeben String als Titel haben sollte.
    Einen Eintrag in die Listbox habe ich natürlich hinzugefügt, der Index wird auch richtig bestimmt.


Anmelden zum Antworten