# PROBLEM: Wie schliesse ich einen Thread welches einen Com Port überwacht.



## Indian (29. Dezember 2004)

Hi

*Wie und wo kann ich eine Bedingung in meiner Thread Funktion einbauen, so das der Thread SOFORT beendet wird und mit ihm auch alles aus dem Speicher verschwindet.*
   

kann einfach dieses Problem nicht lösen. Ich überwache meine ComPort über ein Thread. Jedoch schaffe ich es nicht diesen Thread zu beenden. Hab keine Ahnung wie ich denn Thread beenden kann und bis jetzt konnte mir auch keiner GENAU sagen wie. Es funktioniert einfach nicht. Es liegt an der WaitForMultipleObjects() bzw. WaitComEvent() Methode.

Ziel ist es denn Thread für denn einen Dialog zu starten und wenn ein anderer Dialog gestarted wird denn ersten Thread zu beenden und wieder einen Thread für denn 2. Dialog zu starten.


*So starte ich denn Thread*

```
BOOL CSerialPort::StartMonitoring()
{
	if (!(m_Thread = AfxBeginThread(CommThread, this,THREAD_PRIORITY_NORMAL)))
		return FALSE;
	TRACE("Thread started\n");
	m_EndThread = FALSE;

	return TRUE;	
}
```

*So versuche ich Ihn zu beenden (wird jedoch nur pausiert, bringt jedoch nichts)*

```
BOOL CSerialPort::StopMonitoring()
{
	m_EndThread = TRUE;
	m_Thread->SuspendThread();
	CloseHandle(m_hComm);
	TRACE("Thread suspended\n");
	return TRUE;	
}
```

*Meine Thread Funktion*

```
//
//  The CommThread Function.
//
UINT CSerialPort::CommThread(LPVOID pParam)
{
	// Cast the void pointer passed to the thread back to
	// a pointer of CSerialPort class
	CSerialPort *port = (CSerialPort*)pParam;
	
	// Set the status variable in the dialog class to
	// TRUE to indicate the thread is running.
	port->m_bThreadAlive = TRUE;	
		
	// Misc. variables
	DWORD BytesTransfered = 0; 
	DWORD Event = 0;
	DWORD CommEvent = 0;
	DWORD dwError = 0;
	COMSTAT comstat;
	BOOL  bResult = TRUE;

//****Eingfügt aus Receiv Char Funktion weil comstat fehler Run Time Failure #3 Comstat gebracht hat****
	BOOL  bRead = TRUE; 
	DWORD BytesRead = 0;
	unsigned char RXBuff;
//******************************************************************************************************


	// Clear comm buffers at startup
	if (port->m_hComm)		// check if the port is opened
		PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);


	// begin forever loop.  This loop will run as long as the thread is alive.
	for (;;) 
	{ 
		// Make a call to WaitCommEvent().  This call will return immediatly
		// because our port was created as an async port (FILE_FLAG_OVERLAPPED
		// and an m_OverlappedStructerlapped structure specified).  This call will cause the 
		// m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to 
		// be placed in a non-signeled state if there are no bytes available to be read,
		// or to a signeled state if there are bytes available.  If this event handle 
		// is set to the non-signeled state, it will be set to signeled when a 
		// character arrives at the port.

		// we do this for each port!

		bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);

		if (!bResult)  
		{ 
			// If WaitCommEvent() returns FALSE, process the last error to determin
			// the reason..
			switch (dwError = GetLastError()) 
			{ 
			case ERROR_IO_PENDING: 	
				{ 
					// This is a normal return value if there are no bytes
					// to read at the port.
					// Do nothing and continue
					break;
				}
			case 87:
				{
					// Under Windows NT, this value is returned for some reason.
					// I have not investigated why, but it is also a valid reply
					// Also do nothing and continue.
					break;
				}
			default:
				{
					// All other error codes indicate a serious error has
					// occured.  Process this error.
					port->ProcessErrorMessage("WaitCommEvent()");
					break;
				}
			}
		}
		else
		{
			// If WaitCommEvent() returns TRUE, check to be sure there are
			// actually bytes in the buffer to read.  
			//
			// If you are reading more than one byte at a time from the buffer 
			// (which this program does not do) you will have the situation occur 
			// where the first byte to arrive will cause the WaitForMultipleObjects() 
			// function to stop waiting.  The WaitForMultipleObjects() function 
			// resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state
			// as it returns.  
			//
			// If in the time between the reset of this event and the call to 
			// ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again
			// to the signeled state. When the call to ReadFile() occurs, it will 
			// read all of the bytes from the buffer, and the program will
			// loop back around to WaitCommEvent().
			// 
			// At this point you will be in the situation where m_OverlappedStruct.hEvent is set,
			// but there are no bytes available to read.  If you proceed and call
			// ReadFile(), it will return immediatly due to the async port setup, but
			// GetOverlappedResults() will not return until the next character arrives.
			//
			// It is not desirable for the GetOverlappedResults() function to be in 
			// this state.  The thread shutdown event (event 0) and the WriteFile()
			// event (Event2) will not work if the thread is blocked by GetOverlappedResults().
			//
			// The solution to this is to check the buffer with a call to ClearCommError().
			// This call will reset the event handle, and if there are no bytes to read
			// we can loop back through WaitCommEvent() again, then proceed.
			// If there are really bytes to read, do nothing and proceed.
		
			bResult = ClearCommError(port->m_hComm, &dwError, &comstat);

			if (comstat.cbInQue == 0)
				continue;
		}	// end if bResult

		// Main wait function.  This function will normally block the thread
		// until one of nine events occur that require action.
		Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);

		switch (Event)
		{
		case 0:
			{
				// Shutdown event.  This is event zero so it will be
				// the higest priority and be serviced first.

			 	port->m_bThreadAlive = FALSE;
				
				// Kill this thread.  break is not needed, but makes me feel better.
				AfxEndThread(100);
				break;
			}
		case 1:	// read event
			{
				GetCommMask(port->m_hComm, &CommEvent);
				if (CommEvent & EV_CTS)
					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
				if (CommEvent & EV_RXFLAG)
					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
				if (CommEvent & EV_BREAK)
					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
				if (CommEvent & EV_ERR)
					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
				if (CommEvent & EV_RING)
					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
				
				if (CommEvent & EV_RXCHAR)
					// Receive character event from port.
					//ReceiveChar(port,comstat);   //Auskommentiert weil Fehlermeldung müsste funktionieren ka warum

//****Eingfügt aus Receive Char Funktion weil comstat fehler Run Time Failure #3 Comstat gebracht hat****
	for (;;) 
	{ 
		// Gain ownership of the comm port critical section.
		// This process guarantees no other part of this program 
		// is using the port object. 
		
		EnterCriticalSection(&port->m_csCommunicationSync);

		// ClearCommError() will update the COMSTAT structure and
		// clear any other errors.
		
		bResult = ClearCommError(port->m_hComm, &dwError, &comstat);

		LeaveCriticalSection(&port->m_csCommunicationSync);

		// start forever loop.  I use this type of loop because I
		// do not know at runtime how many loops this will have to
		// run. My solution is to start a forever loop and to
		// break out of it when I have processed all of the
		// data available.  Be careful with this approach and
		// be sure your loop will exit.
		// My reasons for this are not as clear in this sample 
		// as it is in my production code, but I have found this 
		// solutiion to be the most efficient way to do this.
		
		if (comstat.cbInQue == 0)
		{
			// break out when all bytes have been read
			break;
		}
						
		EnterCriticalSection(&port->m_csCommunicationSync);

		if (bRead)
		{
			bResult = ReadFile(port->m_hComm,		// Handle to COMM port 
							   &RXBuff,				// RX Buffer Pointer
							   1,					// Read one byte
							   &BytesRead,			// Stores number of bytes read
							   &port->m_ov);		// pointer to the m_ov structure
			// deal with the error code 
			if (!bResult)  
			{ 
				switch (dwError = GetLastError()) 
				{ 
					case ERROR_IO_PENDING: 	
						{ 
							// asynchronous i/o is still in progress 
							// Proceed on to GetOverlappedResults();
							bRead = FALSE;
							break;
						}
					default:
						{
							// Another error has occured.  Process this error.
							port->ProcessErrorMessage("ReadFile()");
							break;
						} 
				}
			}
			else
			{
				// ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
				bRead = TRUE;
			}
		}  // close if (bRead)

		if (!bRead)
		{
			bRead = TRUE;
			bResult = GetOverlappedResult(port->m_hComm,	// Handle to COMM port 
										  &port->m_ov,		// Overlapped structure
										  &BytesRead,		// Stores number of bytes read
										  TRUE); 			// Wait flag

			// deal with the error code 
			if (!bResult)  
			{
				port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");
			}	
		}  // close if (!bRead)
				
		LeaveCriticalSection(&port->m_csCommunicationSync);

		// notify parent that a byte was received
		::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);
	} // end forever loop

					
				break;
			}  
		case 2: // write event
			{
				// Write character event from port
				WriteChar(port);
				break;
			}

		} // end switch

	} // close forever loop
//******************************************************************************************************
	AfxEndThread(True);
	return 0;
}
```



HILFE


----------



## Tobias K. (29. Dezember 2004)

moin


Du könntest deinen Thread bei jedem Durchlauf eine Adresse überprüfen lassen, die ihr in Form eines Pointers mitgegeben wurde.
Dein "Hauptprogramm" kennt diese Adresse natürlich auch und kann dort einen bestimmten Wert ablegen.
Da der Thread die Adresse bei jedem Durchlauf überprüft, kann er sich dann beenden wenn unter der Adresse ein der Wert zum beenden liegt.

So kannst du natürlich auch andereFunktionen des Threads steuern...


mfg
umbrasaxum


----------



## Endurion (29. Dezember 2004)

Deine WaitForMultipleObjects macht doch genau, was du willst. Da wird eine Reihe Events abgefragt. Setze selber eins dazu (mit CreateEvent erzeugt) und wenn du den Thread beenden willst, dann SetEvent dieses Event. Das WaitForMultipleObjects springt an der Stelle raus, du fragst ab, ob es dein Event war und lässt den Thread einfach aus der Endlosschleife rausspringen (ein Thread ist beendet, wenn seine Hauptroutine beendet wird). Fettich.


----------



## Indian (30. Dezember 2004)

Endurion:

danke, ich hatte sowas auch vermutet. Jedoch weis ich nicht genau welches Event ich hier setzen muss. 

ich hab ein Handle *m_hEventArray*, welches 3 Felder enthält. Das m_hEventArray[0] enthält das shutdown event. Folgende Zeile warted natürlich auch ein event:



> Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);



jetzt weiss ich leider nicht wie ich denn ersten Wert des Array hier beeinflussen kann so das diese nächste zeile ausgefürht werden kann. Eine variante könnte möglich sein, wenn ich HANDLE				m_hShutdownEvent; als public deklariere und diese dann im Hauptprogramm einfach setze. Würde das funktionieren ?



> switch (Event)
> {
> case 0:
> {
> ...



 Kannst du mir hierbei helfen ?


----------



## Endurion (30. Dezember 2004)

Genauso eigentlich, wenn das ShutDown-Event nur für die Stelle verwendet wird, reicht ein SetEvent mit dem Handle drin (ob public oder getter oder global ist egal), und dann sollte WaitForMultipleObjects da sofort rausspringen. Du musst natürlich bei der Abfrage sichergehen, dass es auch das ShutDown-Event ist, aber die Abfrage hast du ja schon drin. Ein einfaches return in der Threadroutine sollte reichen, so dass der Prozessor da rausspring und den Thread sauber beendet. Eventuell ein paar Sachen aufräumen, falls du das nicht schon woanders machst.

Sollte so aussehen:

SetEvent( port->m_hEventArray[0] );


----------



## Indian (30. Dezember 2004)

also ich habs versucht.

Wenn ich das Event setze reagiert leider das Thread nicht. WaitForMultipleObjects reagiert einfach nicht darauf. Habe einen stop an der stelle gesetzt welcher nie aufgerufen wird.

Ich bin momentan mit meinem Latein vollkommen am  ende und ich weiss leider auch nicht mehr was ich fragen soll bzw. wo ich noch suchen soll. Mein Projekt ist nun fertig zu 99,9% ist aber nutzlos wenn ich diesen Thread welchen denn ComPort überwacht nicht beenden kann. 

Es muss doch irgendwie möglich diesen zu beenden ?


----------



## Endurion (31. Dezember 2004)

Hängt der denn bei WaitForMultipleObjects oder schon bei WaitCommEvent?

Wenn das gar nicht geht, kannst du da ein kleines Beispielprojekt nur mit den Threads machen und das hier reinstellen; dann gugg ich da mal rüber.


----------



## Indian (31. Dezember 2004)

danke für deine hilfe

irgendwie funktioniert das wohl nicht so wie es funktionieren soll. Ob das nun an Visual Studio Net liegt oder am zeitlichen Abstand, kann ich leider jetzt nicht beurteilen. Ich denke denn Thread zu schliessen und wieder zu erzeugen im anderen Dialog, damit der ComPort überwacht werden kann, funktioniert wirklich nicht vom zeitlichen hergesehen. Ich meine die Freigabe bzw. belegen einer Windowsressources funktioniert einwandfrei wenn ich step by step im debug modus durch gehen. Ohne debug macht es bum oder es bleibt alles stehen. 

Naja wie auch immer. Ich habs nun anderst gemacht. Ich überwache denn ComPort und lasse denn Thread nur im Hauptdialog laufen und übergebe ganz einfach die eingelesen daten zu den anderen Dialogen. Natürlich setze ich vorher eine Variable auf true damit der Wert nicht im Hauptdialog bearbeitet werden soll sondern übergeben wird zu dem aufgerufenen Dialog. 

Mal ne frage hat Visual Studio Net ein paar macken ?  Gibts da auch updates. Denn manchmal verändere ich gar nichts und compilierte Programme funktioniern und mal wieder nicht. Mal stürzen sie ab mal wieder nicht.  

Komisch.

Danke dir vielmals für deine Hilfe. Jedoch wäre ich fast verrückt werden.


----------



## Endurion (31. Dezember 2004)

Eher im Gegenteil, Studio .NET ist so ziemlich das Beste, was man kriegen kann. Ein paar lästige Sachen muss man halt abstellen (Dynamische Hilfe, was ein Sch***). Aber im Gegensatz zu VC6 ist es hervorragend. Man braucht leider einen bisschen fitteren Rechner dafür.


----------



## Indian (3. Januar 2005)

naja,

mir kommt es nur so vor als würde der Kompiler ein paar Fehler machen. Wie gesagt ich habs nun anderst gemacht. Jetzt muss ich noch ein paar feinarbeiten mach und denn code dokumentieren dann bin ich fertig. 

Kann man auch eine AutoDokumentation erstellen die man kürz über denn Code laufen lässt und dann eine html dokumentation erhält ? Ich dachte ich hab schon mal sowas gehört bzw. schon irgendwo gelesen ? Irgendein Tip ?


----------



## Endurion (3. Januar 2005)

Doxygen kann sowas, dummerweise sind die meisten Autodokumentierer auf ein bestimmtes Kommentarformat angewiesen.

[edit: Das waren mehr als 60 Sekunden]


----------



## Indian (3. Januar 2005)

Also hat Visual Studio NET nichts integeriert ?..


Thx ich schau mir mal das an


----------

