•
•
•
•
What is DaniWeb IT Discussion Community?
You're currently browsing the C++ section within the Software Development category of DaniWeb, a massive community of 374,546 software developers, web developers, Internet marketers, and tech gurus who are all enthusiastic about making contacts, networking, and learning from each other. In fact, there are 2,653 IT professionals currently interacting right now! Registration is free, only takes a minute and lets you enjoy all of the interactive features of the site.
Please support our C++ advertiser:
Views: 2511 | Replies: 6 | Solved
![]() |
•
•
Join Date: Feb 2007
Posts: 5
Reputation:
Rep Power: 0
Solved Threads: 0
Hi, I'm very new to C++ and I am getting the following error when I try to compile:
[Linker error] undefined reference to `CSerial::CSerial()'
[Linker error] undefined reference to `CSerial::Open(char const*, unsigned long, unsigned long, bool)'
[Linker error] undefined reference to `CSerial::~CSerial()'
.
.
.
etc.
This is the code that I am trying to compile:
The 'Serial.h' header file can be downloaded (along with the code) here:
http://www.codeproject.com/system/Se...erial_demo.zip
or
http://www.codeproject.com/system/serial.asp
I am using DevC++
Basically I am trying to write a program to control an external power supply using a serial port. I can tell the power supply what to do using hyperterminal, so I know the connection is working. I'm just inexperienced in doing the serial communications port stuff with C++ so I'm trying to learn through this guy's demo project.
Any help with this Linker Error or this application would be amazing!
-Sam Cape
[Linker error] undefined reference to `CSerial::CSerial()'
[Linker error] undefined reference to `CSerial::Open(char const*, unsigned long, unsigned long, bool)'
[Linker error] undefined reference to `CSerial::~CSerial()'
.
.
.
etc.
This is the code that I am trying to compile:
#define STRICT
#include <tchar.h>
#include <windows.h>
#include "Serial.h"
int ShowError (LONG lError, LPCTSTR lptszMessage)
{
// Generate a message text
TCHAR tszMessage[256];
wsprintf(tszMessage,_T("%s\n(error code %d)"), lptszMessage, lError);
// Display message-box and return with an error-code
::MessageBox(0,tszMessage,_T("Hello world"), MB_ICONSTOP|MB_OK);
return 1;
}
int WINAPI _tWinMain (HINSTANCE /*hInst*/, HINSTANCE /*hInstPrev*/, LPTSTR /*lptszCmdLine*/, int /*nCmdShow*/)
{
CSerial serial;
LONG lLastError = ERROR_SUCCESS;
// Attempt to open the serial port (COM1)
lLastError = serial.Open(_T("COM1"),0,0,false);
if (lLastError != ERROR_SUCCESS)
return ::ShowError(serial.GetLastError(), _T("Unable to open COM-port"));
// Setup the serial port (9600,N81) using hardware handshaking
lLastError = serial.Setup(CSerial::EBaud9600,CSerial::EData8,CSerial::EParNone,CSerial::EStop1);
if (lLastError != ERROR_SUCCESS)
return ::ShowError(serial.GetLastError(), _T("Unable to set COM-port setting"));
// Setup handshaking
lLastError = serial.SetupHandshaking(CSerial::EHandshakeHardware);
if (lLastError != ERROR_SUCCESS)
return ::ShowError(serial.GetLastError(), _T("Unable to set COM-port handshaking"));
// The serial port is now ready and we can send/receive data. If
// the following call blocks, then the other side doesn't support
// hardware handshaking.
lLastError = serial.Write("Hello world\n");
if (lLastError != ERROR_SUCCESS)
return ::ShowError(serial.GetLastError(), _T("Unable to send data"));
// Close the port again
serial.Close();
return 0;
}The 'Serial.h' header file can be downloaded (along with the code) here:
http://www.codeproject.com/system/Se...erial_demo.zip
or
http://www.codeproject.com/system/serial.asp
I am using DevC++
Basically I am trying to write a program to control an external power supply using a serial port. I can tell the power supply what to do using hyperterminal, so I know the connection is working. I'm just inexperienced in doing the serial communications port stuff with C++ so I'm trying to learn through this guy's demo project.
Any help with this Linker Error or this application would be amazing!
-Sam Cape
Last edited by Caped Sam : Feb 2nd, 2007 at 11:56 am. Reason: typo
Linker errors are usually generated when the linker can't find the function definations i.e. the body of the mentioned functions.
You need to place the "Serial.h" and "Serial.cpp" file in the same directory as that of the main or driver file and make sure the above mentioned functions are defined in the "Serial.cpp" file.
You need to place the "Serial.h" and "Serial.cpp" file in the same directory as that of the main or driver file and make sure the above mentioned functions are defined in the "Serial.cpp" file.
"I don't accept change. I don't deserve to live."
"Working a real job is a win if you're lazy, greedy, or unmotivated. If you're average, you fit right in. And if you're above average, the basic terms of employment and premise of the arrangement is against your interests."
"Working a real job is a win if you're lazy, greedy, or unmotivated. If you're average, you fit right in. And if you're above average, the basic terms of employment and premise of the arrangement is against your interests."
•
•
Join Date: Feb 2007
Posts: 5
Reputation:
Rep Power: 0
Solved Threads: 0
•
•
•
•
Linker errors are usually generated when the linker can't find the function definations i.e. the body of the mentioned functions.
You need to place the "Serial.h" and "Serial.cpp" file in the same directory as that of the main or driver file and make sure the above mentioned functions are defined in the "Serial.cpp" file.
I tried what you asked and got the same error. I placed my 'hello world' file in the same directory that contains the "Serial.h" file and the "Serial.cpp" file. I also made sure that the list of directories in my compiler's include path contained the directory where all 3 of these files live.
For reference, here is the code for each of the files:
// 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 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
#endif
static const char THIS_FILE[] = __FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Helper methods
inline void CSerial::CheckRequirements (LPOVERLAPPED lpOverlapped, DWORD dwTimeout) const
{
#ifdef SERIAL_NO_OVERLAPPED
// Check if an overlapped structure has been specified
if (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 request
return ::CancelIo(m_hFile);
#endif // SERIAL_NO_CANCELIO
}
//////////////////////////////////////////////////////////////////////
// Code
CSerial::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 implicitly
Close();
}
}
CSerial::EPort CSerial::CheckPort (LPCTSTR lpszDevice)
{
// Try to open the device
HANDLE hFile = ::CreateFile(lpszDevice,
GENERIC_READ|GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
// Check if we could open the device
if (hFile == INVALID_HANDLE_VALUE)
{
// Display error
switch (::GetLastError())
{
case ERROR_FILE_NOT_FOUND:
// The specified COM-port does not exist
return EPortNotAvailable;
case ERROR_ACCESS_DENIED:
// The specified COM-port is in use
return EPortInUse;
default:
// Something else is wrong
return EPortUnknownError;
}
}
// Close handle
::CloseHandle(hFile);
// Port is available
return EPortAvailable;
}
LONG CSerial::Open (LPCTSTR lpszDevice, DWORD dwInQueue, DWORD dwOutQueue, bool fOverlapped)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the port isn't already opened
if (m_hFile)
{
m_lLastError = ERROR_ALREADY_INITIALIZED;
_RPTF0(_CRT_WARN,"CSerial::Open - Port already opened\n");
return m_lLastError;
}
// Open the device
m_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 handle
m_hFile = 0;
// Display error
m_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 information
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN,"CSerial::Open - Unable to create event\n");
// Close the port
::CloseHandle(m_hFile);
m_hFile = 0;
// Return the error
return m_lLastError;
}
}
#else
// Overlapped flag shouldn't be specified
_ASSERTE(!fOverlapped);
#endif
// Setup the COM-port
if (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 warning
long lLastError = ::GetLastError();
_RPTF0(_CRT_WARN,"CSerial::Open - Unable to setup the COM-port\n");
// Close the port
Close();
// Save last error from SetupComm
m_lLastError = lLastError;
return m_lLastError;
}
}
// Setup the default communication mask
SetMask();
// Non-blocking reads is default
SetupReadTimeouts(EReadTimeoutNonblocking);
// Setup the device for default settings
COMMCONFIG commConfig = {0};
DWORD dwSize = sizeof(commConfig);
commConfig.dwSize = dwSize;
if (::GetDefaultCommConfig(lpszDevice,&commConfig,&dwSize))
{
// Set the default communication configuration
if (!::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 successful
return m_lLastError;
}
LONG CSerial::Close (void)
{
// Reset error state
m_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 handle
if (m_hevtOverlapped)
{
::CloseHandle(m_hevtOverlapped);
m_hevtOverlapped = 0;
}
#endif
// Close COM port
::CloseHandle(m_hFile);
m_hFile = 0;
// Return successful
return m_lLastError;
}
LONG CSerial::Setup (EBaudrate eBaudrate, EDataBits eDataBits, EParity eParity, EStopBits eStopBits)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 device
CDCB dcb;
if (!::GetCommState(m_hFile,&dcb))
{
// Obtain the error code
m_lLastError = :: GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::Setup - Unable to obtain DCB information\n");
return m_lLastError;
}
// Set the new data
dcb.BaudRate = DWORD(eBaudrate);
dcb.ByteSize = BYTE(eDataBits);
dcb.Parity = BYTE(eParity);
dcb.StopBits = BYTE(eStopBits);
// Determine if parity is used
dcb.fParity = (eParity != EParNone);
// Set the new DCB structure
if (!::SetCommState(m_hFile,&dcb))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::Setup - Unable to set DCB information\n");
return m_lLastError;
}
// Return successful
return m_lLastError;
}
LONG CSerial::SetEventChar (BYTE bEventChar, bool fAdjustMask)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 device
CDCB dcb;
if (!::GetCommState(m_hFile,&dcb))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::SetEventChar - Unable to obtain DCB information\n");
return m_lLastError;
}
// Set the new event character
dcb.EvtChar = char(bEventChar);
// Adjust the event mask, to make sure the event will be received
if (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 structure
if (!::SetCommState(m_hFile,&dcb))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::SetEventChar - Unable to set DCB information\n");
return m_lLastError;
}
// Return successful
return m_lLastError;
}
LONG CSerial::SetMask (DWORD dwEventMask)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::SetMask - Unable to set event mask\n");
return m_lLastError;
}
// Save event mask and return successful
m_dwEventMask = dwEventMask;
return m_lLastError;
}
LONG CSerial::WaitEvent (LPOVERLAPPED lpOverlapped, DWORD dwTimeout)
{
// Check if time-outs are supported
CheckRequirements(lpOverlapped,dwTimeout);
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 specified
if (!m_hevtOverlapped && (lpOverlapped || (dwTimeout != INFINITE)))
{
// Set the internal error code
m_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 happen
OVERLAPPED ovInternal;
if (!lpOverlapped && m_hevtOverlapped)
{
// Setup our own overlapped structure
memset(&ovInternal,0,sizeof(ovInternal));
ovInternal.hEvent = m_hevtOverlapped;
// Use our internal overlapped structure
lpOverlapped = &ovInternal;
}
// Make sure the overlapped structure isn't busy
_ASSERTE(!m_hevtOverlapped || HasOverlappedIoCompleted(lpOverlapped));
// Wait for the COM event
if (!::WaitCommEvent(m_hFile,LPDWORD(&m_eEvent),lpOverlapped))
{
// Set the internal error code
long lLastError = ::GetLastError();
// Overlapped operation in progress is not an actual error
if (lLastError != ERROR_IO_PENDING)
{
// Save the error
m_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 structure
if (lpOverlapped == &ovInternal)
{
// Wait for the overlapped operation to complete
switch (::WaitForSingleObject(lpOverlapped->hEvent,dwTimeout))
{
case WAIT_OBJECT_0:
// The overlapped operation has completed
break;
case WAIT_TIMEOUT:
// Cancel the I/O operation
CancelCommIo();
// The operation timed out. Set the internal error code and quit
m_lLastError = ERROR_TIMEOUT;
return m_lLastError;
default:
// Set the internal error code
m_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 event
if (!::WaitCommEvent(m_hFile,LPDWORD(&m_eEvent),0))
{
// Set the internal error code
m_lLastError = ::GetLastError();
// Issue an error and quit
_RPTF0(_CRT_WARN,"CSerial::WaitEvent - Unable to wait for COM event\n");
return m_lLastError;
}
#endif
// Return successfully
return m_lLastError;
}
LONG CSerial::SetupHandshaking (EHandshake eHandshake)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 device
CDCB dcb;
if (!::GetCommState(m_hFile,&dcb))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::SetupHandshaking - Unable to obtain DCB information\n");
return m_lLastError;
}
// Set the handshaking flags
switch (eHandshake)
{
case EHandshakeOff:
dcb.fOutxCtsFlow = false; // Disable CTS monitoring
dcb.fOutxDsrFlow = false; // Disable DSR monitoring
dcb.fDtrControl = DTR_CONTROL_DISABLE; // Disable DTR monitoring
dcb.fOutX = false; // Disable XON/XOFF for transmission
dcb.fInX = false; // Disable XON/XOFF for receiving
dcb.fRtsControl = RTS_CONTROL_DISABLE; // Disable RTS (Ready To Send)
break;
case EHandshakeHardware:
dcb.fOutxCtsFlow = true; // Enable CTS monitoring
dcb.fOutxDsrFlow = true; // Enable DSR monitoring
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; // Enable DTR handshaking
dcb.fOutX = false; // Disable XON/XOFF for transmission
dcb.fInX = false; // Disable XON/XOFF for receiving
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; // Enable RTS handshaking
break;
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 transmission
dcb.fInX = true; // Enable XON/XOFF for receiving
dcb.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 structure
if (!::SetCommState(m_hFile,&dcb))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::SetupHandshaking - Unable to set DCB information\n");
return m_lLastError;
}
// Return successful
return m_lLastError;
}
LONG CSerial::SetupReadTimeouts (EReadTimeout eReadTimeout)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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-outs
COMMTIMEOUTS cto;
if (!::GetCommTimeouts(m_hFile,&cto))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::SetupReadTimeouts - Unable to obtain timeout information\n");
return m_lLastError;
}
// Set the new timeouts
switch (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 structure
if (!::SetCommTimeouts(m_hFile,&cto))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::SetupReadTimeouts - Unable to set timeout information\n");
return m_lLastError;
}
// Return successful
return m_lLastError;
}
CSerial::EBaudrate CSerial::GetBaudrate (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 device
CDCB dcb;
if (!::GetCommState(m_hFile,&dcb))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::GetBaudrate - Unable to obtain DCB information\n");
return EBaudUnknown;
}
// Return the appropriate baudrate
return EBaudrate(dcb.BaudRate);
}
CSerial::EDataBits CSerial::GetDataBits (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 device
CDCB dcb;
if (!::GetCommState(m_hFile,&dcb))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::GetDataBits - Unable to obtain DCB information\n");
return EDataUnknown;
}
// Return the appropriate bytesize
return EDataBits(dcb.ByteSize);
}
CSerial::EParity CSerial::GetParity (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 device
CDCB dcb;
if (!::GetCommState(m_hFile,&dcb))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::GetParity - Unable to obtain DCB information\n");
return EParUnknown;
}
// Check if parity is used
if (!dcb.fParity)
{
// No parity
return EParNone;
}
// Return the appropriate parity setting
return EParity(dcb.Parity);
}
CSerial::EStopBits CSerial::GetStopBits (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 device
CDCB dcb;
if (!::GetCommState(m_hFile,&dcb))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::GetStopBits - Unable to obtain DCB information\n");
return EStopUnknown;
}
// Return the appropriate stopbits
return EStopBits(dcb.StopBits);
}
DWORD CSerial::GetEventMask (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_lLastError = ERROR_INVALID_HANDLE;
// Issue an error and quit
_RPTF0(_CRT_WARN,"CSerial::GetEventMask - Device is not opened\n");
return 0;
}
// Return the event mask
return m_dwEventMask;
}
BYTE CSerial::GetEventChar (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 device
CDCB dcb;
if (!::GetCommState(m_hFile,&dcb))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::GetEventChar - Unable to obtain DCB information\n");
return 0;
}
// Set the new event character
return BYTE(dcb.EvtChar);
}
CSerial::EHandshake CSerial::GetHandshaking (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 device
CDCB dcb;
if (!::GetCommState(m_hFile,&dcb))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::GetHandshaking - Unable to obtain DCB information\n");
return EHandshakeUnknown;
}
// Check if hardware handshaking is being used
if ((dcb.fDtrControl == DTR_CONTROL_HANDSHAKE) && (dcb.fRtsControl == RTS_CONTROL_HANDSHAKE))
return EHandshakeHardware;
// Check if software handshaking is being used
if (dcb.fOutX && dcb.fInX)
return EHandshakeSoftware;
// No handshaking is being used
return EHandshakeOff;
}
LONG CSerial::Write (const void* pData, size_t iLen, DWORD* pdwWritten, LPOVERLAPPED lpOverlapped, DWORD dwTimeout)
{
// Check if time-outs are supported
CheckRequirements(lpOverlapped,dwTimeout);
// Overlapped operation should specify the pdwWritten variable
_ASSERTE(!lpOverlapped || pdwWritten);
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Use our own variable for read count
DWORD dwWritten;
if (pdwWritten == 0)
{
pdwWritten = &dwWritten;
}
// Reset the number of bytes written
*pdwWritten = 0;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 specified
if (!m_hevtOverlapped && (lpOverlapped || (dwTimeout != INFINITE)))
{
// Set the internal error code
m_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 happen
OVERLAPPED ovInternal;
if (!lpOverlapped && m_hevtOverlapped)
{
// Setup our own overlapped structure
memset(&ovInternal,0,sizeof(ovInternal));
ovInternal.hEvent = m_hevtOverlapped;
// Use our internal overlapped structure
lpOverlapped = &ovInternal;
}
// Make sure the overlapped structure isn't busy
_ASSERTE(!m_hevtOverlapped || HasOverlappedIoCompleted(lpOverlapped));
// Write the data
if (!::WriteFile(m_hFile,pData,iLen,pdwWritten,lpOverlapped))
{
// Set the internal error code
long lLastError = ::GetLastError();
// Overlapped operation in progress is not an actual error
if (lLastError != ERROR_IO_PENDING)
{
// Save the error
m_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 structure
if (lpOverlapped == &ovInternal)
{
// Wait for the overlapped operation to complete
switch (::WaitForSingleObject(lpOverlapped->hEvent,dwTimeout))
{
case WAIT_OBJECT_0:
// The overlapped operation has completed
if (!::GetOverlappedResult(m_hFile,lpOverlapped,pdwWritten,FALSE))
{
// Set the internal error code
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN,"CSerial::Write - Overlapped completed without result\n");
return m_lLastError;
}
break;
case WAIT_TIMEOUT:
// Cancel the I/O operation
CancelCommIo();
// The operation timed out. Set the internal error code and quit
m_lLastError = ERROR_TIMEOUT;
return m_lLastError;
default:
// Set the internal error code
m_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 data
if (!::WriteFile(m_hFile,pData,iLen,pdwWritten,0))
{
// Set the internal error code
m_lLastError = ::GetLastError();
// Issue an error and quit
_RPTF0(_CRT_WARN,"CSerial::Write - Unable to write the data\n");
return m_lLastError;
}
#endif
// Return successfully
return m_lLastError;
}
LONG CSerial::Write (LPCSTR pString, DWORD* pdwWritten, LPOVERLAPPED lpOverlapped, DWORD dwTimeout)
{
// Check if time-outs are supported
CheckRequirements(lpOverlapped,dwTimeout);
// Determine the length of the string
return 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 supported
CheckRequirements(lpOverlapped,dwTimeout);
// Overlapped operation should specify the pdwRead variable
_ASSERTE(!lpOverlapped || pdwRead);
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Use our own variable for read count
DWORD dwRead;
if (pdwRead == 0)
{
pdwRead = &dwRead;
}
// Reset the number of bytes read
*pdwRead = 0;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 specified
if (!m_hevtOverlapped && (lpOverlapped || (dwTimeout != INFINITE)))
{
// Set the internal error code
m_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 happen
OVERLAPPED ovInternal;
if (lpOverlapped == 0)
{
// Setup our own overlapped structure
memset(&ovInternal,0,sizeof(ovInternal));
ovInternal.hEvent = m_hevtOverlapped;
// Use our internal overlapped structure
lpOverlapped = &ovInternal;
}
// Make sure the overlapped structure isn't busy
_ASSERTE(!m_hevtOverlapped || HasOverlappedIoCompleted(lpOverlapped));
// Read the data
if (!::ReadFile(m_hFile,pData,iLen,pdwRead,lpOverlapped))
{
// Set the internal error code
long lLastError = ::GetLastError();
// Overlapped operation in progress is not an actual error
if (lLastError != ERROR_IO_PENDING)
{
// Save the error
m_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 structure
if (lpOverlapped == &ovInternal)
{
// Wait for the overlapped operation to complete
switch (::WaitForSingleObject(lpOverlapped->hEvent,dwTimeout))
{
case WAIT_OBJECT_0:
// The overlapped operation has completed
if (!::GetOverlappedResult(m_hFile,lpOverlapped,pdwRead,FALSE))
{
// Set the internal error code
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN,"CSerial::Read - Overlapped completed without result\n");
return m_lLastError;
}
break;
case WAIT_TIMEOUT:
// Cancel the I/O operation
CancelCommIo();
// The operation timed out. Set the internal error code and quit
m_lLastError = ERROR_TIMEOUT;
return m_lLastError;
default:
// Set the internal error code
m_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 data
if (!::ReadFile(m_hFile,pData,iLen,pdwRead,0))
{
// Set the internal error code
m_lLastError = ::GetLastError();
// Issue an error and quit
_RPTF0(_CRT_WARN,"CSerial::Read - Unable to read the data\n");
return m_lLastError;
}
#endif
// Return successfully
return m_lLastError;
}
LONG CSerial::Purge()
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 code
m_lLastError = ::GetLastError();
_RPTF0(_CRT_WARN,"CSerial::Purge - Overlapped completed without result\n");
}
// Return successfully
return m_lLastError;
}
LONG CSerial::Break (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_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 successfully
return m_lLastError;
}
CSerial::EEvent CSerial::GetEventType (void)
{
#ifdef _DEBUG
// Check if the event is within the mask
if ((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 type
m_eEvent = EEventNone;
// Return the current cause
return eEvent;
}
CSerial::EError CSerial::GetError (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Check if the device is open
if (m_hFile == 0)
{
// Set the internal error code
m_lLastError = ERROR_INVALID_HANDLE;
// Issue an error and quit
_RPTF0(_CRT_WARN,"CSerial::GetError - Device is not opened\n");
return EErrorUnknown;
}
// Obtain COM status
DWORD dwErrors = 0;
if (!::ClearCommError(m_hFile,&dwErrors,0))
{
// Set the internal error code
m_lLastError = ::GetLastError();
// Issue an error and quit
_RPTF0(_CRT_WARN,"CSerial::GetError - Unable to obtain COM status\n");
return EErrorUnknown;
}
// Return the error
return EError(dwErrors);
}
bool CSerial::GetCTS (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Obtain the modem status
DWORD dwModemStat = 0;
if (!::GetCommModemStatus(m_hFile,&dwModemStat))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::GetCTS - Unable to obtain the modem status\n");
return false;
}
// Determine if CTS is on
return (dwModemStat & MS_CTS_ON) != 0;
}
bool CSerial::GetDSR (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Obtain the modem status
DWORD dwModemStat = 0;
if (!::GetCommModemStatus(m_hFile,&dwModemStat))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::GetDSR - Unable to obtain the modem status\n");
return false;
}
// Determine if DSR is on
return (dwModemStat & MS_DSR_ON) != 0;
}
bool CSerial::GetRing (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Obtain the modem status
DWORD dwModemStat = 0;
if (!::GetCommModemStatus(m_hFile,&dwModemStat))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::GetRing - Unable to obtain the modem status");
return false;
}
// Determine if Ring is on
return (dwModemStat & MS_RING_ON) != 0;
}
bool CSerial::GetRLSD (void)
{
// Reset error state
m_lLastError = ERROR_SUCCESS;
// Obtain the modem status
DWORD dwModemStat = 0;
if (!::GetCommModemStatus(m_hFile,&dwModemStat))
{
// Obtain the error code
m_lLastError = ::GetLastError();
// Display a warning
_RPTF0(_CRT_WARN,"CSerial::GetRLSD - Unable to obtain the modem status");
return false;
}
// Determine if RLSD is on
return (dwModemStat & MS_RLSD_ON) != 0;
}// Serial.h - Definition 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
#ifndef __SERIAL_H
#define __SERIAL_H
//////////////////////////////////////////////////////////////////////
// The SERIAL_DEFAULT_OVERLAPPED defines if the default open mode uses
// overlapped I/O. When overlapped I/O is available (normal Win32
// platforms) it uses overlapped I/O. Windows CE doesn't allow the use
// of overlapped I/O, so it is disabled there by default.
#ifndef SERIAL_DEFAULT_OVERLAPPED
#ifndef SERIAL_NO_OVERLAPPED
#define SERIAL_DEFAULT_OVERLAPPED true
#else
#define SERIAL_DEFAULT_OVERLAPPED false
#endif
#endif
//////////////////////////////////////////////////////////////////////
//
// CSerial - Win32 wrapper for serial communications
//
// Serial communication often causes a lot of problems. This class
// tries to supply an easy to use interface to deal with serial
// devices.
//
// The class is actually pretty ease to use. You only need to open
// the COM-port, where you need to specify the basic serial
// communication parameters. You can also choose to setup handshaking
// and read timeout behaviour.
//
// The following serial classes are available:
//
// CSerial - Serial communication support.
// CSerialEx - Serial communication with listener thread for events
// CSerialSync - Serial communication with synchronized event handler
// CSerialWnd - Asynchronous serial support, which uses the Win32
// message queue for event notification.
// CSerialMFC - Preferred class to use in MFC-based GUI windows.
//
//
// Pros:
// -----
// - Easy to use (hides a lot of nasty Win32 stuff)
// - Fully ANSI and Unicode aware
//
// Cons:
// -----
// - Little less flexibility then native Win32 API, however you can
// use this API at the same time for features which are missing
// from this class.
// - Incompatible with Windows 95 or Windows NT v3.51 (or earlier),
// because CancelIo isn't support on these platforms. Define the
// SERIAL_NO_CANCELIO macro for support of these platforms as
// well. When this macro is defined, then only time-out values of
// 0 or INFINITE are valid.
//
//
// Copyright (C) 1999-2003 Ramon de Klein
// (Ramon.de.Klein@ict.nl)
class CSerial
{
// Class enumerations
public:
// Communication event
typedef enum
{
EEventUnknown = -1, // Unknown event
EEventNone = 0, // Event trigged without cause
EEventBreak = EV_BREAK, // A break was detected on input
EEventCTS = EV_CTS, // The CTS signal changed state
EEventDSR = EV_DSR, // The DSR signal changed state
EEventError = EV_ERR, // A line-status error occurred
EEventRing = EV_RING, // A ring indicator was detected
EEventRLSD = EV_RLSD, // The RLSD signal changed state
EEventRecv = EV_RXCHAR, // Data is received on input
EEventRcvEv = EV_RXFLAG, // Event character was received on input
EEventSend = EV_TXEMPTY, // Last character on output was sent
EEventPrinterError = EV_PERR, // Printer error occured
EEventRx80Full = EV_RX80FULL, // Receive buffer is 80 percent full
EEventProviderEvt1 = EV_EVENT1, // Provider specific event 1
EEventProviderEvt2 = EV_EVENT2, // Provider specific event 2
}
EEvent;
// Baudrate
typedef enum
{
EBaudUnknown = -1, // Unknown
EBaud110 = CBR_110, // 110 bits/sec
EBaud300 = CBR_300, // 300 bits/sec
EBaud600 = CBR_600, // 600 bits/sec
EBaud1200 = CBR_1200, // 1200 bits/sec
EBaud2400 = CBR_2400, // 2400 bits/sec
EBaud4800 = CBR_4800, // 4800 bits/sec
EBaud9600 = CBR_9600, // 9600 bits/sec
EBaud14400 = CBR_14400, // 14400 bits/sec
EBaud19200 = CBR_19200, // 19200 bits/sec (default)
EBaud38400 = CBR_38400, // 38400 bits/sec
EBaud56000 = CBR_56000, // 56000 bits/sec
EBaud57600 = CBR_57600, // 57600 bits/sec
EBaud115200 = CBR_115200, // 115200 bits/sec
EBaud128000 = CBR_128000, // 128000 bits/sec
EBaud256000 = CBR_256000, // 256000 bits/sec
}
EBaudrate;
// Data bits (5-8)
typedef enum
{
EDataUnknown = -1, // Unknown
EData5 = 5, // 5 bits per byte
EData6 = 6, // 6 bits per byte
EData7 = 7, // 7 bits per byte
EData8 = 8 // 8 bits per byte (default)
}
EDataBits;
// Parity scheme
typedef enum
{
EParUnknown = -1, // Unknown
EParNone = NOPARITY, // No parity (default)
EParOdd = ODDPARITY, // Odd parity
EParEven = EVENPARITY, // Even parity
EParMark = MARKPARITY, // Mark parity
EParSpace = SPACEPARITY // Space parity
}
EParity;
// Stop bits
typedef enum
{
EStopUnknown = -1, // Unknown
EStop1 = ONESTOPBIT, // 1 stopbit (default)
EStop1_5 = ONE5STOPBITS,// 1.5 stopbit
EStop2 = TWOSTOPBITS // 2 stopbits
}
EStopBits;
// Handshaking
typedef enum
{
EHandshakeUnknown = -1, // Unknown
EHandshakeOff = 0, // No handshaking
EHandshakeHardware = 1, // Hardware handshaking (RTS/CTS)
EHandshakeSoftware = 2 // Software handshaking (XON/XOFF)
}
EHandshake;
// Timeout settings
typedef enum
{
EReadTimeoutUnknown = -1, // Unknown
EReadTimeoutNonblocking = 0, // Always return immediately
EReadTimeoutBlocking = 1 // Block until everything is retrieved
}
EReadTimeout;
// Communication errors
typedef enum
{
EErrorUnknown = 0, // Unknown
EErrorBreak = CE_BREAK, // Break condition detected
EErrorFrame = CE_FRAME, // Framing error
EErrorIOE = CE_IOE, // I/O device error
EErrorMode = CE_MODE, // Unsupported mode
EErrorOverrun = CE_OVERRUN, // Character buffer overrun, next byte is lost
EErrorRxOver = CE_RXOVER, // Input buffer overflow, byte lost
EErrorParity = CE_RXPARITY,// Input parity error
EErrorTxFull = CE_TXFULL // Output buffer full
}
EError;
// Port availability
typedef enum
{
EPortUnknownError = -1, // Unknown error occurred
EPortAvailable = 0, // Port is available
EPortNotAvailable = 1, // Port is not present
EPortInUse = 2 // Port is in use
}
EPort;
// Construction
public:
CSerial();
virtual ~CSerial();
// Operations
public:
// Check if particular COM-port is available (static method).
static EPort CheckPort (LPCTSTR lpszDevice);
// Open the serial communications for a particular COM port. You
// need to use the full devicename (i.e. "COM1") to open the port.
// It's possible to specify the size of the input/output queues.
virtual LONG Open (LPCTSTR lpszDevice, DWORD dwInQueue = 0, DWORD dwOutQueue = 0, bool fOverlapped = SERIAL_DEFAULT_OVERLAPPED);
// Close the serial port.
virtual LONG Close (void);
// Setup the communication settings such as baudrate, databits,
// parity and stopbits. The default settings are applied when the
// device has been opened. Call this function if these settings do
// not apply for your application. If you prefer to use integers
// instead of the enumerated types then just cast the integer to
// the required type. So the following two initializations are
// equivalent:
//
// Setup(EBaud9600,EData8,EParNone,EStop1)
//
// or
//
// Setup(EBaudrate(9600),EDataBits(8),EParity(NOPARITY),EStopBits(ONESTOPBIT))
//
// In the latter case, the types are not validated. So make sure
// that you specify the appropriate values.
virtual LONG Setup (EBaudrate eBaudrate = EBaud9600,
EDataBits eDataBits = EData8,
EParity eParity = EParNone,
EStopBits eStopBits = EStop1);
// Set/clear the event character. When this byte is being received
// on the serial port then the EEventRcvEv event is signalled,
// when the mask has been set appropriately. If the fAdjustMask flag
// has been set, then the event mask is automatically adjusted.
virtual LONG SetEventChar (BYTE bEventChar, bool fAdjustMask = true);
// Set the event mask, which indicates what events should be
// monitored. The WaitEvent method can only monitor events that
// have been enabled. The default setting only monitors the
// error events and data events. An application may choose to
// monitor CTS. DSR, RLSD, etc as well.
virtual LONG SetMask (DWORD dwMask = EEventBreak|EEventError|EEventRecv);
// The WaitEvent method waits for one of the events that are
// enabled (see SetMask).
virtual LONG WaitEvent (LPOVERLAPPED lpOverlapped = 0, DWORD dwTimeout = INFINITE);
// Setup the handshaking protocol. There are three forms of
// handshaking:
//
// 1) No handshaking, so data is always send even if the receiver
// cannot handle the data anymore. This can lead to data loss,
// when the sender is able to transmit data faster then the
// receiver can handle.
// 2) Hardware handshaking, where the RTS/CTS lines are used to
// indicate if data can be sent. This mode requires that both
// ports and the cable support hardware handshaking. Hardware
// handshaking is the most reliable and efficient form of
// handshaking available, but is hardware dependant.
// 3) Software handshaking, where the XON/XOFF characters are used
// to throttle the data. A major drawback of this method is that
// these characters cannot be used for data anymore.
virtual LONG SetupHandshaking (EHandshake eHandshake);
// Read operations can be blocking or non-blocking. You can use
// this method to setup wether to use blocking or non-blocking
// reads. Non-blocking reads is the default, which is required
// for most applications.
//
// 1) Blocking reads, which will cause the 'Read' method to block
// until the requested number of bytes have been read. This is
// useful if you know how many data you will receive.
// 2) Non-blocking reads, which will read as many bytes into your
// buffer and returns almost immediately. This is often the
// preferred setting.
virtual LONG SetupReadTimeouts (EReadTimeout eReadTimeout);
// Obtain communication settings
virtual EBaudrate GetBaudrate (void);
virtual EDataBits GetDataBits (void);
virtual EParity GetParity (void);
virtual EStopBits GetStopBits (void);
virtual EHandshake GetHandshaking (void);
virtual DWORD GetEventMask (void);
virtual BYTE GetEventChar (void);
// Write data to the serial port. Note that we are only able to
// send ANSI strings, because it probably doesn't make sense to
// transmit Unicode strings to an application.
virtual LONG Write (const void* pData, size_t iLen, DWORD* pdwWritten = 0, LPOVERLAPPED lpOverlapped = 0, DWORD dwTimeout = INFINITE);
virtual LONG Write (LPCSTR pString, DWORD* pdwWritten = 0, LPOVERLAPPED lpOverlapped = 0, DWORD dwTimeout = INFINITE);
// Read data from the serial port. Refer to the description of
// the 'SetupReadTimeouts' for an explanation about (non) blocking
// reads and how to use this.
virtual LONG Read (void* pData, size_t iLen, DWORD* pdwRead = 0, LPOVERLAPPED lpOverlapped = 0, DWORD dwTimeout = INFINITE);
// Send a break
LONG Break (void);
// Determine what caused the event to trigger
EEvent GetEventType (void);
// Obtain the error
EError GetError (void);
// Obtain the COMM and event handle
HANDLE GetCommHandle (void) { return m_hFile; }
// Check if com-port is opened
bool IsOpen (void) const { return (m_hFile != 0); }
// Obtain last error status
LONG GetLastError (void) const { return m_lLastError; }
// Obtain CTS/DSR/RING/RLSD settings
bool GetCTS (void);
bool GetDSR (void);
bool GetRing (void);
bool GetRLSD (void);
// Purge all buffers
LONG Purge (void);
protected:
// Internal helper class which wraps DCB structure
class CDCB : public DCB
{
public:
CDCB() { DCBlength = sizeof(DCB); }
};
// Attributes
protected:
LONG m_lLastError; // Last serial error
HANDLE m_hFile; // File handle
EEvent m_eEvent; // Event type
DWORD m_dwEventMask; // Event mask
#ifndef SERIAL_NO_OVERLAPPED
HANDLE m_hevtOverlapped; // Event handle for internal overlapped operations
#endif
protected:
// Check the requirements
void CheckRequirements (LPOVERLAPPED lpOverlapped, DWORD dwTimeout) const;
// CancelIo wrapper (for Win95 compatibility)
BOOL CancelCommIo (void);
};
#endif // __SERIAL_H

