Thread und Progresscontrol
-
Hallo,
aus Mainframe heraus werden modale Dialoge geöffnet, die zum Teil viele
Daten laden, sodass es unter Umständen einige Sekunden braucht, bis der
Dialog erscheint. Ich möchte gerne während der Dialog sich füllt in der
Mainframe Statusleiste einen Progresscontrol aktualieren.Meine Idee wäre in den modalen Dialogen jeweils über einen Pointer auf CMainframe
StartProgress() bzw. StopProgress() aufzurufen. Letzteres ist in meinem Test
noch nicht implementiert.Das Problem ist, dass die Progresscontrolaktualiesierung erst dann "anläuft"
wenn der modale Dialog seine Daten geladen hat. (SendMessage+PostMessage).Wo könnte das Problem liegen?
Edit: Nachtrag
Auch mit einem Timer-Event habe ich dieses Verhalten. Ich habe das Problem zumindest so eingrenzen können, das es was mit der CMFCRibbonProgressBar zu tun haben muss. Ein Beispiel mit einer "normalen" Progressbar funktioniert.Grüsse
foodax// gekürzter Quellcode ... void CMainFrame::StartProgress() { m_pThreadProgress = ::AfxBeginThread(ThreadProgress, (LPVOID) m_hWnd); } UINT CMainFrame::ThreadProgress ( LPVOID pVoid ) { HWND hwnd = reinterpret_cast<HWND>( pVoid ); while ( true ) // test { afxDump << "ThreadProgress\n") ::SendMessage(hwnd, WM_MESSAGE_UPDATE_PROGRESS, (WPARAM) 0, (LPARAM) 0); Sleep(100); } return 0; } LRESULT CMainFrame::OnUpdateProgress(UINT wParam, LONG lParam) { afxDump << "OnUpdateProgress\n") // Aktualisierung Progressbar ... return 0; }
-
na ja mit Sleep(100) wird gewartet aber die Messages von Windows nicht weiter verteilt, dadurch bleibt auch deine Message auf dem stapel liegen bis mal wieder der MessageLoop läuft.
Also mußt du dich um den Messageloop selbst kümmern, das kann man dann machen mit
MSG msg; while( ::PeekMessage( &msg, NULL, NULL, NULL, PM_NOREMOVE ) ) ::AfxGetThread()->PumpMessage();
und schon sollte deine Nachricht auch ankommen
-
Hallo CTecS,
danke für die Info. Mit der PumpMessage() hatte ich das schon versucht. Ich dachte
aber auch, dass ein modaler Dialog seine eigene Nachrichtenschleife hat?Ich habe jetzt mal auf einen Timer und eine Progress in der CMFCStatusBar umgestellt.
Hier dieselben Probleme. Nach dem Füllen des Dialog (1) in OnInitDialog() wird der Timer
aufgerufen.Wo könnte man noch suchen?
Grüsse
foodaxvoid CMainFrame::OnOpenTheDialog() { StartProgress(); // Test: den Progress vorab starten .... CMyDialog dlg; // (1) dlg.DoModal(); } void CMainFrame::StartProgress() { m_wndStatusBar.EnablePaneProgressBar (1, m_nProgressMax); m_nProgressCurr = 0; SetTimer (ID_PROGRESS_TIMER, 50, NULL); } void CMainFrame::StopProgress() { KillTimer (ID_PROGRESS_TIMER); m_wndStatusBar.EnablePaneProgressBar (1, -1); } void CMainFrame::OnTimer(UINT_PTR nIDEvent) { afxDump << "OnTimer()\n"; m_nProgressCurr += 5; if (m_nProgressCurr > m_nProgressMax) { m_nProgressCurr = 0; } m_wndStatusBar.SetPaneProgress (1, m_nProgressCurr); MSG msg; while (::PeekMessage(&msg, NULL,NULL, NULL, PM_NOREMOVE)) AfxGetThread()->PumpMessage(); CFrameWndEx::OnTimer(nIDEvent); }
-
Zumindest in deinem Codeausschnitt kann ich nichts sehen.
Wobei ich Tippe jetzt mal ins blaue das es an deinem DoModal liegen könnte, hast du mal versucht den Dialog mit Create zu erstellen, denn OnOpenTheDialog wird doch sicher über einen Button aufgerufen also hängst du ja immer noch in der Nachrichtenbehandlung.
Aber Nagel mich net fest, wie gesagt kann dran liegen muß aber nicht, ich hatte die konstellation so noch nicht, aber vielleicht meldet sich noch jemand anderes der dir da weiter helfen kann
-
Dieser Code ist Quatsch. Wie kommst Du darauf in OnTimer einen MessagePump einzubauen.
Deine Timer werden nur ausgeführt, wenn auch ein MessagePump läuft. Nicht umgekehrt...
Wenn Du einen Timer benutzt muss eine MessageLoop in diesem Thread laufen, wenn Du irgendwo eine infinite Loop hast wird kein Timer ausgeführt und kein Progress gesetzt.
-
Hallo Martin,
zu meinem Verständnis. Wenn ich den Thread (siehe erster Versuch oben) im Mainframe vor DoModal() erzeuge und in OnInitDialog() den Dialogthread mit sleep(...) lahm lege, sollte doch der Thread aus Mainframe und damit mein Progress weiterlaufen?
Grüsse
foodax
-
Das ja! Aber eben nicht wenn eine nachricht aus dem Mainframe an den Dialog des Threads gesendet wird!
Dein erster Versuch sieht eigentlich richtig aus, nur kann man ja nicht sehen, was Dein Mainframe macht, nachdem der Thread angelegt wurde. Wenn nun der Worker eine Nachricht sendet (nicht posted) kann der Worker auch blockieren, wenn der Mainthread keine Nachrichten abholt, und wenn der Mainthread z.b. auf den Worker wartet hat man einen schönen Deadlock.
Debugge. Einfach auf Brak-All gehen und anschauen was jeder Thread macht.
-
Hallo Martin,
ich habe ein minimales Testprojekt erstellt, welches nur den Thread erstellt und einen Dialog modal aufruft. Hier ist derselbe Effekt. Der unten aufgeführte Quellcode bringt die folgende Debugausgabe. Man sieht schön die PostMessages(), deren Empfänger erst dann tätig wird, wenn der Sleep() des modalen Dialoges beendet ist. Das Projekt ist ein SDI-Projekt mit Ribbons und einer Formview als View.
Grüsse
foodaxPostMessage()
PostMessage()
PostMessage()
PostMessage()
PostMessage()
PostMessage()
PostMessage()
PostMessage()
PostMessage()
PostMessage()
PostMessage()
OnUpdateProgress()
OnUpdateProgress()
OnUpdateProgress()
OnUpdateProgress()
OnUpdateProgress()
OnUpdateProgress()
OnUpdateProgress()
OnUpdateProgress()
OnUpdateProgress()
OnUpdateProgress()
OnUpdateProgress()
PostMessage()
OnUpdateProgress()
PostMessage()
OnUpdateProgress()
PostMessage()
OnUpdateProgress()
PostMessage()
OnUpdateProgress()
PostMessage()
...void CMainFrame::StartThread() { m_pThreadProgress = ::AfxBeginThread(ThreadProgress, (LPVOID) m_hWnd); } UINT CMainFrame::ThreadProgress ( LPVOID pVoid ) { HWND hwnd = reinterpret_cast<HWND>( pVoid ); while ( true ) { afxDump << "PostMessage()\n"; ::PostMessage(hwnd, WM_MESSAGE_UPDATE_PROGRESS, (WPARAM) 0, (LPARAM) 0); Sleep(200); } return 0; } LRESULT CMainFrame::OnUpdateProgress(UINT wParam, LONG lParam) { afxDump << "OnUpdateProgress()\n"; return 0; } void CMainFrame::OnButtonMydialog() { StartThread(); CMyDialog dlg; dlg.DoModal(); } //... BOOL CMyDialog::OnInitDialog() { CDialogEx::OnInitDialog(); Sleep(2000); return TRUE; // return TRUE unless you set the focus to a control // AUSNAHME: OCX-Eigenschaftenseite muss FALSE zurückgeben. }
-
Ist doch logisch!
Ich habe es doch schon geschrieben. Dein Diaog läuft im Mainthread und wenn der eben nicht reagieren kann weil er eben nicht die Messaagequeue pumpt, wird auch nichts aus der Messagequeue gelesen!Ich verstehe nicht was Du mit diesem Beisiel sagen willst.
Es ist genau wie ich schreibe. Nochmal: Eine Nachricht aus einem anderen Thread wird (egal ob Send/Post) erst durch die verwednung von Peek/GetMessage verarbeitet.
Wird Get/PeekMessage in dieser Zeitnicht aufegrufen (Dein Sleep verhindert das) kann auch keine Nachricht bearbeitet werden.
-
Hallo Martin,
logisch kommt zeitlich nach verstehen
Ich habe das Füllen des Dialoges in einen eigenen Thread ausgelagert und damit klappts dann auch mit dem Progress des Mainthreads. Danke für die Hilfe
Grüsse
foodax