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


Anmelden zum Antworten