Serial port reading in c, using WinApi functions; WaitCommEvent fails

2

I tried to write a small event-based application in C for serial port reading (sources below). My program is to use the WinApi functions. The comport.c has the functions written to handle COM-port (open, read, write), the utils.c has some helper functions.

My program produces always the following output:

COM1 is selected to be listened.

GetCommMask result: 0x00000029 (EV_RXCHAR: 0x0001, EV_CTS: 0x0008, EV_RLSD: 0x0020)

Press any key to proceed...

I/O is pending (WaitCommEvent)...

I/O is pending (WaitCommEvent)...

I/O is pending (WaitCommEvent)...

I/O is pending (WaitCommEvent)...

I/O is pending (WaitCommEvent)...

It seems, that the function WaitCommEvent fails, the GetLastError() gives back error 87 (I/O pending).

Please help me to find out what the problem is, which parameter is invalid? See below the code.

The main.c:

#include "stdafx.h"
#include "comport.h"
#include "utils.h"
#include <conio.h>

#define READ_BUFF_MAX_LENGTH    1024

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD dwEvtMask;
    HANDLE hComPort;
    OVERLAPPED oEventHandler;
    int portNum;
    DWORD readTotal;
    BOOL bOverlappedPending = FALSE;
    char readBuff[READ_BUFF_MAX_LENGTH];

    if(2 > argc)
    {
        printf("Use the program: ZliFuerZlvbusInterface.exe XXX (where XXX is the number of com port), \r\ne.G. for COM1 -> ZliFuerZlvbusInterface.exe 1\r\n");
        return 1;
    }
    else
    {
        portNum = atoi(argv[1]);
        if(0 == IsValidComNumber(portNum))
        {
            printf("ERROR: COM port number %d is invalid (parsed from '%s').\r\n", portNum, argv[1]);
            return 2;
        }
        else
        {
            printf("COM%d is selected to be listened.\r\n", portNum);
        }
    }

    if(0 == CreateSerialConnection(&hComPort, &oEventHandler, portNum, 1200, 8, EVEN, STOP1))
    {
        return 3;
    }

    if(FALSE == GetCommMask(hComPort, &dwEvtMask))
    {
        printf("GetCommMask failed with error:\r\n");
        PrintLastErrorText();
        return 4;
    }
    else
    {
        printf("GetCommMask result: 0x%08X (EV_RXCHAR: 0x0001, EV_CTS: 0x0008, EV_RLSD: 0x0020)\r\n", dwEvtMask);
    }

    printf("Press any key to proceed...\r\n");
    getch();

    while(1)
    {
        if(0 != kbhit())
        {
            if(27 == getch()) // ESC pressed
            {
                printf("Key ESC pressed, exiting...\r\n");
                break;
            }
        }

        bOverlappedPending = FALSE;
        readTotal = 0;

        if(TRUE == WaitCommEvent(hComPort, &dwEvtMask, &oEventHandler))
        {
            if(dwEvtMask & EV_CTS) // Clear-to-send signal present
            {
                PrintCurrentDateTime();
                printf("COM%d: Clear-to-send signal set\r\n", portNum);
            }

            if(dwEvtMask & EV_RLSD) // Data-carrier-detect signal present
            {
                PrintCurrentDateTime();
                printf("COM%d: Data-carrier-detect signal set\r\n", portNum);
            }

            if(dwEvtMask & EV_RXCHAR) // Data received
            {
                ReadSerial(&hComPort, &oEventHandler, portNum, readBuff, READ_BUFF_MAX_LENGTH);
            }
        }
        else
        {
            if(ERROR_IO_PENDING == GetLastError())
            {
                printf("I/O is pending (WaitCommEvent)...\r\n");
                bOverlappedPending = TRUE;
            }
            else
            {
                printf("WaitCommEvent failed with error:\r\n");
                PrintLastErrorText();
            }
        }

        if(TRUE == bOverlappedPending)
        {
            if(FALSE == GetOverlappedResult(hComPort, &oEventHandler, &readTotal, TRUE))
            {
                printf("GetOverlappedResult failed with error:\r\n");
                PrintLastErrorText();
            }
        }
    }

    CloseSerialConnection(&hComPort);

    return 0;
}

The comport.c:

#include <stdio.h>
#include "comport.h"
#include "utils.h"

int IsValidComNumber(int com)
{
    if ((com < 1) ||
        (com > 256))
    {
        return 0;
    }

    return 1;
}

int IsValidBaud(int baud)
{
    switch(baud)
    {
    case CBR_110:
    case CBR_300:
    case CBR_600:
    case CBR_1200:
    case CBR_2400:
    case CBR_4800:
    case CBR_9600:
    case CBR_14400:
    case CBR_19200:
    case CBR_38400:
    case CBR_56000:
    case CBR_57600:
    case CBR_115200:
    case CBR_128000:
    case CBR_256000:
        {
            return 1;
            break;
        }
    default:
        {
            break;
        }
    }

    return 0;
}

int IsValidBits(int bits)
{
    if ((bits < 5) ||
        (bits > 8))
    {
        return 0;
    }
    else
    {
        return 1;
    }
}

int CreateSerialConnection(HANDLE* handle, OVERLAPPED* overlapped, int portNumber, int baud, int bits, enum ParType parity, enum StopType stopbits)
{
    DCB dcb;
    COMMTIMEOUTS timeouts;
    TCHAR portVirtualFile[32];
    
    // For serial port name this format must be used (as virtual file): "\\\\.\\COMx"
    memset(portVirtualFile, 0, 32);
#ifdef _UNICODE
    swprintf(portVirtualFile, 32, L"\\\\.\\COM%d", portNumber);
#else
    sprintf_s(portVirtualFile, 32, "\\\\.\\COM%d", portNumber);
#endif

    if(0 == IsValidBaud(baud))
    {
        printf("ERROR: Specified baud rate %d is invalid for serial connection to COM%d.\r\n", baud, portNumber);
        return 0;
    }

    if(0 == IsValidBits(bits))
    {
        printf("ERROR: Specified number of data bits %d is invalid for serial connection to COM%d.\r\n", bits, portNumber);
        return 0;
    }

    *handle = CreateFile(portVirtualFile,    // Specify port device
        GENERIC_READ | GENERIC_WRITE,        // Specify mode that open device.
        0,                                    // the devide isn't shared.
        NULL,                                // the object gets a default security.
        OPEN_EXISTING,                        // Specify which action to take on file. 
        FILE_FLAG_OVERLAPPED,                // Use overlapped I/O.
        NULL);                                // default.

    if(*handle == INVALID_HANDLE_VALUE)
    {
        printf("ERROR: Opening serial port COM%d failed\r\n", portNumber);
        return 0;
    }

    if(FALSE == GetCommState(*handle, &dcb))
    {
        printf("ERROR: Getting current state of COM%d\r\n", portNumber);
        PrintLastErrorText();
        return 0;
    }

    memset(&dcb, 0, sizeof(dcb));    //zero initialize the structure
    dcb.DCBlength = sizeof(dcb);    //fill in length
    dcb.BaudRate = baud;        // baud rate
    dcb.ByteSize = bits;        // data size, xmit and rcv
    dcb.Parity   = parity;        // parity bit
    dcb.StopBits = stopbits;    // stop bits

    if(FALSE == SetCommState(*handle, &dcb))
    {
        printf("ERROR: Setting new state of COM%d failed.\r\n", portNumber);
        PrintLastErrorText();
        return 0;
    }

    if(FALSE == SetCommMask(*handle, EV_RXCHAR | EV_CTS | EV_RLSD))
    {
        printf("ERROR: Setting new COM MASK (events) for COM%d failed.\r\n", portNumber);
        PrintLastErrorText();
        return 0;
    }

    timeouts.ReadIntervalTimeout = MAXDWORD;
    timeouts.ReadTotalTimeoutMultiplier = 0;
    timeouts.ReadTotalTimeoutConstant = 0;
    timeouts.WriteTotalTimeoutMultiplier = 0;
    timeouts.WriteTotalTimeoutConstant = 0;
    if(FALSE == SetCommTimeouts(*handle, &timeouts))
    {
        printf("ERROR: Setting timeout parameters for COM%d failed.\r\n", portNumber);
        PrintLastErrorText();
        return 0;
    }

    (*overlapped).hEvent = CreateEvent(
        NULL,   // default security attributes 
        TRUE,   // manual-reset event 
        FALSE,  // not signaled 
        NULL    // no name
    );

    
    if(NULL == overlapped->hEvent)
    {
        printf("ERROR: CreateEvent for COM%d failed.\r\n", portNumber);
        PrintLastErrorText();
        return 0;
    }

    // Initialize the rest of the OVERLAPPED structure to zero.
    overlapped->Internal = 0;
    overlapped->InternalHigh = 0;
    overlapped->Offset = 0;
    overlapped->OffsetHigh = 0;

    return 1;
}

int CloseSerialConnection(HANDLE* handle)
{
    if(FALSE == CloseHandle(*handle))
    {
        printf("ERROR: Cannot close handle 0x8.8%X\r\n", *handle);
        PrintLastErrorText();
        return 0;
    }
    
    *handle = NULL;
    return 1;
}

int ReadSerial(HANDLE* handle, LPOVERLAPPED ov, int num, char* buffer, int max_len)
{
    DWORD readTotal = 0;

    if(FALSE == ClearCommError(*handle, NULL, NULL))
    {
        printf("ClearCommError failed with error:\r\n");
        PrintLastErrorText();
        return 0;
    }
    else
    {
        memset(buffer, 0, max_len);
        if(FALSE == ReadFile(*handle, buffer, max_len, &readTotal, ov))
        {
            printf("ERROR: Reading from port COM%d failed\r\n", num);
            if(ERROR_IO_PENDING == GetLastError())
            {
                printf("I/O is pending (ReadFile)...\r\n");
                
                if(FALSE == GetOverlappedResult(*handle, ov, &readTotal, TRUE))
                {
                    printf("GetOverlappedResult failed with error:\r\n");
                    PrintLastErrorText();
                    return 0;
                }
            }
            else
            {
                PrintLastErrorText();
                return 0;
            }
        }
        else
        {
            PrintCurrentDateTime();
            printf("Received %d characters on port COM%d: ", readTotal, num);
            PrintBufferContent(buffer, readTotal);
        }
    }

    return 1;
}

The utils.c:

#include <Windows.h>
#include <stdio.h>
#include "utils.h"

void PrintLastErrorText(void)
{
    DWORD retSize;
    LPTSTR pTemp = NULL;

    retSize = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&pTemp, 0, NULL);
    if ((retSize > 0) &&
        (pTemp != NULL))
    {
        printf("Last error: %s (0x%08X)\r\n", pTemp, GetLastError());
        LocalFree((HLOCAL)pTemp);
    }
}

void PrintCurrentDateTime(void)
{
    SYSTEMTIME systime;
    GetLocalTime(&systime);
    printf("%04d.%02d.%02d %02d:%02d:%02d:%03d ", systime.wYear, systime.wMonth, systime.wDay, systime.wHour, systime.wMinute, systime.wSecond, systime.wMilliseconds);
}

void PrintBufferContent(char* buff, int len)
{
    int i;
    for(i = 0; i<len; i++)
    {
        printf("%02X ", buff[i]);
    }
}
c
windows
winapi
console
serial-port
asked on Stack Overflow Nov 20, 2015 by macilaci123 • edited Jun 20, 2020 by Community

1 Answer

1

You're using the same OVERLAPPED structure for WaitCommEvent and ReadFile. Try using separate/independent OVERLAPPED for each.

UPDATE: Ignore that previous answer.

If your call to WaitCommEvent returns ERROR_IO_PENDING, you're not waiting for it to complete. Rather than loop around and call WaitCommEvent again, you need to wait for the operation to complete (typically via GetOverlappedResult).

You cannot have multiple pending asynchronous requests share a single OVERLAPPED structure. By looping and calling WaitCommEvent again after ERROR_IO_PENDING, that's exactly what's happening.

answered on Stack Overflow Nov 20, 2015 by keithmo • edited Nov 20, 2015 by keithmo

User contributions licensed under CC BY-SA 3.0