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:

#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/Serial/Serial_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 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.

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

It seems like all of the functions are there in Serial.cpp but unfortunately the Linker Error problem is still there. Any more ideas would be greatly appreciated!

-Sam

You need to make sure that the .cpp files you're trying to compile are actually ADDED to your project, not just in the same directory. Otherwise the compiler has no way of finding them.

If that doesn't work, try cleaning all targets and removing all intermediate files.

You need to make sure that the .cpp files you're trying to compile are actually ADDED to your project, not just in the same directory. Otherwise the compiler has no way of finding them.

Thanks, Joeprogrammer! That definately cleared up the linker error problem I was having. Do you, in general, need to put ALL relavent .cpp files into a project together before it will work? What about .h files, do they have to be added to the project in the same way? Thanks for all of your help.

Of course, there is a new problem now.

When I compile, I get many errors like this:

130 C:\Dev-Cpp\include\Serial\Serial\Serial.cpp `_CRT_WARN' undeclared (first use this function)

for each time the file Serial.cpp mentions _CRT_WARN, _RPTF0, or _ASSERTE, which amounts to about 57 errors.

Also, in the message window, I am seeing a lot of messages like this:
In destructor
In member function
In member function,
etc.

My compiler dosen't count these messages as errors, so what should I understand them to mean?

Thank you all for your help.

P.S. Since the topic of 'linker error' has been solved for now with this particular program, should I start a new thread when asking about the previously mentioned 'undeclared' errors?

Thanks in advance,
Sam

Do you, in general, need to put ALL relavent .cpp files into a project together before it will work?

If you're using code from a .cpp file, then yes it has to be included in the project.
Technically header files don't actually have to be included, because header files are actually included with the preprocessor directive #include . Having the header files in the same directory as the rest of your project is all that's required.

When I compile, I get many errors like this:

130 C:\Dev-Cpp\include\Serial\Serial\Serial.cpp `_CRT_WARN' undeclared (first use this function)

It's exactly what the compiler says it is: it has no idea what _CRT_WARN is because it's never been declared. It's probably a flag that you need to #define, so if you haven't written this code, it's a good idea to get documentation for it.

Also, in the message window, I am seeing a lot of messages like this:
In destructor
In member function
In member function,
etc.

My compiler dosen't count these messages as errors, so what should I understand them to mean?

These messages simply help specify where an error is located so you can correct it. Look at previous error messages (or later ones depending on your compiler) to find out the exact cause of the error.

In my editor that file is not there I dont know wheter it is missed or not.
is it compulasory to use external variables?

This question has already been answered. Start a new discussion instead.