Debug Assertion failed | afxcmn.inl line 132
-
In OnInitDlg steht:
// Laden der Kursdaten if(stockDB_initiated) stockdata_thread = ::AfxBeginThread(load_stockdata_masterthread, this);UINT CHSIDepotmanagerDlg::load_stockdata_masterthread(LPVOID pParam) { CHSIDepotmanagerDlg* pDlg = (CHSIDepotmanagerDlg*) pParam; pDlg->child_param.pointer = pDlg; // Vorbereitung des CProgress-Fensters int length = pDlg->m_stockCtrl.GetItemCount(); pDlg->m_progress_dialog.init(0,length); pDlg->m_progress_dialog.show(); pDlg->m_progress_dialog.setText("Laden der historischen Kursdaten ..."); // Das Multithreading CWinThread* pThread; for(int g=0; g<length; g++) { pDlg->child_param.index = g; pThread = ::AfxBeginThread( load_stockdata_slavethread, &pDlg->child_param, 0, 0, CREATE_SUSPENDED, NULL); pThread->ResumeThread(); ::WaitForSingleObject( pThread->m_hThread, INFINITE ); } return true; }UINT CHSIDepotmanagerDlg::load_stockdata_slavethread(LPVOID pParam) { thread_parameter* myParam = (thread_parameter*) pParam; CHSIDepotmanagerDlg* pDlg = (CHSIDepotmanagerDlg*) myParam->pointer; int stock_index = myParam->index; // Laden der historischen Kursdaten pDlg->load_stock_data(stock_index); return 0; }BOOL CHSIDepotmanagerDlg::load_stock_data(int stock_index) { bool abbruch, start_set=false; CString stock_symbol, filename, fullpath, strLesePuffer, error, header, substring, start_date, end_date; CStdioFile DateiLesen; stock_symbol = m_stockCtrl.GetItemText(stock_index,0); filename = stock_symbol; filename.Replace(".","_"); filename += ".csv"; fullpath = path_stock_data + "\\" + filename; header = "Date,Open,High,Low,Close,Volume,Adj. Close*"; int pos, last_pos, length, satz_ident; // Einlesen der Kursdaten if (DateiLesen.Open(fullpath, CFile::modeRead)) { while(DateiLesen.ReadString(strLesePuffer)) { strLesePuffer.TrimLeft(); strLesePuffer.TrimRight(); if(strLesePuffer.Find(header,0)==-1) { pos = 0; last_pos = 0; satz_ident=0; abbruch = false; while(true) { pos = strLesePuffer.Find(",",last_pos); if(pos==-1){ length = strLesePuffer.GetLength()-last_pos; abbruch = true; }else length = pos-last_pos; substring = strLesePuffer.Mid(last_pos,length); if(satz_ident==0){ if(!start_set) { start_set =true; start_date =substring; } end_date =substring; // stockDB[stock_index].InsertItem(stockDB[stock_index].GetItemCount(),substring); } // }else stockDB[stock_index].SetItemText(stockDB[stock_index].GetItemCount()-1,satz_ident,substring); last_pos = pos+1; satz_ident += 1; if(abbruch) break; } } } }else { error.Format("Es ist eine Fehler beim Lesen der Datei %s aufgetreten.",filename); MessageBox(error,"Lesefehler",MB_ICONINFORMATION); } m_progress_dialog.m_ctlProgress.SetPos(m_progress_dialog.m_ctlProgress.GetPos()+1); // if(m_progress_dialog.m_ctlProgress.GetPos()==m_stockCtrl.GetItemCount()) // m_progress_dialog.hide(); m_stockCtrl.SetItemText(stock_index,1,start_date); m_stockCtrl.SetItemText(stock_index,2,end_date); return true; }
-
Wie du siehst benutze ich AfxCreateThread mehr als einmal.
-
Wo genau passiert
// Laden der Kursdaten if(stockDB_initiated) stockdata_thread = ::AfxBeginThread(load_stockdata_masterthread, this);Gruss, Gio
-
Hier:
BOOL CHSIDepotmanagerDlg::OnInitDialog()
-
Ok, dann musst du in der CHSIDepotmanagerDlg::OnDestroy() (zu überladen im Wizard) vor aufruf der CDialog::OnDestroy() dafür sorgen, dass Dein erster Thread beendet wird. Dieser erste Thread muss natürlich vor "seinem Ende" dafür Sorge tragen, dass alle von ihm erzeugten Threads beendet sind, etc.
Wie das geht, dafür gibt es mehrere Möglichkeiten. Eine für Deinen Dialog ist z.B.
... DWORD dwStatus; // check current state VERIFY(::GetExitCodeThread(stockdata_thread->m_hThread, &dwStatus)); // wait until the thread has terminated while( dwStatus == STILL_ACTIVE && stockdata_thread->m_hThread != NULL ) { // check, if the thread is running ... ::GetExitCodeThread(stockdata_thread->m_hThread, &dwStatus ); };Gruss, Gio
-
Mh, mal geht es und mal nicht.
void CHSIDepotmanagerDlg::OnDestroy() { CDialog::OnDestroy(); DWORD dwStatus; // check current state VERIFY(::GetExitCodeThread(stockdata_thread->m_hThread, &dwStatus)); // wait until the thread has terminated while( dwStatus == STILL_ACTIVE && stockdata_thread->m_hThread != NULL ) { // check, if the thread is running ... ::GetExitCodeThread(stockdata_thread->m_hThread, &dwStatus ); } }Wenn ein Feher kommt, dan kommt:
Debug Assertion Failed | afxcmn2.inl | Line: 288_AFXCMN_INLINE int CProgressCtrl::SetPos(int nPos)
{ ASSERT(::IsWindow(m_hWnd)); return (int) ::SendMessage(m_hWnd, PBM_SETPOS, nPos, 0L); }
-
Keine Änderung am Quellcode.
Ein weiterer Fehler in Zeile:VERIFY(::GetExitCodeThread(stockdata_thread->m_hThread, &dwStatus));
-
Der Fehler kommt, wenn man erst alles durchlaufen lässt und dann das Programm beendet.
-
Das habe ich mir vorhin auch überlegt (bovor ich den Rechner eingeschalten habe).
Ich vergaß, dass da noch zwei "Kleinigkeiten" zu beachten sind
.Zunächst ist mir jetzt aber noch aufgefallen, dass als zweiter Parameter an AfxBeginThread(..) der this-Pointer des Dialoges übergeben wird. Der wird dann auch einfach in der Thread-Funktion auf die CDialog-Klasse gecastet. Dass das so ins Auge gehen kann, habe ich neulich schon mal in einem anderen Zusammenhang erläutert. CWnd ist nicht Thread-Save (siehe MFC Dokumentation)! Man übergebe m_hWnd an den Thread, und benutzt z.b. CMyDialog *pDlg = dynamic_cast<CMyDialog *>(CWnd::FromHandle((HWND)pParam) ) in der Thread-Funktion. Ist in jedem Fall sicherer als den CWnd Zeiger zu übergeben, das geht oft (meist in der _DEBUG), manchmal aber auch nicht (und dann sucht man sich schon mal einen Wolf).
Nun zum Eigentlichen:
// Wie oben bemerkt, m_hWnd übergeben, dann die Besonderheit, der letzte Parameter CREATE_SUSPENDED. // Das bewirkt, dass der Thread nicht sofort "los läuft". stockdata_thread = ::AfxBeginThread(load_stockdata_masterthread, m_hWnd, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED ); if(stockdata_thread) { // nun sicherstellen, dass die CWinThread Instanz sich nach Ende nicht selbst zerstört stockdata_thread->m_bAutoDelete = FALSE; // und den Thread laufen lassen stockdata_thread->ResumeThread(); }Dann in der OnDestroy:
void CHSIDepotmanagerDlg::OnDestroy() { DWORD dwStatus; // check current state VERIFY(::GetExitCodeThread(stockdata_thread->m_hThread, &dwStatus)); // wait until the thread has terminated while( dwStatus == STILL_ACTIVE && stockdata_thread->m_hThread != NULL ) { // check, if the thread is running ... ::GetExitCodeThread(stockdata_thread->m_hThread, &dwStatus ); } // nun noch die Thread Instanz löschen delete stockdata_thread; // Erst die Basisklasse Aufrufen, wenn der Thread beendet ist, // weil der Thread das Dialogfenster braucht! CDialog::OnDestroy(); }So sollte es eigentlich gehen.
Gruss, Gio
-
Vielen Dank, dass du dran bleibst Gio aber es scheint noch ein kleiner Fehler drin zu sein.
CHSIDepotmanagerDlg *pDlg = dynamic_cast<CHSIDepotmanagerDlg *>(CWnd::FromHandle((HWND)pParam) );
führt zu
warning C4541: 'dynamic_cast' fuer polymorphen Typ 'class CWnd' mit /GR- verwendet; unvorhersehbares Verhalten moeglich
Fällt dir dazu was ein?
-
Liegt an den Compiler Optionen. dynamic_cast benötigt RTTI.
=> unter den Projekteinstellungen "C/C++" unter der Kategorie "C++ Language" den Schalter "Enable Run-Time Type Information (RTTI)" anschalten. Entspricht dem CL Kommandozeilenparametert "/GR".
Wenn du das nicht willst, dann statt
CHSIDepotmanagerDlg *pDlg = dynamic_cast<CHSIDepotmanagerDlg*>(CWnd::FromHandle((HWND)pParam) );einfach "hart" casten:
CHSIDepotmanagerDlg *pDlg = (CHSIDepotmanagerDlg *)(CWnd::FromHandle((HWND)pParam) );Geht auch. Aber mit RTTI und nachfolgender NULL Pointer-Abfrage habe ich in meiner Applikation gute Erfahrungen gemacht. Das liegt am Design meiner Applikation, und daher der Vorschlag.
Gruss, Gio