class _SERIAL_PORT_DATA
{
public:
//Constructors /Destructors
_SERIAL_PORT_DATA();
~_SERIAL_PORT_DATA();
HINSTANCE m_hKernel32;
typedef BOOL (WINAPI CANCELIO)(HANDLE);
typedef CANCELIO* LPCANCELIO;
LPCANCELIO m_lpfnCancelIo;
};
_SERIAL_PORT_DATA::_SERIAL_PORT_DATA()
{
m_hKernel32 = LoadLibrary(L"KERNEL32.DLL");
//VERIFY(m_hKernel32 != NULL);
m_lpfnCancelIo = (LPCANCELIO) GetProcAddress(m_hKernel32, "CancelIo");
}
_SERIAL_PORT_DATA::~_SERIAL_PORT_DATA()
{
FreeLibrary(m_hKernel32);
m_hKernel32 = NULL;
}
/The local variable which handle the function pointers
_SERIAL_PORT_DATA _SerialPortData;
////////// Exception handling code
void AfxThrowSerialException(DWORD dwError /* = 0 */)
{
if (dwError == 0)
dwError = ::GetLastError();
CSerialException* pException = new CSerialException(dwError);
TRACE(_T("Warning: throwing CSerialException for error %d\n"), dwError);
THROW(pException);
//throw(Exception(get_last_error_string()));
}
BOOL CSerialException::GetErrorMessage(LPTSTR pstrError, UINT nMaxError, PUINT pnHelpContext)
{
ASSERT(pstrError != NULL && AfxIsValidString(pstrError, nMaxError));
if (pnHelpContext != NULL)
*pnHelpContext = 0;
LPTSTR lpBuffer;
BOOL bRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, m_dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
(LPTSTR) &lpBuffer, 0, NULL);
if (bRet == FALSE)
*pstrError = '\0';
else
{
lstrcpyn(pstrError, lpBuffer, nMaxError);
bRet = TRUE;
LocalFree(lpBuffer);
}
return bRet;
}
CString CSerialException::GetErrorMessage()
{
CString rVal;
LPTSTR pstrError = rVal.GetBuffer(4096);
GetErrorMessage(pstrError, 4096, NULL);
rVal.ReleaseBuffer();
return rVal;
}
CSerialException::CSerialException(DWORD dwError)
{
m_dwError = dwError;
}
CSerialException::~CSerialException()
{
}
IMPLEMENT_DYNAMIC(CSerialException, CException)
#ifdef _DEBUG
void CSerialException::Dump(CDumpContext& dc) const
{
CObject::Dump(dc);
dc << "m_dwError = " << m_dwError;
}
#endif
////////// The actual serial port code
CSerialPort::CSerialPort()
{
m_hComm = INVALID_HANDLE_VALUE;
m_bOverlapped = FALSE;
m_hEvent = NULL;
}
CSerialPort::~CSerialPort()
{
Close();
}
//IMPLEMENT_DYNAMIC(CSerialPort, CObject)
void CSerialPort::Open(int nPort, DWORD dwBaud, Parity parity, BYTE DataBits, StopBits stopbits, FlowControl fc, BOOL bOverlapped)
{
//Validate our parameters
ASSERT(nPort>0 && nPort<=255);
Close(); //In case we are already open
//Call CreateFile to open up the comms port
char sPort[20];
sprintf(sPort, "\\\\.\\COM%d", nPort);
DWORD dwCreateProperty;
if(bOverlapped)
{
dwCreateProperty=FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED;
}
else
{
dwCreateProperty=FILE_ATTRIBUTE_NORMAL;
}
// bOverlapped ? FILE_FLAG_OVERLAPPED : 0
m_hComm = CreateFileA(sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,dwCreateProperty, NULL);
if (m_hComm == INVALID_HANDLE_VALUE)
{
//TRACE(_T("Failed to open up the comms port\n"));
AfxThrowSerialException();
}
this->m_CurPortNum = nPort;
//Create the event we need for later synchronisation use
/*
if(bOverlapped)
{
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hEvent == NULL)
{
Close();
TRACE(_T("Failed in call to CreateEvent in Open\n"));
AfxThrowSerialException();
}
}
*/
m_bOverlapped = bOverlapped;
//Get the current state prior to changing it
DCB dcb;
dcb.DCBlength = sizeof(DCB);
GetState(dcb);
//Setup the baud rate
dcb.BaudRate = dwBaud;
//Setup the Parity
switch (parity)
{
case EvenParity: dcb.Parity = EVENPARITY; break;
case MarkParity: dcb.Parity = MARKPARITY; break;
case NoParity: dcb.Parity = NOPARITY; break;
case OddParity: dcb.Parity = ODDPARITY; break;
case SpaceParity: dcb.Parity = SPACEPARITY; break;
default: ASSERT(FALSE); break;
}
//Setup the data bits
dcb.ByteSize = DataBits;
//Setup the stop bits
switch (stopbits)
{
case OneStopBit: dcb.StopBits = ONESTOPBIT; break;
case OnePointFiveStopBits: dcb.StopBits = ONE5STOPBITS; break;
case TwoStopBits: dcb.StopBits = TWOSTOPBITS; break;
default: ASSERT(FALSE); break;
}
//Setup the flow control
dcb.fDsrSensitivity = FALSE;
switch (fc)
{
case NoFlowControl:
{
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
break;
}
case CtsRtsFlowControl:
{
dcb.fOutxCtsFlow = TRUE;
dcb.fOutxDsrFlow = FALSE;
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
break;
}
case CtsDtrFlowControl:
{
dcb.fOutxCtsFlow = TRUE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
break;
}
case DsrRtsFlowControl:
{
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = TRUE;
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
break;
}
case DsrDtrFlowControl:
{
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = TRUE;
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
break;
}
case XonXoffFlowControl:
{
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fOutX = TRUE;
dcb.fInX = TRUE;
dcb.XonChar = 0x11;
dcb.XoffChar = 0x13;
dcb.XoffLim = 100;
dcb.XonLim = 100;
break;
}
default:
{
ASSERT(FALSE);
break;
}
}
//Now that we have all the settings in place, make the changes
SetState(dcb);
}
void CSerialPort::Close()
{
if (IsOpen())
{
//Close down the comms port
BOOL bSuccess = CloseHandle(m_hComm);
m_hComm = INVALID_HANDLE_VALUE;
if (!bSuccess)
{
//TRACE(_T("Failed to close up the comms port, GetLastError:%d\n"), GetLastError());
AfxThrowSerialException();
}
m_bOverlapped = FALSE;
//Free up the event object we are using
if(m_hEvent)
{
CloseHandle(m_hEvent);
m_hEvent = NULL;
}
}
}
void CSerialPort::Attach(HANDLE hComm, BOOL bOverlapped)
{
Close();
m_hComm = hComm;
m_bOverlapped = bOverlapped;
//Create the event we need for later synchronisation use
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hEvent == NULL)
{
Close();
//TRACE(_T("Failed in call to CreateEvent in Attach\n"));
AfxThrowSerialException();
}
}
HANDLE CSerialPort::Detach()
{
HANDLE hrVal = m_hComm;
m_hComm = INVALID_HANDLE_VALUE;
CloseHandle(m_hEvent);
m_hEvent = NULL;
return hrVal;
}
DWORD CSerialPort::Read(void* lpBuf, DWORD dwCount)
{
ASSERT(IsOpen());
ASSERT(!m_bOverlapped);
DWORD Errors;
COMSTAT comstat;
BOOL ok;
ok = ::ClearCommError(this->m_hComm,&Errors,&comstat);
if(!ok)
{
return 0;
}
if(comstat.cbInQue==0)
{
return 0;
}
DWORD dwBytesRead = 0;
if (!ReadFile(m_hComm, lpBuf, comstat.cbInQue, &dwBytesRead, NULL))
{
//TRACE(_T("Failed in call to ReadFile\n"));
AfxThrowSerialException();
}
return dwBytesRead;
}
BOOL CSerialPort::Read(void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped, DWORD* pBytesRead)
{
ASSERT(IsOpen());
ASSERT(m_bOverlapped);
DWORD dwBytesRead = 0;
//DWORD Errors;
// COMSTAT comstat;
/*
BOOL ok;
ok = ::ClearCommError(this->m_hComm,&Errors,&comstat);
if(!ok)
{
return false;
}
if(comstat.cbInQue==0)
{
return false;
}
*/
BOOL bSuccess = ReadFile(m_hComm, lpBuf, dwCount, &dwBytesRead, &overlapped);
if (!bSuccess)
{
if (GetLastError() != ERROR_IO_PENDING)
{
//TRACE(_T("Failed in call to ReadFile\n"));
AfxThrowSerialException();
}
}
else
{
if (pBytesRead)
*pBytesRead = dwBytesRead;
}
return bSuccess;
}
DWORD CSerialPort::Write(const void* lpBuf, DWORD dwCount)
{
ASSERT(IsOpen());
ASSERT(!m_bOverlapped);
DWORD dwBytesWritten = 0;
if (!WriteFile(m_hComm, lpBuf, dwCount, &dwBytesWritten, NULL))
{
//TRACE(_T("Failed in call to WriteFile\n"));
AfxThrowSerialException();
}
return dwBytesWritten;
}
BOOL CSerialPort::Write(const void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped, DWORD* pBytesWritten)
{
ASSERT(IsOpen());
ASSERT(m_bOverlapped);
DWORD dwBytesWritten = 0;
BOOL bSuccess = WriteFile(m_hComm, lpBuf, dwCount, &dwBytesWritten, &overlapped);
if (!bSuccess)
{
if (GetLastError() != ERROR_IO_PENDING)
{
//TRACE(_T("Failed in call to WriteFile\n"));
AfxThrowSerialException();
}
}
else
{
if (pBytesWritten)
*pBytesWritten = dwBytesWritten;
}
return bSuccess;
}
bool CSerialPort::GetOverlappedResult(OVERLAPPED& overlapped, DWORD& dwBytesTransferred, BOOL bWait)
{
ASSERT(IsOpen());
ASSERT(m_bOverlapped);
BOOL bSuccess = ::GetOverlappedResult(m_hComm, &overlapped, &dwBytesTransferred, bWait);
if (!bSuccess)
{
if (GetLastError() != ERROR_IO_PENDING)
{
//TRACE(_T("Failed in call to GetOverlappedResult\n"));
AfxThrowSerialException();
}
}
return bSuccess == TRUE;
}
void WINAPI CSerialPort::_OnCompletion(DWORD dwErrorCode, DWORD dwCount, LPOVERLAPPED lpOverlapped)
{
//Validate our parameters
ASSERT(lpOverlapped);
//Convert back to the C++ world
CSerialPort* pSerialPort = (CSerialPort*) lpOverlapped->hEvent;
ASSERT(pSerialPort);
//ASSERT(pSerialPort->IsKindOf(RUNTIME_CLASS(CSerialPort)));
//Call the C++ function
pSerialPort->OnCompletion(dwErrorCode, dwCount, lpOverlapped);
}
void CSerialPort::OnCompletion(DWORD /*dwErrorCode*/, DWORD /*dwCount*/, LPOVERLAPPED lpOverlapped)
{
//Jus
void CSerialPort::CancelIo()
{
ASSERT(IsOpen());
if (_SerialPortData.m_lpfnCancelIo == NULL)
{
//TRACE(_T("CancelIo function is not supported on this OS. You need to be running at least NT 4 or Win 98 to use this function\n"));
AfxThrowSerialException(ERROR_CALL_NOT_IMPLEMENTED);
}
if (!::_SerialPortData.m_lpfnCancelIo(m_hComm))
{
//TRACE(_T("Failed in call to CancelIO\n"));
AfxThrowSerialException();
}
}
DWORD CSerialPort::BytesWaiting()
{
ASSERT(IsOpen());
//Check to see how many characters are unread
COMSTAT stat;
GetStatus(stat);
return stat.cbInQue;
}
BOOL CSerialPort::DataWaiting(DWORD dwTimeout)
{
ASSERT(IsOpen());
ASSERT(m_hEvent);
//Setup to wait for incoming data
DWORD dwOldMask;
GetMask(dwOldMask);
SetMask(EV_RXCHAR);
//Setup the overlapped structure
OVERLAPPED o;
o.hEvent = m_hEvent;
//Assume the worst;
BOOL bSuccess = FALSE;
DWORD dwEvent;
bSuccess = WaitEvent(dwEvent, o);
if (!bSuccess)
{
if (WaitForSingleObject(o.hEvent, dwTimeout) == WAIT_OBJECT_0)
{
DWORD dwBytesTransferred;
GetOverlappedResult(o, dwBytesTransferred, FALSE);
bSuccess = TRUE;
}
}
//Reset the event mask
SetMask(dwOldMask);
return bSuccess;
}
void CSerialPort::WriteEx(const void* lpBuf, DWORD dwCount)
{
ASSERT(IsOpen());
OVERLAPPED* pOverlapped = new OVERLAPPED;
ZeroMemory(pOverlapped, sizeof(OVERLAPPED));
pOverlapped->hEvent = (HANDLE) this;
if (!WriteFileEx(m_hComm, lpBuf, dwCount, pOverlapped, _OnCompletion))
{
delete pOverlapped;
//TRACE(_T("Failed in call to WriteFileEx\n"));
AfxThrowSerialException();
}
}
void CSerialPort::ReadEx(void* lpBuf, DWORD dwCount)
{
ASSERT(IsOpen());
OVERLAPPED* pOverlapped = new OVERLAPPED;
ZeroMemory(pOverlapped, sizeof(OVERLAPPED));
pOverlapped->hEvent = (HANDLE) this;
if (!ReadFileEx(m_hComm, lpBuf, dwCount, pOverlapped, _OnCompletion))
{
delete pOverlapped;
//TRACE(_T("Failed in call to ReadFileEx\n"));
AfxThrowSerialException();
}
}
void CSerialPort::TransmitChar(char cChar)
{
ASSERT(IsOpen());
if (!TransmitCommChar(m_hComm, cChar))
{
//TRACE(_T("Failed in call to TransmitCommChar\n"));
AfxThrowSerialException();
}
}
void CSerialPort::GetConfig(COMMCONFIG& config)
{
ASSERT(IsOpen());
DWORD dwSize = sizeof(COMMCONFIG);
if (!GetCommConfig(m_hComm, &config, &dwSize))
{
//TRACE(_T("Failed in call to GetCommConfig\n"));
AfxThrowSerialException();
}
}
void CSerialPort::SetConfig(COMMCONFIG& config)
{
ASSERT(IsOpen());
DWORD dwSize = sizeof(COMMCONFIG);
if (!SetCommConfig(m_hComm, &config, dwSize))
{
//TRACE(_T("Failed in call to SetCommConfig\n"));
AfxThrowSerialException();
}
}
void CSerialPort::SetBreak()
{
ASSERT(IsOpen());
if (!SetCommBreak(m_hComm))
{
//TRACE(_T("Failed in call to SetCommBreak\n"));
AfxThrowSerialException();
}
}
void CSerialPort::ClearBreak()
{
ASSERT(IsOpen());
if (!ClearCommBreak(m_hComm))
{
//TRACE(_T("Failed in call to SetCommBreak\n"));
AfxThrowSerialException();
}
}
void CSerialPort::ClearError(DWORD& dwErrors)
{
ASSERT(IsOpen());
if (!ClearCommError(m_hComm, &dwErrors, NULL))
{
//TRACE(_T("Failed in call to ClearCommError\n"));
AfxThrowSerialException();
}
}
void CSerialPort::GetDefaultConfig(int nPort, COMMCONFIG& config)
{
//Validate our parameters
ASSERT(nPort>0 && nPort<=255);
//Create the device name as a string
char sPort[20];
sprintf(sPort, "COM%d", nPort);
DWORD dwSize = sizeof(COMMCONFIG);
if (!GetDefaultCommConfigA(sPort, &config, &dwSize))
{
//TRACE(_T("Failed in call to GetDefaultCommConfig\n"));
AfxThrowSerialException();
}
}
void CSerialPort::SetDefaultConfig(int nPort, COMMCONFIG& config)
{
//Validate our parameters
ASSERT(nPort>0 && nPort<=255);
//Create the device name as a string
char sPort[20];
sprintf(sPort, "COM%d", nPort);