Auf UI warten



  • Hallo!

    Über einen Dialog wählt der Benutzer eine Datei, die gelöscht werden soll.
    Vor dem Löschen - da das Löschen länger dauern kann - deaktiviere ich einige Controls und zeige eine Fortschrittsanzaige an. Danach lösche ich die Datei.

    Nun kommt die UI etwas ins schleudern, da die Oberfläche noch nicht aktualisiert (neu gezeichnet) ist, aber mit dem Löschen der Datei schon begonnen wurde...

    Wie kann ich vor dem Löschen der Datei auf die UI warten, bis die alles gezeichnet hat und dann erst mit dem Löschen beginnen?



  • Du wirst wohl nicht drum rum kommen deine Löschaktion in einen Thread zu packen. Ich nehm an das du es jetzt in einem Handler eines Buttons machst und da du in demfall in der Nachrichtenschleife hängst weil dein Löschen ja gemacht wird kann die Nachricht zum aktualisieren deines Dialoeg niocht Bearbeitet werden.
    Entwerder du schiebst das in einen Thread und alles wird gut oder du mußt Verrenkungen machen damit der Messageloop weiter läuft.



  • CTecS schrieb:

    Du wirst wohl nicht drum rum kommen deine Löschaktion in einen Thread zu packen. Ich nehm an das du es jetzt in einem Handler eines Buttons machst und da du in demfall in der Nachrichtenschleife hängst weil dein Löschen ja gemacht wird kann die Nachricht zum aktualisieren deines Dialoeg niocht Bearbeitet werden.

    Das ist richtig.

    CTecS schrieb:

    Entwerder du schiebst das in einen Thread und alles wird gut oder du mußt Verrenkungen machen damit der Messageloop weiter läuft.

    Wie würden denn Verrenkungen aussehen?


  • Mod

    HaJo. schrieb:

    Nun kommt die UI etwas ins schleudern, da die Oberfläche noch nicht aktualisiert (neu gezeichnet) ist, aber mit dem Löschen der Datei schon begonnen wurde...

    Wie kann ich vor dem Löschen der Datei auf die UI warten, bis die alles gezeichnet hat und dann erst mit dem Löschen beginnen?

    UpdateWindow/RedrawWindow

    Aber das Problem kommt wieder, wenn Du ein anderes Fensterr über die Anwendnung legst. Vista und Windows7 sind da etwas gnädiger, weil Sie den Inhalt zwischenspeichern...
    Ohne Abarbeiten des WM_PAINT Handlers / sprich oihne Messageloop ist das alles problematisch. Du kannst alerdings zwischendrin immer mal UpdateWindow/RedrawWindow aufrufen.



  • Eine Funktion zum abarbeiten der Message Loop und deren gelegentlichen Aufruf würde evtl. was bringen (wie schon geschrieben), die Funktion könnte in etwa so aussehen :

    void CMyClass::DoEvents()
    {
        MSG msg;
        while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    

    Gruß
    Marcus


  • Mod

    Wenn bitte schon ein Message Pump drin ist, dann nur mit AfxPumpMessage und nicht TranslateMessage/DispatchMessage!



  • Martin, kann man AfxPumpMessage eigentlich auch aus einem Arbeitsthread aufrufen oder muss der Aufruf immer aus dem Hauptthread erfolgen?



  • Ah.. OK , wieder etwas neues , leider noch nicht ganz "neues gelernt", da ich nicht ganz verstehe was es mit AfxMessagePump auf sich hat, kann keine richtige Doku finden.

    Was ich gefunden habe ist CWinThread::PumpMessage(), d.h. ich könnte z.b.

    theApp.PumpMessage();
    

    aufrufen um zu veranlassen dass die MessageQueue abgearbeitet wird ?

    Danke,
    gruß
    Marcus



  • Martin Richter schrieb:

    Wenn bitte schon ein Message Pump drin ist, dann nur mit AfxPumpMessage und nicht TranslateMessage/DispatchMessage!

    AfxPumpMessage ist mit ebenfalls neu.
    Ein UpdateWindow hat zwar fürs erste etwas gebracht, jedoch ist ein Fortschrittsanzeige mit PBS_MARQUEE-Stil an Board.

    Ich werde dann wohl den Weg über den Worker Thread gehen.


  • Mod

    1. AfxPumpMessage ist ein shortcut fur AfxGetApp()->PumpMessage() (grob), allerdings bezieht sich dies immer auf den aktuellen Thread. Es kann also auch das aktuelle CWinThread Objekt sein. Also genaugenommen ein Shortcur für AgxGetThread()->PumpMessage();
    2. @sri: Man kann keine Nachricten für andere Threads abholen. PumpMessage/GetMessage funktioniert nur auf die Threadlokale Message Queue. Das ist der Grund warum man die UI nicht auslagert sondern den Arbeitsprozess.
    Nachrichten für Fenster laufen in dem Thread auf, der sie erzeugt...
    HTH



  • Das ganze Vorhaben klappt immer noch nicht.
    Vorweg: Auf die Löschoperation habe ich keinen Einfluss, die wird über eine andere Klasse "von vorne bis hinten durchgeführt".

    Die "Marquee-"Progressbar bewegt sich kein Stück und der Thread/die Anwendung ist komplett blockiert während des Löschens.

    Der Code sieht in etwa wie folgt aus:

    void CDeletionDialog::OnBnClickedDeleteFile
    {
        CString szFilename;
        GetDlgItemText(IDC_FILENAME, szFilename);
    
        this->m_ProgressBar.ShowWindow(SW_SHOW);
        this->m_ProgressBar.SetMarquee(TRUE, 17);                
        UpdateWindow();
    
        CWinThread * pThread = AfxBeginThread(DeletionThreadProc, 0);
    
        WaitForSingleObject(pThread->m_hThread, INFINITE);
    
        AfxMessageBox(_T("Die Datei wurde erfolgreich gelöscht."));
    }
    

    Eigentlich blockiert doch WaitForSingleObject die Awendung nicht, oder?


  • Mod

    HaJo. schrieb:

    Eigentlich blockiert doch WaitForSingleObject die Awendung nicht, oder?

    Doch genau das tut sie!
    Du musst hier eine Message-Loop einbauen und diese beenden, wen der Thread fertig ist.
    Weiterhin slltest Du unbedingt die UI disablen um keine Effekte wegen Reentrancy zu bekommen.



  • Martin Richter schrieb:

    Doch genau das tut sie!
    Du musst hier eine Message-Loop einbauen und diese beenden, wen der Thread fertig ist.

    Wie würde denn die MessageLoop aussehen?

    while(AfxPumpMessage())
    {
        if (WaitForSingleObject(pThread->m_hThread, 10) == WAIT_OBJECT_0)
            // Thread ist fertig
    }
    

    Martin Richter schrieb:

    Weiterhin slltest Du unbedingt die UI disablen um keine Effekte wegen Reentrancy zu bekommen.

    Habe ich bereits gemacht. Allerdings jedes Control einzeln. Geht das auch einfacher?


  • Mod

    Einfach den Main Dialog disablen.

    Nicht einfach PumpMessage ausführen. Das würde das Ende des Threads nicht mitbekommen, wenn keine Nachricht mehr kommt.

    Also zuerst ein

    while (::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
       AfxPumpMessage();
    


  • HaJo. schrieb:

    Geht das auch einfacher?

    Das ganzen Fenster / Dialog disablen, damit ist auch keine Eingabe auf einzelene Elemente innerhalb mehr möglich.

    BTW : Mein Frage mit dem AfxPumpMessage hat sich mit Deinem bsp. Code geklärt 🙂 Mich wundert nur dass die MSDN zu VC6 nichts finde, aber naja..

    Gruß
    Marcus



  • Danke für die Hilfen, es klappt nun.


Anmelden zum Antworten