Rev 270 | Blame | Compare with Previous | Last modification | View Log | SVN | RSS feed
// Serial.cpp - Implementation of the CSerial class//// Copyright (C) 1999-2003 Ramon de Klein (Ramon.de.Klein@ict.nl)//// This library is free software; you can redistribute it and/or// modify it under the terms of the GNU Lesser General Public// License as published by the Free Software Foundation; either// version 2.1 of the License, or (at your option) any later version.//// This library is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU// Lesser General Public License for more details.//// You should have received a copy of the GNU Lesser General Public// License along with this library; if not, write to the Free Software// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA//////////////////////////////////////////////////////////////////////// Include the standard header files#define STRICT#include <crtdbg.h>#include <tchar.h>#include <windows.h>#include "stdafx.h"//////////////////////////////////////////////////////////////////////// Include module headerfile#include "Serial.h"//////////////////////////////////////////////////////////////////////// Disable warning C4127: conditional expression is constant, which// is generated when using the _RPTF and _ASSERTE macros.#pragma warning(disable: 4127)//////////////////////////////////////////////////////////////////////// Enable debug memory manager#ifdef _DEBUG#ifdef THIS_FILE#undef THIS_FILE#endifstatic const char THIS_FILE[] = __FILE__;#define new DEBUG_NEW#endif//////////////////////////////////////////////////////////////////////// Helper methodsinline void CSerial::CheckRequirements (LPOVERLAPPED lpOverlapped, DWORD dwTimeout) const{#ifdef SERIAL_NO_OVERLAPPED// Check if an overlapped structure has been specifiedif (lpOverlapped || (dwTimeout != INFINITE)){// Quit application::MessageBox(0,_T("Overlapped I/O and time-outs are not supported, when overlapped I/O is disabled."),_T("Serial library"), MB_ICONERROR | MB_TASKMODAL);::DebugBreak();::ExitProcess(0xFFFFFFF);}#endif#ifdef SERIAL_NO_CANCELIO// Check if 0 or INFINITE time-out has been specified, because// the communication I/O cannot be cancelled.if ((dwTimeout != 0) && (dwTimeout != INFINITE)){// Quit application::MessageBox(0,_T("Timeouts are not supported, when SERIAL_NO_CANCELIO is defined"),_T("Serial library"), MB_ICONERROR | MB_TASKMODAL);::DebugBreak();::ExitProcess(0xFFFFFFF);}#endif // SERIAL_NO_CANCELIO// Avoid warnings(void) dwTimeout;(void) lpOverlapped;}inline BOOL CSerial::CancelCommIo (void){#ifdef SERIAL_NO_CANCELIO// CancelIo shouldn't have been called at this point::DebugBreak();return FALSE;#else// Cancel the I/O requestreturn ::CancelIo(m_hFile);#endif // SERIAL_NO_CANCELIO}//////////////////////////////////////////////////////////////////////// CodeCSerial::CSerial (): m_lLastError(ERROR_SUCCESS), m_hFile(0), m_eEvent(EEventNone), m_dwEventMask(0)#ifndef SERIAL_NO_OVERLAPPED, m_hevtOverlapped(0)#endif{}CSerial::~CSerial (){// If the device is already closed,// then we don't need to do anything.if (m_hFile){// Display a warning_RPTF0(_CRT_WARN,"CSerial::~CSerial - Serial port not closed\n");// Close implicitlyClose();}}CSerial::EPort CSerial::CheckPort (LPCTSTR lpszDevice){// Try to open the deviceHANDLE hFile = ::CreateFile(lpszDevice,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);// Check if we could open the deviceif (hFile == INVALID_HANDLE_VALUE){// Display errorswitch (::GetLastError()){case ERROR_FILE_NOT_FOUND:// The specified COM-port does not existreturn EPortNotAvailable;case ERROR_ACCESS_DENIED:// The specified COM-port is in usereturn EPortInUse;default:// Something else is wrongreturn EPortUnknownError;}}// Close handle::CloseHandle(hFile);// Port is availablereturn EPortAvailable;}LONG CSerial::Open (LPCTSTR lpszDevice, DWORD dwInQueue, DWORD dwOutQueue, bool fOverlapped){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the port isn't already openedif (m_hFile){m_lLastError = ERROR_ALREADY_INITIALIZED;_RPTF0(_CRT_WARN,"CSerial::Open - Port already opened\n");return m_lLastError;}// Open the devicem_hFile = ::CreateFile(lpszDevice,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,fOverlapped?FILE_FLAG_OVERLAPPED:0,0);if (m_hFile == INVALID_HANDLE_VALUE){// Reset file handlem_hFile = 0;// Display errorm_lLastError = ::GetLastError();_RPTF0(_CRT_WARN, "CSerial::Open - Unable to open port\n");return m_lLastError;}#ifndef SERIAL_NO_OVERLAPPED// We cannot have an event handle yet_ASSERTE(m_hevtOverlapped == 0);// Create the event handle for internal overlapped operations (manual reset)if (fOverlapped){m_hevtOverlapped = ::CreateEvent(0,true,false,0);if (m_hevtOverlapped == 0){// Obtain the error informationm_lLastError = ::GetLastError();_RPTF0(_CRT_WARN,"CSerial::Open - Unable to create event\n");// Close the port::CloseHandle(m_hFile);m_hFile = 0;// Return the errorreturn m_lLastError;}}#else// Overlapped flag shouldn't be specified_ASSERTE(!fOverlapped);#endif// Setup the COM-portif (dwInQueue || dwOutQueue){// Make sure the queue-sizes are reasonable sized. Win9X systems crash// if the input queue-size is zero. Both queues need to be at least// 16 bytes large._ASSERTE(dwInQueue >= 16);_ASSERTE(dwOutQueue >= 16);if (!::SetupComm(m_hFile,dwInQueue,dwOutQueue)){// Display a warninglong lLastError = ::GetLastError();_RPTF0(_CRT_WARN,"CSerial::Open - Unable to setup the COM-port\n");// Close the portClose();// Save last error from SetupCommm_lLastError = lLastError;return m_lLastError;}}// Setup the default communication maskSetMask();// Non-blocking reads is defaultSetupReadTimeouts(EReadTimeoutNonblocking);// Setup the device for default settingsCOMMCONFIG commConfig = {0};DWORD dwSize = sizeof(commConfig);commConfig.dwSize = dwSize;if (::GetDefaultCommConfig(lpszDevice,&commConfig,&dwSize)){// Set the default communication configurationif (!::SetCommConfig(m_hFile,&commConfig,dwSize)){// Display a warning_RPTF0(_CRT_WARN,"CSerial::Open - Unable to set default communication configuration.\n");}}else{// Display a warning_RPTF0(_CRT_WARN,"CSerial::Open - Unable to obtain default communication configuration.\n");}// Return successfulreturn m_lLastError;}LONG CSerial::Close (void){// Reset error statem_lLastError = ERROR_SUCCESS;// If the device is already closed,// then we don't need to do anything.if (m_hFile == 0){// Display a warning_RPTF0(_CRT_WARN,"CSerial::Close - Method called when device is not open\n");return m_lLastError;}#ifndef SERIAL_NO_OVERLAPPED// Free event handleif (m_hevtOverlapped){::CloseHandle(m_hevtOverlapped);m_hevtOverlapped = 0;}#endif// Close COM port::CloseHandle(m_hFile);m_hFile = 0;// Return successfulreturn m_lLastError;}LONG CSerial::Setup (EBaudrate eBaudrate, EDataBits eDataBits, EParity eParity, EStopBits eStopBits){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Setup - Device is not opened\n");return m_lLastError;}// Obtain the DCB structure for the deviceCDCB dcb;if (!::GetCommState(m_hFile,&dcb)){// Obtain the error codem_lLastError = :: GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::Setup - Unable to obtain DCB information\n");return m_lLastError;}// Set the new datadcb.BaudRate = DWORD(eBaudrate);dcb.ByteSize = BYTE(eDataBits);dcb.Parity = BYTE(eParity);dcb.StopBits = BYTE(eStopBits);// Determine if parity is useddcb.fParity = (eParity != EParNone);// Set the new DCB structureif (!::SetCommState(m_hFile,&dcb)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::Setup - Unable to set DCB information\n");return m_lLastError;}// Return successfulreturn m_lLastError;}LONG CSerial::SetEventChar (BYTE bEventChar, bool fAdjustMask){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::SetEventChar - Device is not opened\n");return m_lLastError;}// Obtain the DCB structure for the deviceCDCB dcb;if (!::GetCommState(m_hFile,&dcb)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::SetEventChar - Unable to obtain DCB information\n");return m_lLastError;}// Set the new event characterdcb.EvtChar = char(bEventChar);// Adjust the event mask, to make sure the event will be receivedif (fAdjustMask){// Enable 'receive event character' event. Note that this// will generate an EEventNone if there is an asynchronous// WaitCommEvent pending.SetMask(GetEventMask() | EEventRcvEv);}// Set the new DCB structureif (!::SetCommState(m_hFile,&dcb)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::SetEventChar - Unable to set DCB information\n");return m_lLastError;}// Return successfulreturn m_lLastError;}LONG CSerial::SetMask (DWORD dwEventMask){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::SetMask - Device is not opened\n");return m_lLastError;}// Set the new mask. Note that this will generate an EEventNone// if there is an asynchronous WaitCommEvent pending.if (!::SetCommMask(m_hFile,dwEventMask)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::SetMask - Unable to set event mask\n");return m_lLastError;}// Save event mask and return successfulm_dwEventMask = dwEventMask;return m_lLastError;}LONG CSerial::WaitEvent (LPOVERLAPPED lpOverlapped, DWORD dwTimeout){// Check if time-outs are supportedCheckRequirements(lpOverlapped,dwTimeout);// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::WaitEvent - Device is not opened\n");return m_lLastError;}#ifndef SERIAL_NO_OVERLAPPED// Check if an overlapped structure has been specifiedif (!m_hevtOverlapped && (lpOverlapped || (dwTimeout != INFINITE))){// Set the internal error codem_lLastError = ERROR_INVALID_FUNCTION;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::WaitEvent - Overlapped I/O is disabled, specified parameters are illegal.\n");return m_lLastError;}// Wait for the event to happenOVERLAPPED ovInternal;if (!lpOverlapped && m_hevtOverlapped){// Setup our own overlapped structurememset(&ovInternal,0,sizeof(ovInternal));ovInternal.hEvent = m_hevtOverlapped;// Use our internal overlapped structurelpOverlapped = &ovInternal;}// Make sure the overlapped structure isn't busy_ASSERTE(!m_hevtOverlapped || HasOverlappedIoCompleted(lpOverlapped));// Wait for the COM eventif (!::WaitCommEvent(m_hFile,LPDWORD(&m_eEvent),lpOverlapped)){// Set the internal error codelong lLastError = ::GetLastError();// Overlapped operation in progress is not an actual errorif (lLastError != ERROR_IO_PENDING){// Save the errorm_lLastError = lLastError;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::WaitEvent - Unable to wait for COM event\n");return m_lLastError;}// We need to block if the client didn't specify an overlapped structureif (lpOverlapped == &ovInternal){// Wait for the overlapped operation to completeswitch (::WaitForSingleObject(lpOverlapped->hEvent,dwTimeout)){case WAIT_OBJECT_0:// The overlapped operation has completedbreak;case WAIT_TIMEOUT:// Cancel the I/O operationCancelCommIo();// The operation timed out. Set the internal error code and quitm_lLastError = ERROR_TIMEOUT;return m_lLastError;default:// Set the internal error codem_lLastError = ::GetLastError();// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::WaitEvent - Unable to wait until COM event has arrived\n");return m_lLastError;}}}else{// The operation completed immediatly. Just to be sure// we'll set the overlapped structure's event handle.if (lpOverlapped)::SetEvent(lpOverlapped->hEvent);}#else// Wait for the COM eventif (!::WaitCommEvent(m_hFile,LPDWORD(&m_eEvent),0)){// Set the internal error codem_lLastError = ::GetLastError();// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::WaitEvent - Unable to wait for COM event\n");return m_lLastError;}#endif// Return successfullyreturn m_lLastError;}LONG CSerial::SetupHandshaking (EHandshake eHandshake){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::SetupHandshaking - Device is not opened\n");return m_lLastError;}// Obtain the DCB structure for the deviceCDCB dcb;if (!::GetCommState(m_hFile,&dcb)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::SetupHandshaking - Unable to obtain DCB information\n");return m_lLastError;}// Set the handshaking flagsswitch (eHandshake){case EHandshakeOff:dcb.fOutxCtsFlow = false; // Disable CTS monitoringdcb.fOutxDsrFlow = false; // Disable DSR monitoringdcb.fDtrControl = DTR_CONTROL_DISABLE; // Disable DTR monitoringdcb.fOutX = false; // Disable XON/XOFF for transmissiondcb.fInX = false; // Disable XON/XOFF for receivingdcb.fRtsControl = RTS_CONTROL_DISABLE; // Disable RTS (Ready To Send)break;case EHandshakeHardware:dcb.fOutxCtsFlow = true; // Enable CTS monitoringdcb.fOutxDsrFlow = true; // Enable DSR monitoringdcb.fDtrControl = DTR_CONTROL_HANDSHAKE; // Enable DTR handshakingdcb.fOutX = false; // Disable XON/XOFF for transmissiondcb.fInX = false; // Disable XON/XOFF for receivingdcb.fRtsControl = RTS_CONTROL_HANDSHAKE; // Enable RTS handshakingbreak;case EHandshakeSoftware:dcb.fOutxCtsFlow = false; // Disable CTS (Clear To Send)dcb.fOutxDsrFlow = false; // Disable DSR (Data Set Ready)dcb.fDtrControl = DTR_CONTROL_DISABLE; // Disable DTR (Data Terminal Ready)dcb.fOutX = true; // Enable XON/XOFF for transmissiondcb.fInX = true; // Enable XON/XOFF for receivingdcb.fRtsControl = RTS_CONTROL_DISABLE; // Disable RTS (Ready To Send)break;default:// This shouldn't be possible_ASSERTE(false);m_lLastError = E_INVALIDARG;return m_lLastError;}// Set the new DCB structureif (!::SetCommState(m_hFile,&dcb)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::SetupHandshaking - Unable to set DCB information\n");return m_lLastError;}// Return successfulreturn m_lLastError;}LONG CSerial::SetupReadTimeouts (EReadTimeout eReadTimeout){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::SetupReadTimeouts - Device is not opened\n");return m_lLastError;}// Determine the time-outsCOMMTIMEOUTS cto;if (!::GetCommTimeouts(m_hFile,&cto)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::SetupReadTimeouts - Unable to obtain timeout information\n");return m_lLastError;}// Set the new timeoutsswitch (eReadTimeout){case EReadTimeoutBlocking:cto.ReadIntervalTimeout = 0;cto.ReadTotalTimeoutConstant = 0;cto.ReadTotalTimeoutMultiplier = 0;break;case EReadTimeoutNonblocking:cto.ReadIntervalTimeout = MAXDWORD;cto.ReadTotalTimeoutConstant = 0;cto.ReadTotalTimeoutMultiplier = 0;break;default:// This shouldn't be possible_ASSERTE(false);m_lLastError = E_INVALIDARG;return m_lLastError;}// Set the new DCB structureif (!::SetCommTimeouts(m_hFile,&cto)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::SetupReadTimeouts - Unable to set timeout information\n");return m_lLastError;}// Return successfulreturn m_lLastError;}CSerial::EBaudrate CSerial::GetBaudrate (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::GetBaudrate - Device is not opened\n");return EBaudUnknown;}// Obtain the DCB structure for the deviceCDCB dcb;if (!::GetCommState(m_hFile,&dcb)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::GetBaudrate - Unable to obtain DCB information\n");return EBaudUnknown;}// Return the appropriate baudratereturn EBaudrate(dcb.BaudRate);}CSerial::EDataBits CSerial::GetDataBits (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::GetDataBits - Device is not opened\n");return EDataUnknown;}// Obtain the DCB structure for the deviceCDCB dcb;if (!::GetCommState(m_hFile,&dcb)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::GetDataBits - Unable to obtain DCB information\n");return EDataUnknown;}// Return the appropriate bytesizereturn EDataBits(dcb.ByteSize);}CSerial::EParity CSerial::GetParity (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::GetParity - Device is not opened\n");return EParUnknown;}// Obtain the DCB structure for the deviceCDCB dcb;if (!::GetCommState(m_hFile,&dcb)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::GetParity - Unable to obtain DCB information\n");return EParUnknown;}// Check if parity is usedif (!dcb.fParity){// No parityreturn EParNone;}// Return the appropriate parity settingreturn EParity(dcb.Parity);}CSerial::EStopBits CSerial::GetStopBits (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::GetStopBits - Device is not opened\n");return EStopUnknown;}// Obtain the DCB structure for the deviceCDCB dcb;if (!::GetCommState(m_hFile,&dcb)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::GetStopBits - Unable to obtain DCB information\n");return EStopUnknown;}// Return the appropriate stopbitsreturn EStopBits(dcb.StopBits);}DWORD CSerial::GetEventMask (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::GetEventMask - Device is not opened\n");return 0;}// Return the event maskreturn m_dwEventMask;}BYTE CSerial::GetEventChar (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::GetEventChar - Device is not opened\n");return 0;}// Obtain the DCB structure for the deviceCDCB dcb;if (!::GetCommState(m_hFile,&dcb)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::GetEventChar - Unable to obtain DCB information\n");return 0;}// Set the new event characterreturn BYTE(dcb.EvtChar);}CSerial::EHandshake CSerial::GetHandshaking (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::GetHandshaking - Device is not opened\n");return EHandshakeUnknown;}// Obtain the DCB structure for the deviceCDCB dcb;if (!::GetCommState(m_hFile,&dcb)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::GetHandshaking - Unable to obtain DCB information\n");return EHandshakeUnknown;}// Check if hardware handshaking is being usedif ((dcb.fDtrControl == DTR_CONTROL_HANDSHAKE) && (dcb.fRtsControl == RTS_CONTROL_HANDSHAKE))return EHandshakeHardware;// Check if software handshaking is being usedif (dcb.fOutX && dcb.fInX)return EHandshakeSoftware;// No handshaking is being usedreturn EHandshakeOff;}LONG CSerial::Write (const void* pData, size_t iLen, DWORD* pdwWritten, LPOVERLAPPED lpOverlapped, DWORD dwTimeout){// Check if time-outs are supportedCheckRequirements(lpOverlapped,dwTimeout);// Overlapped operation should specify the pdwWritten variable_ASSERTE(!lpOverlapped || pdwWritten);// Reset error statem_lLastError = ERROR_SUCCESS;// Use our own variable for read countDWORD dwWritten;if (pdwWritten == 0){pdwWritten = &dwWritten;}// Reset the number of bytes written*pdwWritten = 0;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Write - Device is not opened\n");return m_lLastError;}#ifndef SERIAL_NO_OVERLAPPED// Check if an overlapped structure has been specifiedif (!m_hevtOverlapped && (lpOverlapped || (dwTimeout != INFINITE))){// Set the internal error codem_lLastError = ERROR_INVALID_FUNCTION;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Write - Overlapped I/O is disabled, specified parameters are illegal.\n");return m_lLastError;}// Wait for the event to happenOVERLAPPED ovInternal;if (!lpOverlapped && m_hevtOverlapped){// Setup our own overlapped structurememset(&ovInternal,0,sizeof(ovInternal));ovInternal.hEvent = m_hevtOverlapped;// Use our internal overlapped structurelpOverlapped = &ovInternal;}// Make sure the overlapped structure isn't busy_ASSERTE(!m_hevtOverlapped || HasOverlappedIoCompleted(lpOverlapped));// Write the dataif (!::WriteFile(m_hFile,pData,iLen,pdwWritten,lpOverlapped)){// Set the internal error codelong lLastError = ::GetLastError();// Overlapped operation in progress is not an actual errorif (lLastError != ERROR_IO_PENDING){// Save the errorm_lLastError = lLastError;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Write - Unable to write the data\n");return m_lLastError;}// We need to block if the client didn't specify an overlapped structureif (lpOverlapped == &ovInternal){// Wait for the overlapped operation to completeswitch (::WaitForSingleObject(lpOverlapped->hEvent,dwTimeout)){case WAIT_OBJECT_0:// The overlapped operation has completedif (!::GetOverlappedResult(m_hFile,lpOverlapped,pdwWritten,FALSE)){// Set the internal error codem_lLastError = ::GetLastError();_RPTF0(_CRT_WARN,"CSerial::Write - Overlapped completed without result\n");return m_lLastError;}break;case WAIT_TIMEOUT:// Cancel the I/O operationCancelCommIo();// The operation timed out. Set the internal error code and quitm_lLastError = ERROR_TIMEOUT;return m_lLastError;default:// Set the internal error codem_lLastError = ::GetLastError();// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Write - Unable to wait until data has been sent\n");return m_lLastError;}}}else{// The operation completed immediatly. Just to be sure// we'll set the overlapped structure's event handle.if (lpOverlapped)::SetEvent(lpOverlapped->hEvent);}#else// Write the dataif (!::WriteFile(m_hFile,pData,iLen,pdwWritten,0)){// Set the internal error codem_lLastError = ::GetLastError();// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Write - Unable to write the data\n");return m_lLastError;}#endif// Return successfullyreturn m_lLastError;}LONG CSerial::Write (LPCSTR pString, DWORD* pdwWritten, LPOVERLAPPED lpOverlapped, DWORD dwTimeout){// Check if time-outs are supportedCheckRequirements(lpOverlapped,dwTimeout);// Determine the length of the stringreturn Write(pString,strlen(pString),pdwWritten,lpOverlapped,dwTimeout);}LONG CSerial::Read (void* pData, size_t iLen, DWORD* pdwRead, LPOVERLAPPED lpOverlapped, DWORD dwTimeout){// Check if time-outs are supportedCheckRequirements(lpOverlapped,dwTimeout);// Overlapped operation should specify the pdwRead variable_ASSERTE(!lpOverlapped || pdwRead);// Reset error statem_lLastError = ERROR_SUCCESS;// Use our own variable for read countDWORD dwRead;if (pdwRead == 0){pdwRead = &dwRead;}// Reset the number of bytes read*pdwRead = 0;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Read - Device is not opened\n");return m_lLastError;}#ifdef _DEBUG// The debug version fills the entire data structure with// 0xDC bytes, to catch buffer errors as soon as possible.memset(pData,0xDC,iLen);#endif#ifndef SERIAL_NO_OVERLAPPED// Check if an overlapped structure has been specifiedif (!m_hevtOverlapped && (lpOverlapped || (dwTimeout != INFINITE))){// Set the internal error codem_lLastError = ERROR_INVALID_FUNCTION;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Read - Overlapped I/O is disabled, specified parameters are illegal.\n");return m_lLastError;}// Wait for the event to happenOVERLAPPED ovInternal;if (lpOverlapped == 0){// Setup our own overlapped structurememset(&ovInternal,0,sizeof(ovInternal));ovInternal.hEvent = m_hevtOverlapped;// Use our internal overlapped structurelpOverlapped = &ovInternal;}// Make sure the overlapped structure isn't busy_ASSERTE(!m_hevtOverlapped || HasOverlappedIoCompleted(lpOverlapped));// Read the dataif (!::ReadFile(m_hFile,pData,iLen,pdwRead,lpOverlapped)){// Set the internal error codelong lLastError = ::GetLastError();// Overlapped operation in progress is not an actual errorif (lLastError != ERROR_IO_PENDING){// Save the errorm_lLastError = lLastError;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Read - Unable to read the data\n");return m_lLastError;}// We need to block if the client didn't specify an overlapped structureif (lpOverlapped == &ovInternal){// Wait for the overlapped operation to completeswitch (::WaitForSingleObject(lpOverlapped->hEvent,dwTimeout)){case WAIT_OBJECT_0:// The overlapped operation has completedif (!::GetOverlappedResult(m_hFile,lpOverlapped,pdwRead,FALSE)){// Set the internal error codem_lLastError = ::GetLastError();_RPTF0(_CRT_WARN,"CSerial::Read - Overlapped completed without result\n");return m_lLastError;}break;case WAIT_TIMEOUT:// Cancel the I/O operationCancelCommIo();// The operation timed out. Set the internal error code and quitm_lLastError = ERROR_TIMEOUT;return m_lLastError;default:// Set the internal error codem_lLastError = ::GetLastError();// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Read - Unable to wait until data has been read\n");return m_lLastError;}}}else{// The operation completed immediatly. Just to be sure// we'll set the overlapped structure's event handle.if (lpOverlapped)::SetEvent(lpOverlapped->hEvent);}#else// Read the dataif (!::ReadFile(m_hFile,pData,iLen,pdwRead,0)){// Set the internal error codem_lLastError = ::GetLastError();// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Read - Unable to read the data\n");return m_lLastError;}#endif// Return successfullyreturn m_lLastError;}LONG CSerial::Purge(){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Purge - Device is not opened\n");return m_lLastError;}if (!::PurgeComm(m_hFile, PURGE_TXCLEAR | PURGE_RXCLEAR)){// Set the internal error codem_lLastError = ::GetLastError();_RPTF0(_CRT_WARN,"CSerial::Purge - Overlapped completed without result\n");}// Return successfullyreturn m_lLastError;}LONG CSerial::Break (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::Break - Device is not opened\n");return m_lLastError;}// Set the RS-232 port in break mode for a little while::SetCommBreak(m_hFile);::Sleep(100);::ClearCommBreak(m_hFile);// Return successfullyreturn m_lLastError;}CSerial::EEvent CSerial::GetEventType (void){#ifdef _DEBUG// Check if the event is within the maskif ((m_eEvent & m_dwEventMask) == 0)_RPTF2(_CRT_WARN,"CSerial::GetEventType - Event %08Xh not within mask %08Xh.\n", m_eEvent, m_dwEventMask);#endif// Obtain the event (mask unwanted events out)EEvent eEvent = EEvent(m_eEvent & m_dwEventMask);// Reset internal event typem_eEvent = EEventNone;// Return the current causereturn eEvent;}CSerial::EError CSerial::GetError (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Check if the device is openif (m_hFile == 0){// Set the internal error codem_lLastError = ERROR_INVALID_HANDLE;// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::GetError - Device is not opened\n");return EErrorUnknown;}// Obtain COM statusDWORD dwErrors = 0;if (!::ClearCommError(m_hFile,&dwErrors,0)){// Set the internal error codem_lLastError = ::GetLastError();// Issue an error and quit_RPTF0(_CRT_WARN,"CSerial::GetError - Unable to obtain COM status\n");return EErrorUnknown;}// Return the errorreturn EError(dwErrors);}bool CSerial::GetCTS (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Obtain the modem statusDWORD dwModemStat = 0;if (!::GetCommModemStatus(m_hFile,&dwModemStat)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::GetCTS - Unable to obtain the modem status\n");return false;}// Determine if CTS is onreturn (dwModemStat & MS_CTS_ON) != 0;}bool CSerial::GetDSR (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Obtain the modem statusDWORD dwModemStat = 0;if (!::GetCommModemStatus(m_hFile,&dwModemStat)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::GetDSR - Unable to obtain the modem status\n");return false;}// Determine if DSR is onreturn (dwModemStat & MS_DSR_ON) != 0;}bool CSerial::GetRing (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Obtain the modem statusDWORD dwModemStat = 0;if (!::GetCommModemStatus(m_hFile,&dwModemStat)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::GetRing - Unable to obtain the modem status");return false;}// Determine if Ring is onreturn (dwModemStat & MS_RING_ON) != 0;}bool CSerial::GetRLSD (void){// Reset error statem_lLastError = ERROR_SUCCESS;// Obtain the modem statusDWORD dwModemStat = 0;if (!::GetCommModemStatus(m_hFile,&dwModemStat)){// Obtain the error codem_lLastError = ::GetLastError();// Display a warning_RPTF0(_CRT_WARN,"CSerial::GetRLSD - Unable to obtain the modem status");return false;}// Determine if RLSD is onreturn (dwModemStat & MS_RLSD_ON) != 0;}