C++ – Opening a virtual COM port with CSerial (C++)

cserial-portvisual c++winapi

I am using Ramon de Klein's CSerial library in order to open and manage a serial port from my C++ code.

My hardware implements a serial tu USB converter with an FTDI chip, so I am able to connect the serial port to a USB plug in my CPU. Once FTDI drivers are installed, a virtual COM port is shown at 'Device Manager' (Windows).

If I try to open it, it works.

But now I have installed a USB to ethernet server like this one. So I install its drivers and software, and after connecting some usb device to it, is is detected and also added as a virtual serial port to 'Device Manager' window.

But when I try to open the port, it doesn't work. It does work if I open the port with an HyperTerminal-like application, just as if it was a normal serial port, but not in my code.

CSerial library works as if it were creating a new file, and given LastErrorCode is 2: "File not found". This is the Open method from the CSerial library:

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;
}

I don't understand why it doesn't work in the same way as plugging the USB directly to the computer: I always thought that it should work if the COM is listed in 'Device Manager', independently from where it is really connected.

On balance, the way data is comming from was:

RS232—>converted to USB—>USB CPU connector—>virtualized as RS232 in a COM port

And now is:

RS232—>converted to USB—>converted to ethernet by the "netUSB server"—>Ethernet/WiFi at the CPU—>virtualized as a USB device—>virtualized as RS232 in a COM port

Any help?

Best Answer

The problem appeared not because of the hardware elements, but because of the number assigned to the COM port.

When COM port is over 9, CreateFile function fails to open the device, and returns an INVALID_HANDLE_VALUE.

Bug is reported, and solution is reported here.

Related Topic