meteora
December 8th, 2004, 04:33 PM
Hi,
I am newbie to windows threads. I wrote a class to provide GPS access and manipulation. The Initialize function spawns a thread(using AfxBeginThread) that monitors the serial io port. The Code in GPS:::Initialize is as follows
Note that GPS inherits from my SerialIO class
GPS::Initialize{
...some intialization code
InitPort(reinterpret_cast<CWnd * >(this),1);
StartMonitoring();//this is a function in SerialIO class the does a afxbeginthread of serial io communication
while(!Die){
GetGPSData();// This parses the GPS string and performs the necessary operationson it
}
...
}
SerialIOclass code in Serialio.h:
#include "queue.h"
static CQUEUE<TEXT> Buffer(100);
SERIALIO {
BOOL StartMonitoring();
static UINT CommThread(VOID* pParam);
static void ReceiveChar(SERIALIO* port,COMSTAT comstat);
}
SerialIOclass code in Serialio.cpp:
BOOL SERIALIO::StartMonitoring() {
if(!(Thread = AfxBeginThread(CommThread, this))) {
return FALSE;
}
TRACE("Thread started\n");
return TRUE;
}
INT SERIALIO::CommThread(VOID* pParam) {
// Cast the void pointer passed to the thread back to
// a pointer of SERIALIO class
SERIALIO* port = (SERIALIO*) pParam;
// Set the status variable in the dialog class to
// TRUE to indicate the thread is running.
port->TreadAlive = TRUE;
// Misc. variables
DWORD BytesTransfered = 0;
DWORD Event = 0;
DWORD CommEvent = 0;
DWORD dwError = 0;
COMSTAT comstat;
BOOL bResult = TRUE;
// Clear comm buffers at startup
if(port->hComm) { // check if the port is opened
PurgeComm(port->hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
}
// begin forever loop. This loop will run as long as the thread is alive.
for(; ;) {
bResult = !!WaitCommEvent(port->hComm, &Event, &port->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->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->TreadAlive = FALSE;
// Kill this thread. break is not needed, but makes me feel better.
AfxEndThread(100);
break;
}
case 1: {
// read event
GetCommMask(port->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) {
ReceiveChar(port, comstat);// Receive character event from port.
}
break;
}
case 2: { // write event
// Write character event from port
WriteChar(port);
break;
}
} // end switch
} // close forever loop
return 0;
}
//
void SERIALIO::ReceiveChar(SERIALIO* port,COMSTAT comstat) {
BOOL bRead = TRUE;
BOOL bResult = TRUE;
DWORD dwError = 0;
DWORD BytesRead = 0;
TEXT RxChar;
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->CommunicationSync);
// ClearCommError() will update the COMSTAT structure and
// clear any other errors.
bResult = !!ClearCommError(port->hComm, &dwError, &comstat);
LeaveCriticalSection(&port->CommunicationSync);
if(comstat.cbInQue == 0) {
// break out when all bytes have been read
break;
}
EnterCriticalSection(&port->CommunicationSync);
if(bRead) {
// Handle to COMM port
// RX Buffer Pointer
// Read one byte
// Stores number of bytes read
// pointer to the ov structure
bResult = !!ReadFile(port->hComm,&RxChar,1,&BytesRead,&port->ov);
// 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->hComm, // Handle to COMM port
&port->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)
Buffer.Add(RxChar);
LeaveCriticalSection(&port->CommunicationSync);
// notify parent that a byte was received
::SendMessage(/*reinterpret_cast <HWND>*/ (port->m_pOwner), WM_COMM_RXCHAR, (WPARAM) RxChar, (LPARAM) port->m_nPortNr);
} // end forever loop
}
Issue:
The SerialIO class has code to receive characters from a COM port with critical sections encapsulating the read functionality. The characters are being read as long as i receive characters(so its on a infinite loop till no characters are available).
I have a GLobal Circular Queue( i created a class called CQUEUE like foundation classes) which performed operations on queue of any type. This is a user defined class (used similar to templates like... CQueue<TEXT> Buffer(1000);)
i initialized the CQueue globally in the SerialIO.h file as GPS also includes that file. Add into CQueue the characters i receive from the serial io port...but i am not able to get proceed with main program execution properly or i dont see the queue being updated in the main program.
Can anyone help me on this.
1) How do i get the control in the main app after i transfer control to the commthread...
and
2) how do i read out ofe the modified CQUEUE in the main program (using Buffer.Extract in GetGPSDATA())? When i read out ...i dont see the modifications done in the serialio class
Remember: i am reading from the port, writing to the cqueue(in thread) and trying to read out of the cqueue (in main)
p.s: i tried windows messaging...dint help in reading out of the global cqueue!!
visual c++ 7 doesnt allow me to use "volatile" on my CQUEUE type as it says conversion from volatile CQUEUE& to CQUEUE& is not allowed->when i call Buffer.Add()/Buffer.Extract().
I am newbie to windows threads. I wrote a class to provide GPS access and manipulation. The Initialize function spawns a thread(using AfxBeginThread) that monitors the serial io port. The Code in GPS:::Initialize is as follows
Note that GPS inherits from my SerialIO class
GPS::Initialize{
...some intialization code
InitPort(reinterpret_cast<CWnd * >(this),1);
StartMonitoring();//this is a function in SerialIO class the does a afxbeginthread of serial io communication
while(!Die){
GetGPSData();// This parses the GPS string and performs the necessary operationson it
}
...
}
SerialIOclass code in Serialio.h:
#include "queue.h"
static CQUEUE<TEXT> Buffer(100);
SERIALIO {
BOOL StartMonitoring();
static UINT CommThread(VOID* pParam);
static void ReceiveChar(SERIALIO* port,COMSTAT comstat);
}
SerialIOclass code in Serialio.cpp:
BOOL SERIALIO::StartMonitoring() {
if(!(Thread = AfxBeginThread(CommThread, this))) {
return FALSE;
}
TRACE("Thread started\n");
return TRUE;
}
INT SERIALIO::CommThread(VOID* pParam) {
// Cast the void pointer passed to the thread back to
// a pointer of SERIALIO class
SERIALIO* port = (SERIALIO*) pParam;
// Set the status variable in the dialog class to
// TRUE to indicate the thread is running.
port->TreadAlive = TRUE;
// Misc. variables
DWORD BytesTransfered = 0;
DWORD Event = 0;
DWORD CommEvent = 0;
DWORD dwError = 0;
COMSTAT comstat;
BOOL bResult = TRUE;
// Clear comm buffers at startup
if(port->hComm) { // check if the port is opened
PurgeComm(port->hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
}
// begin forever loop. This loop will run as long as the thread is alive.
for(; ;) {
bResult = !!WaitCommEvent(port->hComm, &Event, &port->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->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->TreadAlive = FALSE;
// Kill this thread. break is not needed, but makes me feel better.
AfxEndThread(100);
break;
}
case 1: {
// read event
GetCommMask(port->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) {
ReceiveChar(port, comstat);// Receive character event from port.
}
break;
}
case 2: { // write event
// Write character event from port
WriteChar(port);
break;
}
} // end switch
} // close forever loop
return 0;
}
//
void SERIALIO::ReceiveChar(SERIALIO* port,COMSTAT comstat) {
BOOL bRead = TRUE;
BOOL bResult = TRUE;
DWORD dwError = 0;
DWORD BytesRead = 0;
TEXT RxChar;
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->CommunicationSync);
// ClearCommError() will update the COMSTAT structure and
// clear any other errors.
bResult = !!ClearCommError(port->hComm, &dwError, &comstat);
LeaveCriticalSection(&port->CommunicationSync);
if(comstat.cbInQue == 0) {
// break out when all bytes have been read
break;
}
EnterCriticalSection(&port->CommunicationSync);
if(bRead) {
// Handle to COMM port
// RX Buffer Pointer
// Read one byte
// Stores number of bytes read
// pointer to the ov structure
bResult = !!ReadFile(port->hComm,&RxChar,1,&BytesRead,&port->ov);
// 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->hComm, // Handle to COMM port
&port->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)
Buffer.Add(RxChar);
LeaveCriticalSection(&port->CommunicationSync);
// notify parent that a byte was received
::SendMessage(/*reinterpret_cast <HWND>*/ (port->m_pOwner), WM_COMM_RXCHAR, (WPARAM) RxChar, (LPARAM) port->m_nPortNr);
} // end forever loop
}
Issue:
The SerialIO class has code to receive characters from a COM port with critical sections encapsulating the read functionality. The characters are being read as long as i receive characters(so its on a infinite loop till no characters are available).
I have a GLobal Circular Queue( i created a class called CQUEUE like foundation classes) which performed operations on queue of any type. This is a user defined class (used similar to templates like... CQueue<TEXT> Buffer(1000);)
i initialized the CQueue globally in the SerialIO.h file as GPS also includes that file. Add into CQueue the characters i receive from the serial io port...but i am not able to get proceed with main program execution properly or i dont see the queue being updated in the main program.
Can anyone help me on this.
1) How do i get the control in the main app after i transfer control to the commthread...
and
2) how do i read out ofe the modified CQUEUE in the main program (using Buffer.Extract in GetGPSDATA())? When i read out ...i dont see the modifications done in the serialio class
Remember: i am reading from the port, writing to the cqueue(in thread) and trying to read out of the cqueue (in main)
p.s: i tried windows messaging...dint help in reading out of the global cqueue!!
visual c++ 7 doesnt allow me to use "volatile" on my CQUEUE type as it says conversion from volatile CQUEUE& to CQUEUE& is not allowed->when i call Buffer.Add()/Buffer.Extract().