Thread und Zeiger auf C..Doc



  • Dieser Beitrag könnte Themenverwandt zu einem anderen Problem von mir sein: HIER

    Ich habe eine Klasse die einen (Arbeits-)Thread (als Methode dieser Klasse) ausführt. Sie beinhaltet desweiteren einen Zeiger auf das CMyProgDoc-Objekt meines Programms. Von dieser Klasse existiert ein Objekt in meiner CMyProgDoc-Klasse.
    Im Thread wird nun eine Methode aus CMyProgDoc aufgerufen.

    An dieser Stelle bricht der Debugger das Ausführen des Programms mit folgender Meldung ab:
    Unhandled exception at 0x00549e52 in MyProg.exe: 0xC0000005: Access violation reading location 0x00000004.
    Und er zeigt mir die Funktion GetValueAt(void* key) der Klasse CMapPtrToPtr.

    Gekürzte Codeauszüge:

    class BlubbThread
    {
       // ...
       CMyProgDoc *mParent;
       // ...
       void SetParent(CMyProgDoc *p)
       {
          mParent = p;
       }
       // ...
       static UINT Run(LPVOID pParam)  // der Thread
       {
          BlubbThread *OuterThis = (BlubbThread*)pParam;
          // ...
          OuterThis->mParent->FunktionXYZ();   // FEHLER: hier bricht die Ausführung ab
          // ...
       }
       // ...
    };
    
    class CMyProgDoc : public CDocument
    {
       // ...
       BlubbThread mBlTh;
       // ...
       CMyProgDoc() // Konstruktor
       {
          mBlTh.SetParent(this);
       }
       // ...
       void FunktionXYZ()
       {
           // ...
       }
    };
    


  • Wo rufst Du denn Run auf? Übergibst Du dort etwas vom Typ BlubbThread*?



  • In der Klasse BlubbThread gibt es folgende Methode:

    void BlubbThread::StartThread()
    {
    	mThread = AfxBeginThread(BlubbThread::Run, this, THREAD_PRIORITY_BELOW_NORMAL, 0, CREATE_SUSPENDED, NULL);
    	mThread->ResumeThread();
    	// mThread ist ein Zeiger vom Typ CWinThread
    }
    


  • Kannst du eventuell einschränken, ob der Fehler in der Run() oder in der aufgerufenen Funktion auftritt?

    zwei (oder drei) Möglichkeiten würden mir einfallen:

    1. Du verwendest die StartThread() Methode vom falschen Objekt aus.
      (Frage: Von wo aus wird der Thread angestoßen?
    2. Du greifst in FunctionXYZ() auf dein Window direkt zu (das darf nur der Thread, der das Window angelegt hat).
    3. Das Document hat sich ins Nirvana verabshiedet, bevor du den FunctionXYZ()-Aufruf erreicht hast (und mit ihm sein mBlTh)


  • A.
    1. Es existiert nur ein Objekt.
    2. Öhm, ich weiß nicht so recht, vielleich t steht die Antwort bei B.
    3. Nein, die Funktion wird ausfgerufen.

    B.
    Der Fehler tritt in der aufgerufenen Funktionen [hier: FunctionXYZ()] auf, sie wird also noch ausgeführt. Genauer an folgender Codezeile: mSendingSocket.Create();
    Dabei ist mSendingSocket ein Objekt von einer von CAsyncSocket abgeleiteten Klasse.

    Die aufgerufenen Funktion FunctionXYZ() ansich dürfte nicht fehlerhaft sein. Ich hatte sie schon zuvor verwendet und nicht umgebaut. Sie schein nur nicht korrekt zu funktionieren, wenn ich sie aus dem Thread heraus aufrufe.



  • Dann ruf doch deine FunktionXYZ über SendMessage auf, dann kann nichts mehr schief gehen. Aus Thread auf Funktionen in einem anderen Thread(dein Dialog ist eine anderer Thread) zuzugreifen, bringt fast immer Probleme mit der Syncronisation der beiden Threads. Also lass ich das von Anfangan immer weg und erstelle mir eine UserMessage ala

    #define FunktionXYZ (WM_USER+1)
    

    und das auch meist gleich in der stdafx.h (wegen Faulheit wird eh überall includiert. geb dann den Thread das Handle auf den Dialog mit oder wenn ich den Zeiger vom Dialog mitgebe nutze ich daraus das Handle auf den Dialog.

    SendMessage(DlgHandle,FunktionXYZ,0,0);
    

    und im Dialog erstelle ich mir in der MessageMap

    ON_MESSAGE(FunktionXYZ,OnFunktionXYZ)
    

    und die Funktion dazu:

    LRESULT CMyDialog::OnFunktionXYZ(WPARAM /*Type*/, LPARAM lparam)
    {
        ....
    }
    

    Fertig und SendMessage kommt erst zurück wenn die Funktion ausgefürt wurde und Das Syncron zum Dlg-Thread. Und Übergabe und Rückgabewerte haste auch noch wenn man die braucht.

    Ende

    Gruß Matthias



  • Schonmal versucht den Socket als static zu deklarieren?

    Wie bereits gesagt, so halte ich es auch generell für eine schlechte Idee aus einem Thread heraus Daten eines anderen Thread direkt zu ändern.
    Besser: Der Thread zeigt lediglich an, dass er etwas von einem anderen will und der andere erfüllt einem den Wunsch, wenn er an der Reihe ist.



  • Zwei Fragen hätte ich dazu:

    Wie bekomme ich in der C..Doc das(/ein) Handle für mein Hauptfenster, welches ich dann an den Thread übergeben muss?

    Wie soll ich es realisiseren, wenn ich eigentlich an die FunktionXYZ() Parameter übergeben möchte? Also sagen wir, ich möchte einen CString mitsenden.



  • zu Doc-Handle:

    In deinem View zB: hast du die Funktion GetDocument(), im Document direkt is das der this-Zeiger, und von beiden kommst du auf das handle des Windows in dem du ->m_hWnd benutzt. also gar nicht so schwer. in deiner BlubbThread-Klasse hast du doch auch den Zeiger auf dein Doc.

    zu übergabe von CString, Bei der SendMessage ist der vorletze ind der letzte Parameter jeweils WPARAM,LPARAM was einem word und einem long entspricht. Wenn du jetzt SendMessage benutzt, lannst du über den LPARAM einen Zeiger auf deinen lokalen CString übergeben, Da SendMessage so lande Wartet bis die Message abgearbeitet ist, brachst du dir in dem Fall auch keine gedanken darüber zu machen ob der CString noch Existiert wenn deine Funktion ausgeführt wird.

    also

    CString buffer("dieser Text soll übertragen werden");
    SendMessage(DlgHandle,FunktionXYZ,0,&buffer);
    
    LRESULT CMyDialog::OnFunktionXYZ(WPARAM /*Type*/, LPARAM lparam)
    {
        CString *buffer = static_cast<CString*>lparam; //hab das jetzt mit dem casten nicht ausprobiert ob das so stimmt :confused: 
        //buffer ist der Zeiger auf deinen CString
    }
    

    Sollte dir doch etwas weiter helfen, bevor das ein Roman wird.

    Gruß Matthias



  • CTecS schrieb:

    ... und von beiden kommst du auf das handle des Windows in dem du ->m_hWnd benutz ...

    Tja, bevor ich gefragt habe, hatte ich nach soetwas gesucht (GetHandle, hWnd, ..., m_hWnd) und nichts gefunden. Wenn ich versuche es so zu verwenden, meldet er mir: error C2039: 'm_hWnd' : is not a member of 'CMyProgDoc'

    //EDIT:
    Ach ich sehe gerade, m_hWnd ist Membervariable von C..View.



  • Leider sind meine Probleme nicht am Ende.

    Ich habe nun soweit versucht das Problem über SendMessage zu lösen. Ich wollte die Nachrichtenverarbeitung dabei eigentlich im C..Doc vornehmen, das gibt aber Probleme:

    BEGIN_MESSAGE_MAP(CMyProgDoc, CDocument)
    	ON_MESSAGE(LD_CLIENT_CONNECT, OnLDClientConnect)
    END_MESSAGE_MAP()
    
    // ...
    
    LRESULT CMyProgDoc::OnLDClientConnect(WPARAM wparam, LPARAM lparam)
    {
    	AfxMessageBox("BLUBB");
    }
    

    Ich erhalte die Fehlermeldung ...
    error C2440: 'static_cast' : cannot convert from 'LRESULT (__thiscall CMyProgDoc:: )(WPARAM,LPARAM)' to 'LRESULT (__thiscall CWnd::* )(WPARAM,LPARAM)'*

    Heißt das nun, ich kann Nachrichten nur in von CWnd abgeleiteten Klassen (so wie C..View) durchführen?
    → Wenn ja, wozu wird dann BEGIN_MESSAGE_MAP etc. überhaupt für C..Doc erzeugt?
    → Wenn nein, was muss ich tun, damit es funktioniert?



  • CTecS schrieb:

    CString buffer("dieser Text soll übertragen werden");
    SendMessage(DlgHandle,FunktionXYZ,0,&buffer);
    
    LRESULT CMyDialog::OnFunktionXYZ(WPARAM /*Type*/, LPARAM lparam)
    {
        CString *buffer = static_cast<CString*>lparam; //hab das jetzt mit dem casten nicht ausprobiert ob das so stimmt :confused: 
        //buffer ist der Zeiger auf deinen CString
    }
    

    Gruß Matthias

    ich bekomm da folgenden fehler:
    Fehler 2 error C2061: Syntaxfehler: Bezeichner 'lparam'

    was mach ich falsch? 😕



  • Du hast den Kommentar hinter der Cast-Zeile übersehen (und richtig heißt der Aufruf auch CString* buffer = static_cast<CString*>(lparam);



  • hallo,
    dann bekomm ich den fehler:
    Fehler 4 error C2440: 'static_cast': 'LPARAM' kann nicht in 'CString *' konvertiert werden

    oder muss ich das sonst noch irgendwo angeben das ich da nun ne string übergeb..

    hab dieses WPARAM und LPARAM nich so ganz verstanden 😕



  • Was genau hast du denn eigentlich per SendMessage() rausgeschickt?



  • so wird es aufgerufen:

    CString hmmm("zu übertragen");
    SendMessage(hwnd_main,FunktionXYZ, (WPARAM)0 , (LPARAM)&hmmm);
    

    dann:

    LRESULT CEmpfaengerDlg::OnFunktionBlubMessage(WPARAM wparam, LPARAM lparam)
    {
    CString* buffer = static_cast<CString*>(lparam);
    ...
    

    einen int wert kann ich übertragen..



  • Du kannst keinen CString per SendMessage übertragen (zumindest nicht so wie Du es gemacht hast!).
    Das CString object ist schon längst wieder ungültig, wenn es der Empfänger bekommt.

    Per SendMessage kannst Du eigentlich nur Zahlen übertragen (und natürlich den Spezialfall von Zahlen: nämlich Zeiger). Nur der Zeiger muss auf der Empfängerseite natürlich noch gültig sein!

    Also z.B.:

    LPTSTR szStr = _tcsdup(_T("mein String"));
    SendMessage(..... (LPWARAM) szStr);
    

    und auf der Empfängerseite:

    LRESULT CEmpfaengerDlg::OnFunktionBlubMessage(WPARAM wparam, LPARAM lparam)
    {
      LPTSTR szStr = (LPTSTR) lparam;
      // ...
      free(szStr);
    }
    


  • ui!

    was ist denn das shcon wieder 😉

    also ein zeiger auf einen string..
    aber was unterscheidet den zeiger zu normalen?

    ich schätz mal es hat was mit der gültigkeit zu tun, wenn du das schon ansprichst 😉
    oder wird das wo anders gespeichert?
    wo werden denn überhaupt meine variablen gespeichert?



  • @Jochen Kalmbach

    Will Dir ja nur ungern wiedersprechen, aber das Senden eines CString´s über SendMessage ist durchaus möglich und eigentlich gefahrlos, da bei SendMessage auf die Rückkehr aus der MessageFunktion gewartet wird, da man ja einen Rückgabeparameter hat, also auch der lokale CString der auf dem Stack generiert wurde übergeben werden kann. Im Gegensatz zu PostMessage da sollte man so etwas nicht machen. Aber vielleicht kannst Du mich ja eines besseren belehren.

    MFG Matthias



  • CTecS schrieb:

    @Jochen Kalmbach

    Will Dir ja nur ungern wiedersprechen, aber das Senden eines CString´s über SendMessage ist durchaus möglich und eigentlich gefahrlos, da bei SendMessage auf die Rückkehr aus der MessageFunktion gewartet wird,

    Upps... da hab ich doch klatt was vergessen... danke!


Anmelden zum Antworten