Debug Assertion failed | afxcmn.inl line 132



  • Soweit richtig. Ich kann mir einfach nicht erklären, warum der Fehler nur auftritt, wenn man das Programm abbricht bevor der Thread durchgelaufen ist.

    Hier etwas mehr Details:

    Detected memory leaks!
    Dumping objects ->
    strcore.cpp(118) : {116134} normal block at 0x00338068, 20 bytes long.
    Data: < 4260> 01 00 00 00 07 00 00 00 07 00 00 00 34 32 36 30
    strcore.cpp(118) : {116129} normal block at 0x00338010, 22 bytes long.
    Data: < 31-J> 01 00 00 00 09 00 00 00 09 00 00 00 33 31 2D 4A
    strcore.cpp(118) : {114763} normal block at 0x00339130, 141 bytes long.
    Data: < / 31-J> 01 00 00 00 2F 00 00 00 80 00 00 00 33 31 2D 4A
    strcore.cpp(118) : {109840} normal block at 0x00336288, 22 bytes long.
    Data: < 17-J> 01 00 00 00 09 00 00 00 09 00 00 00 31 37 2D 4A
    strcore.cpp(118) : {109837} normal block at 0x003360D8, 154 bytes long.
    Data: < C:\D> 01 00 00 00 8D 00 00 00 8D 00 00 00 43 3A 5C 44
    strcore.cpp(118) : {109835} normal block at 0x00334DB8, 56 bytes long.
    Data: < + + Date> 01 00 00 00 2B 00 00 00 2B 00 00 00 44 61 74 65
    strcore.cpp(118) : {109834} normal block at 0x003361B0, 154 bytes long.
    Data: < C:\D> 01 00 00 00 8D 00 00 00 8D 00 00 00 43 3A 5C 44
    strcore.cpp(118) : {109832} normal block at 0x00336080, 23 bytes long.
    Data: < BAS_> 01 00 00 00 0A 00 00 00 0A 00 00 00 42 41 53 5F
    strcore.cpp(118) : {109830} normal block at 0x00335F28, 269 bytes long.
    Data: < BAS.> 01 00 00 00 06 00 00 00 00 01 00 00 42 41 53 2E
    thrdcore.cpp(166) : {109825} client block at 0x00335BF0, subtype 0, 112 bytes long.
    a CWinThread object at $00335BF0, 112 bytes long
    thrdcore.cpp(166) : {326} client block at 0x00335290, subtype 0, 112 bytes long.
    a CWinThread object at $00335290, 112 bytes long
    Object dump complete.
    Thread 0x1004 wurde mit Code 2 (0x2) beendet.
    Thread 0xCE8 wurde mit Code 2 (0x2) beendet.
    Das Programm wurde mit Code 2 (0x2) beendet.



  • Wie wäre es mit einem kleinen Codeauszug, um die Zusammenhänge zwischen Dialog und Thread etwas darzustellen?

    Gruss, Gio



  • Es ist mir schon wieder passiert, den Beitrag nicht ganz genau zu lesen.

    ..., warum der Fehler nur auftritt, wenn man das Programm abbricht bevor der Thread durchgelaufen ist.
    

    Bei Schließen muss das Fenster, das den Thread erzeugt hat dafür Sorge tragen, dass der Thread beendet ist, bevor das Fenster geschlossen wird.

    Irgendwo im Dialog passiert wohl ein CreateThread(..) oder ***AfxCreateThread(..)***. Wenn nun das Fenster (also der Dialog) geschlossen wird, bevor der Thread durchgelaufen ist, muss der Dialog dafür sorgen, dass der Thread beendet ist, bevor er selbst zerstört wird.

    Wie das im Einzelfall geht, kommt immer auf die Anwendung an.

    Gruss, Gio



  • 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