bResult = WriteFile(port->m_hComm,
// Handle to COMM Port
port->m_szWriteBuffer, //
// Pointer to message buffer in calling finction
strlen((char*)port->m_szWriteBuffer), // Length of message to send
&BytesSent,
// Where to store the number of bytes sent
&port->m_ov);
// Overlapped structure
// deal with any error codes
if (!bResult)
{
DWORD dwError = GetLastError();
switch (dwError)
{
case ERROR_IO_PENDING:
{
// continue to GetOverlappedResu ts()
BytesSent = 0;
bWrite = false;
break;
}
default:
{
// all other error codes
port->ProcessErrorMessage("Write ile()");
}
}
}
else
{
LeaveCriticalSection(&port->m_csCommunicationSync);
}
} // end if(bWrite)
if (!bWrite)
{
bWrite = true;
bResult = GetOverlappedResult(port->m_hComm, // Handle to COM port
&port->m_ov, // Overlapped structure
&BytesSent, // Stores number of bytes sent
true);
// Wait flag
// deal with the error code
if (!bResult)
{
port->ProcessErrorMessage("GetOverlappedResults() in Wri eFile()");
}
} // end if (!bWrite)
// Verify that the data size send equals what we tried to send
if (BytesSent != strlen((char*)port->m_szWriteBuffer))
{
printf("WARNING: WriteFile() error.. Bytes Sent: %d; Message Len th: %d\n", BytesSent, strlen((char*)port->m_szWriteBuffer));
}
}
// 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;
}
if (bRead)
{
bResult = ReadFile(port->m_hComm, // Handl 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 s ill in progress
// Proceed on to GetOverLappedResults();
bRead = false;
break;
}
default:
{
// Another error has occ red. Process this error.
port->ProcessErrorMessage ("ReadFile()");
break;
}
}
}
else
{
// ReadFile() returned complete. It is not neces ary to call GetOv erlappedResults()
bRead = true;
}
} // close if (bRead)
if (!bRead)
{
bRead = true;
bResult = GetOverlappedResult(port->m_hComm, // Handl 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()");
// notify parent that a byte was received
::SendMessage((port->m_pOwner)->Handle, WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);
} // end forever loop
}
//
// Write a string to the port
//
void TSerialPort::WriteToPort(char* string)
{
assert(m_hComm != 0);
//
// The CommThread Function.
//
DWORD _stdcall TSerialPort::CommThread(LPVOID pParam)
{
// Cast the void pointer passed to the thread back to
// a pointer of TSerialPort class
TSerialPort *port = (TSerialPort*)pParam;
// Set the status variable in the dialog class to
// TRUE to indicate the thread is running.
port->m_bThreadAlive = true;
// 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 immeditly
// because our port was created as an async port (FILE_FLAG_OVERAPPED
// 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 availble 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 hena
// character arrives at the port.
if (!bResult)
{
// If WaitCommEvent() returns FALSE, process the last er or to determin
// the reason..
switch (dwError = GetLastError())
{
case ERROR_IO_PENDING:
{
// This is a normal return value if ther 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 seri us error has
// occured. Process this error.
port->ProcessErrorMessage("WaitCommEvent");
break;
}
}
}
else
{
// If WaitCommEvent() returns TRUE, check to be sure the e 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 si uation occur
// where the first byte to arrive will cause the WaitFor ultipleObj ects()
// function to stop waiting. The WaitForMultipleObjects ) function
// resets the event handle in m_OverlappedStruct.hEvent o the non- signelead state
// as it returns.
//
// If in the time between the reset of this event and th call to
// ReadFile() more bytes arrive, the m_OverlappedStruct. Event hand le will be set again
// to the signeled state. When the call to ReadFile() oc urs, it wi ll
// read all of the bytes from the buffer, and the progra will
// loop back around to WaitCommEvent().
//
// At this point you will be in the situation where m_Ov rlappedStr uct.hEvent is set,
// but there are no bytes available to read. If you pro eed and ca ll
// ReadFile(), it will return immediatly due to the asyn port setu p, but
// GetOverlappedResults() will not return until the next character arrives.
//
// It is not desirable for the GetOverlappedResults() fu ction to b e in
// this state. The thread shutdown event (event 0) and he WriteFi le()
// event (Event2) will not work if the thread is blocked by GetOver lappedResults().
//
// The solution to this is to check the buffer with a ca l to Clear CommError().
// This call will reset the event handle, and if there a e no bytes to read
// we can loop back through WaitCommEvent() again, then roceed.
// If there are really bytes to read, do nothing and pro eed.
// get a handle to the port
m_hComm = CreateFile(szPort,
// communication port string (COMX)
GENERIC_READ | GENERIC_WRITE,
// read/write types
0,
// comm devices must be opened with exclusive accessss
NULL,
// no security attributes
OPEN_EXISTING,
// comm devices must use OPEN_EXISTING
FILE_FLAG_OVERLAPPED,
// Async I/O
0);
// template must be 0 for comm devices
if (m_hComm == INVALID_HANDLE_VALUE)
{
// port not found
delete [] szPort;
delete [] szBaud;
return false;
}
// set the timeout values
m_CommTimeouts.ReadIntervalTimeout = 1000;
m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000;
m_CommTimeouts.ReadTotalTimeoutConstant = 1000;
m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000;
m_CommTimeouts.WriteTotalTimeoutConstant = 1000;
// configure
if (SetCommTimeouts(m_hComm, &m_CommTimeouts))
{
if (SetCommMask(m_hComm, dwCommEvents))
{
if (GetCommState(m_hComm, &m_dcb))
{
m_dcb.fRtsControl = RTS_CONTROL_ENABLE;
// set RTS bit high!
if (BuildCommDCB(szBaud, &m_dcb))
{
if (SetCommState(m_hComm, &m_dcb))
; // normal operation... continu
#define WM_COMM_BREAK_DETECTED WM_USER+1 // A break was detected on input.
#define WM_COMM_CTS_DETECTED WM_USER+2 // The CTS (clear-to-sennd) signal changed state.
#define WM_COMM_DSR_DETECTED WM_USER+3 // The DSR (data-set-reaady) signal changed state.
#define WM_COMM_ERR_DETECTED WM_USER+4 // A line-status error ooccurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.
#define WM_COMM_RING_DETECTED WM_USER+5 // A ring indicator was detected.
#define WM_COMM_RLSD_DETECTED WM_USER+6 // The RLSD (receive-lin-signal-detect) signal changed state.
#define WM_COMM_RXCHAR WM_USER+7 // A character ws received and placed in the input buffer.
#define WM_COMM_RXFLAG_DETECTED WM_USER+8 // The event character wsreceived and placed in the input buffer.
#define WM_COMM_TXEMPTY_DETECTED WM_USER+9 // The last character in the output buffer was sent.
class TSerialPort
{
public:
// contruction and destruction
TSerialPort();
virtual ~TSerialPort();
// Event array.
// One element is used for each event. There are two event handles for each port.
// A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent).
// There is a general shutdown when the port is closed.
HANDLE m_hEventArray[3];