// Serial.cpp: implementation of the CSerial class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Serial.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
#pragma warning (disable : 4127)
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSerial::CSerial() :
m_pParent(NULL),
m_heStop(NULL),
m_heSerialEvent(NULL),
m_hThread(NULL),
m_lLastError(ERROR_SUCCESS),
m_hPort(NULL),
m_eEvent(EEVENT_NONE),
m_heOverlapped(NULL),
m_lpfunc(NULL)
{
}
CSerial::~CSerial()
{
if (NULL != m_hThread)
{
_RPTF0(_CRT_WARN, "CSerial::~CSerial():\tSerial port not closed, closing explicitly\n");
Close();
}
}
CSerial::Availability CSerial::CheckPort(LPCTSTR lpszDevice)
{
Availability Ret = EAVAILABILITY_AVAILABLE;
HANDLE hPort = ::CreateFile(lpszDevice, GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (INVALID_HANDLE_VALUE == hPort)
{
switch(::GetLastError())
{
case ERROR_FILE_NOT_FOUND: // no such port
Ret = EAVAILABILITY_NOTAVAILABLE;
break;
case ERROR_ACCESS_DENIED: // in use
Ret = EAVAILABILITY_INUSE;
break;
default:
Ret = EAVAILABILITY_UNKNOWN;
break;
}
}
else
{
::CloseHandle(hPort);
}
return Ret;
}
LONG CSerial::Open(LPCTSTR lpszDevice, void* pParent, LPSERIALFUNC lpSer, DWORD dwInQueue, DWORD dwOutQueue)
{
m_lLastError = ERROR_SUCCESS;
ASSERT(NULL != pParent);
ASSERT(NULL != lpSer);
if (NULL != m_hPort)
{
m_lLastError = ERROR_ALREADY_INITIALIZED;
_RPTF0(_CRT_WARN, "CSerial::Open():\tPort already Opened\n");
return m_lLastError;
}
// Add FILE_SHARE_READ | FILE_SHARE_WRITE to allow for multiple threads to access com port (must use mutex)
m_hPort = ::CreateFile(lpszDevice, GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (INVALID_HANDLE_VALUE == m_hPort)
{
m_hPort = NULL;
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::Open():\tUnable to open port\n");
return m_lLastError;
}
ASSERT(NULL == m_heOverlapped);
m_heOverlapped = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == m_heOverlapped)
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::Open():\tUnable to create event\n");
::CloseHandle(m_hPort);
m_hPort = NULL;
return m_lLastError;
}
if (FALSE == ::SetupComm(m_hPort, dwInQueue, dwOutQueue))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::Open():\tUnable to setup the Serial port\n");
Close();
return m_lLastError;
}
SetEventMask();
SetBaudRate(EBAUDRATE_9600);
SetParity(EPARITY_NONE);
SetDataBits(EDATABITS_8);
SetStopBits(ESTOPBITS_1);
SetHandshaking(EHANDSHAKE_OFF);
SetReadTimeout(ETIMEOUT_NONBLOCKING);
m_heStop = ::CreateEvent(NULL, TRUE, FALSE, NULL);
m_heSerialEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == m_heStop || NULL == m_heSerialEvent)
{
LONG lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::Open():\tUnable to create events\n");
Close();
m_lLastError = lLastError;
return m_lLastError;
}
DWORD dwThreadID = 0;
m_hThread = ::CreateThread(NULL, NULL, ThreadProc, (LPVOID)this, CREATE_SUSPENDED, &dwThreadID);
if (NULL == m_hThread)
{
LONG lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::Open():\tUnable to start Serial thread\n");
::CloseHandle(m_heStop);
m_heStop = NULL;
::CloseHandle(m_heSerialEvent);
m_heSerialEvent = NULL;
Close();
m_lLastError = lLastError;
return m_lLastError;
}
m_pParent = pParent;
m_lpfunc = lpSer;
::ResumeThread(m_hThread);
return m_lLastError;
}
LONG CSerial::Close()
{
if (NULL != m_hThread)
{
::SetEvent(m_heStop);
::WaitForSingleObject(m_hThread, INFINITE);
::CloseHandle(m_hThread);
::CloseHandle(m_heStop);
::CloseHandle(m_heSerialEvent);
m_hThread = NULL;
m_heStop = NULL;
m_heSerialEvent = NULL;
}
m_pParent = NULL;
m_lpfunc = NULL;
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
_RPTF0(_CRT_WARN, "CSerial::Close():\tDon't need to close when device is not open\n");
return m_lLastError;
}
::CloseHandle(m_heOverlapped);
m_heOverlapped = NULL;
::CloseHandle(m_hPort);
m_hPort = NULL;
return m_lLastError;
}
LONG CSerial::SetBaudRate(BaudRate Baud)
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::SetBaudRate():\tDevice not open\n");
return m_lLastError;
}
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (FALSE == ::GetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetBaudRate():\tUnable to open COM state information\n");
return m_lLastError;
}
switch (Baud)
{
case EBAUDRATE_110:
case EBAUDRATE_300:
case EBAUDRATE_600:
case EBAUDRATE_1200:
case EBAUDRATE_2400:
case EBAUDRATE_4800:
case EBAUDRATE_9600:
case EBAUDRATE_14400:
case EBAUDRATE_19200:
case EBAUDRATE_38400:
case EBAUDRATE_56000:
case EBAUDRATE_57600:
case EBAUDRATE_115200:
case EBAUDRATE_128000:
case EBAUDRATE_256000:
dcb.BaudRate = (DWORD)Baud;
break;
default:
dcb.BaudRate = (DWORD)EBAUDRATE_38400;
break;
}
if (FALSE == ::SetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetBaudRate():\tUnable to set COM state information\n");
return m_lLastError;
}
return m_lLastError;
}
LONG CSerial::SetParity(Parity Par)
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::SetParity():\tDevice not open\n");
return m_lLastError;
}
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (FALSE == ::GetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetParity():\tUnable to open COM state information\n");
return m_lLastError;
}
switch (Par)
{
case EPARITY_NONE:
case EPARITY_EVEN:
case EPARITY_ODD:
case EPARITY_SPACE:
case EPARITY_MARK:
dcb.Parity = (BYTE)Par;
dcb.fParity = (Par != EPARITY_NONE);
break;
default:
dcb.Parity = (BYTE)EPARITY_NONE;
dcb.fParity = FALSE;
break;
}
if (FALSE == ::SetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetParity():\tUnable to set COM state information\n");
return m_lLastError;
}
return m_lLastError;
}
LONG CSerial::SetDataBits(DataBits Data)
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::SetDataBits():\tDevice not open\n");
return m_lLastError;
}
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (FALSE == ::GetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetDataBits():\tUnable to open COM state information\n");
return m_lLastError;
}
switch (Data)
{
case EDATABITS_5:
case EDATABITS_6:
case EDATABITS_7:
case EDATABITS_8:
dcb.ByteSize = (BYTE)Data;
break;
default:
dcb.ByteSize = (BYTE)EDATABITS_8;
break;
}
if (FALSE == ::SetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetDataBits():\tUnable to set COM state information\n");
return m_lLastError;
}
return m_lLastError;
}
LONG CSerial::SetStopBits(StopBits Stop)
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::SetStopBits():\tDevice not open\n");
return m_lLastError;
}
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (FALSE == ::GetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetStopBits():\tUnable to open COM state information\n");
return m_lLastError;
}
switch (Stop)
{
case ESTOPBITS_1:
case ESTOPBITS_1_5:
case ESTOPBITS_2:
dcb.StopBits = (BYTE)Stop;
break;
default:
dcb.StopBits = (BYTE)ESTOPBITS_1;
break;
}
if (FALSE == ::SetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetStopBits():\tUnable to set COM state information\n");
return m_lLastError;
}
return m_lLastError;
}
LONG CSerial::SetHandshaking(HandShaking Hand)
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::SetHandshaking():\tDevice not open\n");
return m_lLastError;
}
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (FALSE == ::GetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetHandshaking():\tUnable to open COM state information\n");
return m_lLastError;
}
switch (Hand)
{
case EHANDSHAKE_OFF:
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
break;
case EHANDSHAKE_SOFTWARE:
dcb.fOutxCtsFlow = TRUE;
dcb.fOutxDsrFlow = TRUE;
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
break;
case EHANDSHAKE_HARDWARE:
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fOutX = TRUE;
dcb.fInX = TRUE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
break;
default: // by default, turn it off
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
break;
// ASSERT(!"Invalid Handshaking Mode");
// m_lLastError = E_INVALIDARG;
// return m_lLastError;
}
if (FALSE == ::SetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetHandshaking():\tUnable to set COM state information\n");
return m_lLastError;
}
return m_lLastError;
}
LONG CSerial::SetEventChar(BYTE ch, BOOL bAdjustMask)
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::SetStopBits():\tDevice not open\n");
return m_lLastError;
}
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (FALSE == ::GetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetStopBits():\tUnable to open COM state information\n");
return m_lLastError;
}
dcb.EvtChar = (char)ch;
if (TRUE == bAdjustMask)
{
SetEventMask(GetEventMask() | EEVENT_RECVEV);
}
if (FALSE == ::SetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetStopBits():\tUnable to set COM state information\n");
return m_lLastError;
}
return m_lLastError;
}
LONG CSerial::SetEventMask(DWORD dwMask)
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::SetEventMask():\tDevice is not open\n");
return m_lLastError;
}
if (FALSE == ::SetCommMask(m_hPort, dwMask))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetEventMask():\tUnable to set event mask\n");
return m_lLastError;
}
return m_lLastError;
}
LONG CSerial::SetReadTimeout(Timeout Time)
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::SetReadTimeout():\tDevice is not open\n");
return m_lLastError;
}
COMMTIMEOUTS cto;
if (FALSE == ::GetCommTimeouts(m_hPort, &cto))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetReadTimeout():\tUnable to read timeout information\n");
return m_lLastError;
}
switch (Time)
{
case ETIMEOUT_BLOCKING:
cto.ReadIntervalTimeout = 0;
cto.ReadTotalTimeoutConstant = 0;
cto.ReadTotalTimeoutMultiplier = 0;
break;
case ETIMEOUT_NONBLOCKING:
cto.ReadIntervalTimeout = MAXDWORD;
cto.ReadTotalTimeoutConstant = 0;
cto.ReadTotalTimeoutMultiplier = 0;
break;
default:
ASSERT(!"Invalid Timeout Mode");
m_lLastError = E_INVALIDARG;
return m_lLastError;
}
if (FALSE == ::SetCommTimeouts(m_hPort, &cto))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::SetReadTimeout():\tUnable to set timeout information\n");
return m_lLastError;
}
return m_lLastError;
}
CSerial::BaudRate CSerial::GetBaudRate()
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::GetBaudRate():\tDevice is not open\n");
return EBAUDRATE_UNKNOWN;
}
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (FALSE == ::GetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::GetBaudRate():\tUnable to read DCB information\n");
return EBAUDRATE_UNKNOWN;
}
return (BaudRate)dcb.BaudRate;
}
CSerial::Parity CSerial::GetParity()
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::GetParity():\tDevice is not open\n");
return EPARITY_UNKNOWN;
}
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (FALSE == ::GetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::GetParity():\tUnable to read DCB information\n");
return EPARITY_UNKNOWN;
}
if (FALSE == dcb.fParity)
{
return EPARITY_NONE;
}
return (Parity)dcb.Parity;
}
CSerial::DataBits CSerial::GetDataBits()
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::GetDataBits():\tDevice is not open\n");
return EDATABITS_UNKNOWN;
}
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (FALSE == ::GetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::GetDataBits():\tUnable to read DCB information\n");
return EDATABITS_UNKNOWN;
}
return (DataBits)dcb.ByteSize;
}
CSerial::StopBits CSerial::GetStopBits()
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::GetStopBits():\tDevice is not open\n");
return ESTOPBITS_UNKNOWN;
}
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (FALSE == ::GetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::GetStopBits():\tUnable to read DCB information\n");
return ESTOPBITS_UNKNOWN;
}
return (StopBits)dcb.ByteSize;
}
CSerial::HandShaking CSerial::GetHandshaking()
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::GetHandShaking():\tDevice is not open\n");
return EHANDSHAKE_UNKNOWN;
}
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (FALSE == ::GetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::GetHandShaking():\tUnable to read DCB information\n");
return EHANDSHAKE_UNKNOWN;
}
if (DTR_CONTROL_HANDSHAKE == dcb.fDtrControl && RTS_CONTROL_HANDSHAKE == dcb.fRtsControl)
return EHANDSHAKE_HARDWARE;
if (TRUE == dcb.fInX && TRUE == dcb.fOutX)
return EHANDSHAKE_SOFTWARE;
return EHANDSHAKE_OFF;
}
BYTE CSerial::GetEventChar()
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::GetEventChar():\tDevice is not open\n");
return NULL;
}
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (FALSE == ::GetCommState(m_hPort, &dcb))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::GetEventChar():\tUnable to read DCB information\n");
return NULL;
}
return (BYTE)dcb.EvtChar;
}
DWORD CSerial::GetEventMask()
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::GetEventMask():\tDevice is not open\n");
return NULL;
}
DWORD dwMask;
if (FALSE == ::GetCommMask(m_hPort, &dwMask))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::GetEventMask():\tUnable to read mask information\n");
return NULL;
}
return dwMask;
}
LONG CSerial::Write(const void* pData, DWORD dwSize, DWORD* pdwWrote, LPOVERLAPPED lpOverlapped, DWORD dwTimeout)
{
ASSERT((NULL == lpOverlapped || NULL != pdwWrote) || !"Overlapped Operation should specify the Wrote variable");
m_lLastError = ERROR_SUCCESS;
DWORD dwWrote = 0;
if (NULL == pdwWrote)
pdwWrote = &dwWrote;
*pdwWrote = 0;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::Write():\tDevice is not open\n");
return m_lLastError;
}
OVERLAPPED ovl;
if (NULL == lpOverlapped)
{
memset(&ovl, 0, sizeof(OVERLAPPED));
ovl.hEvent = m_heOverlapped;
lpOverlapped = &ovl;
}
ASSERT(HasOverlappedIoCompleted(lpOverlapped) || !"Overlapped Operation in progress");
if (FALSE == ::WriteFile(m_hPort, pData, dwSize, pdwWrote, lpOverlapped))
{
LONG lLastError = ::GetLastError();
if (ERROR_IO_PENDING != lLastError)
{
m_lLastError = lLastError;
_RPTF0(_CRT_WARN, "CSerial::Write():\tUnable to write data\n");
return m_lLastError;
}
if (&ovl == lpOverlapped)
{
switch(::WaitForSingleObject(lpOverlapped->hEvent, dwTimeout))
{
case WAIT_OBJECT_0:
if (FALSE == ::GetOverlappedResult(m_hPort, lpOverlapped, pdwWrote, FALSE))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::Write():\tOverlapped completed with no results\n");
return m_lLastError;
}
break;
case WAIT_TIMEOUT:
::CancelIo(m_hPort);
m_lLastError = ERROR_TIMEOUT;
return m_lLastError;
default:
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::Write():\tUnable to wait for data to be sent\n");
return m_lLastError;
}
}
}
else
{
::SetEvent(lpOverlapped->hEvent);
}
return m_lLastError;
}
LONG CSerial::Read(void* pData, DWORD dwSize, DWORD* pdwRead, LPOVERLAPPED lpOverlapped, DWORD dwTimeout)
{
ASSERT((NULL == lpOverlapped || NULL != pdwRead) || !"Overlapped Operation should specify the Read variable");
m_lLastError = ERROR_SUCCESS;
DWORD dwRead = 0;
if (NULL == pdwRead)
pdwRead = &dwRead;
*pdwRead = 0;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::Read():\tDevice is not open\n");
return m_lLastError;
}
OVERLAPPED ovl;
if (NULL == lpOverlapped)
{
memset(&ovl, 0, sizeof(ovl));
ovl.hEvent = m_heOverlapped;
lpOverlapped = &ovl;
}
ASSERT(HasOverlappedIoCompleted(lpOverlapped) || !"Overlapped Operation in progress");
#ifdef _DEBUG
memset(pData, 0xDC, dwSize); // help to find buffer overrun problems
#endif // _DEBUG
if (FALSE == ::ReadFile(m_hPort, pData, dwSize, pdwRead, lpOverlapped))
{
LONG lLastError = ::GetLastError();
if (ERROR_IO_PENDING != lLastError)
{
m_lLastError = lLastError;
_RPTF0(_CRT_WARN, "CSerial::Read():\tUnable to read from device\n");
return m_lLastError;
}
if (&ovl == lpOverlapped)
{
switch(::WaitForSingleObject(lpOverlapped->hEvent, dwTimeout))
{
case WAIT_OBJECT_0:
if (FALSE == ::GetOverlappedResult(m_hPort, lpOverlapped, pdwRead, FALSE))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::Read():\tOverlapped Operation completed with no results\n");
return m_lLastError;
}
break;
case WAIT_TIMEOUT:
::CancelIo(m_hPort);
m_lLastError = ERROR_TIMEOUT;
return m_lLastError;
default:
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::Read():\tUnable to wait for data to be read\n");
return m_lLastError;
}
}
}
else
{
::SetEvent(lpOverlapped->hEvent);
}
return m_lLastError;
}
HANDLE CSerial::GetHandle()
{
return m_hPort;
}
BOOL CSerial::IsOpen()
{
return (NULL != m_hPort);
}
LONG CSerial::GetLastError() const
{
return m_lLastError;
}
BOOL CSerial::GetCTS()
{
m_lLastError = ERROR_SUCCESS;
DWORD dwModemStatus = 0L;
if (FALSE == ::GetCommModemStatus(m_hPort, &dwModemStatus))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::GetCTS():\tUnable to read modem status\n");
return FALSE;
}
return 0 != (dwModemStatus & MS_CTS_ON);
}
BOOL CSerial::GetDSR()
{
m_lLastError = ERROR_SUCCESS;
DWORD dwModemStatus = 0L;
if (FALSE == ::GetCommModemStatus(m_hPort, &dwModemStatus))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::GetDSR():\tUnable to read modem status\n");
return FALSE;
}
return 0 != (dwModemStatus & MS_DSR_ON);
}
BOOL CSerial::GetRing()
{
m_lLastError = ERROR_SUCCESS;
DWORD dwModemStatus = 0L;
if (FALSE == ::GetCommModemStatus(m_hPort, &dwModemStatus))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::GetRing():\tUnable to read modem status\n");
return FALSE;
}
return 0 != (dwModemStatus & MS_RING_ON);
}
BOOL CSerial::GetRLSD()
{
m_lLastError = ERROR_SUCCESS;
DWORD dwModemStatus = 0L;
if (FALSE == ::GetCommModemStatus(m_hPort, &dwModemStatus))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::GetRLSD():\tUnable to read modem status\n");
return FALSE;
}
return 0 != (dwModemStatus & MS_RLSD_ON);
}
LONG CSerial::Purge()
{
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::Purge():\tDevice is not open\n");
return m_lLastError;
}
if (FALSE == ::PurgeComm(m_hPort, PURGE_RXCLEAR | PURGE_TXCLEAR))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::Purge():\tPurge failed\n");
}
return m_lLastError;
}
LONG CSerial::WaitEvent(LPOVERLAPPED lpOverlapped, DWORD dwTimeout)
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::WaitEvent():\tDevice is not open\n");
return m_lLastError;
}
OVERLAPPED ovl;
if (NULL == lpOverlapped)
{
memset(&ovl, 0, sizeof(OVERLAPPED));
ovl.hEvent = m_heOverlapped;
lpOverlapped = &ovl;
}
ASSERT(HasOverlappedIoCompleted(lpOverlapped) || !"Overlapped Operation in progress");
if (FALSE == ::WaitCommEvent(m_hPort, (LPDWORD)&m_eEvent, lpOverlapped))
{
LONG lLastError = ::GetLastError();
if (ERROR_IO_PENDING != lLastError)
{
m_lLastError = lLastError;
_RPTF0(_CRT_WARN, "CSerial::WaitEvent():\tUnable to wait for Serial event\n");
return m_lLastError;
}
if (&ovl == lpOverlapped)
{
switch(::WaitForSingleObject(lpOverlapped->hEvent, dwTimeout))
{
case WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
::CancelIo(m_hPort);
m_lLastError = ERROR_TIMEOUT;
return m_lLastError;
default:
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::WaitEvent():\tUnable to wait for Serial event to arrive\n");
return m_lLastError;
}
}
}
else
{
::SetEvent(lpOverlapped->hEvent);
}
return m_lLastError;
}
CSerial::Event CSerial::GetLastEvent()
{
Event event = m_eEvent;
m_eEvent = EEVENT_NONE;
return event;
}
CSerial::Error CSerial::GetLastSerialError()
{
m_lLastError = ERROR_SUCCESS;
if (NULL == m_hPort)
{
m_lLastError = ERROR_INVALID_HANDLE;
_RPTF0(_CRT_WARN, "CSerial::GetLastSerialError():\tDevice is not open\n");
return EERROR_UNKNOWN;
}
DWORD dwErrors = 0;
if (FALSE == ::ClearCommError(m_hPort, &dwErrors, 0))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::GetLastSerialError():\tUnable to read Serial status\n");
return EERROR_UNKNOWN;
}
return (Error)dwErrors;
}
DWORD WINAPI CSerial::ThreadProc(LPVOID lpParam)
{
CSerial* pThis = reinterpret_cast<CSerial*>(lpParam);
return pThis->ThreadProc();
}
DWORD CSerial::ThreadProc()
{
OVERLAPPED ovl = { 0 };
ovl.hEvent = m_heSerialEvent;
HANDLE handles[2];
handles[0] = m_heStop;
handles[1] = ovl.hEvent;
if (ERROR_SUCCESS != WaitEvent(&ovl))
return m_lLastError;
BOOL bStop = FALSE;
while (FALSE == bStop)
{
switch(::WaitForMultipleObjects(sizeof(handles)/sizeof(HANDLE), handles, FALSE, INFINITE))
{
case WAIT_OBJECT_0:
{
bStop = TRUE;
break;
}
case WAIT_OBJECT_0 + 1:
{
Event event = GetLastEvent();
DWORD dwErrors = 0;
if (FALSE == ::ClearCommError(m_hPort, &dwErrors, 0))
{
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN, "CSerial::ThreadProc():\tUnable to clear errors\n");
}
Error err = (Error)dwErrors;
if (0 != event)
{
// ::PostMessage(m_hParent, g_iSerialMessage, (WPARAM)event, (LPARAM)err);
m_lpfunc((WPARAM)m_pParent, (WPARAM)event, (LPARAM)err);
}
if (ERROR_SUCCESS != WaitEvent(&ovl))
{
return m_lLastError;
}
break;
}
default: // something went wrong!
{
bStop = TRUE;
break;
}
}
}
return 0;
}