To exclude possible problems from .NET SerialPort I've written a C++ version.
While doing so I noticed that many settings in the DCB structure not only do not matter but if changed are causing problems. So,... a baud rate of 0 bytes looks strange but is actually working for a virtual USB-serial port.
Monitoring COMSTAT data I can see that there is data available, exact 0x3800 bytes. And also that ReadFile(...) is not using the data.
Why does ReadFile(...) not want to read the data?
I've not yet figured out why.
Here are the results that I get.
COM port = "COM3", changed
DCB DCBlength = 0x1c = 28
BaudRate = 0
fBinary = 1
fParity = 0
fOutxCtsFlow = 0
fOutxDsrFlow = 0
fDtrControl = 1
fDsrSensitivity = 0
fTXContinueOnXoff = 0
fOutX = 0
fInX = 0
fErrorChar = 0
fNull = 0
fRtsControl = 1
fAbortOnError = 0
XonLim = 0x0
XoffLim = 0x0
ByteSize = 0
Parity = 0
StopBits = 0
XonChar = 0x0
XoffChar = 0x0
ErrorChar = 0x0
EofChar = 0x0
EvtChar = 0x0
errors code = 0x0
comstat fCtsHole = 0
fDsrHold = 0
fRlsdHold = 0
fXoffHold = 0
fXoffSent = 0
fEof = 0
fTxim = 0
InQue = 14336
OutQue = 0
Here is the code for my C++ version.
#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <windows.h>
#define COM_PORT "COM3" /* "\\\\.\\COM3" */
#define BUFFER_SIZE 1024
#define BYTES_PER_LINE 16
int count = 0;
int byte_count = 0;
void OnRx(byte b)
{
if (count % BYTES_PER_LINE == 0)
{
std::cout << std::endl << std::setw(6) << std::hex << byte_count << ":";
count = 0;
byte_count += BYTES_PER_LINE;
}
std::cout << " " << std::setw(2) << std::hex << b;
}
void LogDCB(const DCB &serialParams, const char * state)
{
std::cout << std::endl
<< "COM port = \"" << COM_PORT << "\", " << state << std::endl
<< std::hex
<< "DCB DCBlength = 0x" << std::setw(2) << serialParams.DCBlength
<< std::dec
<< " = " << serialParams.DCBlength << std::endl;
std::cout << " BaudRate = " << serialParams.BaudRate << std::endl;
std::cout << " fBinary = " << serialParams.fBinary << std::endl;
std::cout << " fParity = " << serialParams.fParity << std::endl;
std::cout << " fOutxCtsFlow = " << serialParams.fOutxCtsFlow << std::endl;
std::cout << " fOutxDsrFlow = " << serialParams.fOutxDsrFlow << std::endl;
std::cout << " fDtrControl = " << serialParams.fDtrControl << std::endl;
std::cout << " fDsrSensitivity = " << serialParams.fDsrSensitivity << std::endl;
std::cout << " fTXContinueOnXoff = " << serialParams.fTXContinueOnXoff << std::endl;
std::cout << " fOutX = " << serialParams.fOutX << std::endl;
std::cout << " fInX = " << serialParams.fInX << std::endl;
std::cout << " fErrorChar = " << serialParams.fErrorChar << std::endl;
std::cout << " fNull = " << serialParams.fNull << std::endl;
std::cout << " fRtsControl = " << serialParams.fRtsControl << std::endl;
std::cout << " fAbortOnError = " << serialParams.fAbortOnError << std::endl;
std::cout << std::hex
<< " XonLim = 0x" << serialParams.XonLim << std::endl;
std::cout << " XoffLim = 0x" << serialParams.XoffLim << std::endl;
std::cout << std::dec
// byte values of 0 are not visible, casting to unsigned int helps.
<< " ByteSize = " << static_cast<unsigned int>(serialParams.ByteSize) << std::endl;
std::cout << " Parity = " << static_cast<unsigned int>(serialParams.Parity) << std::endl;
std::cout << " StopBits = " << static_cast<unsigned int>(serialParams.StopBits) << std::endl;
std::cout << std::hex
// char values are not displayed as values, double cast required to loose the sign and char type info to fix it.
<< " XonChar = 0x" << static_cast<unsigned int>(static_cast<unsigned char>(serialParams.XonChar)) << std::endl;
std::cout << " XoffChar = 0x" << static_cast<unsigned int>(static_cast<unsigned char>(serialParams.XoffChar)) << std::endl;
std::cout << " ErrorChar = 0x" << static_cast<unsigned int>(static_cast<unsigned char>(serialParams.ErrorChar)) << std::endl;
std::cout << " EofChar = 0x" << static_cast<unsigned int>(static_cast<unsigned char>(serialParams.EofChar)) << std::endl;
std::cout << " EvtChar = 0x" << static_cast<unsigned int>(static_cast<unsigned char>(serialParams.EvtChar)) << std::endl
<< std::dec;
}
int main()
{
// see: https://learn.microsoft.com/nl-nl/windows/win32/devio/monitoring-communications-events
// see: https://www.codeproject.com/Articles/2682/Serial-Communication-in-Windows
// see: https://web.archive.org/web/20180127160838/http:/bd.eduweb.hhs.nl/micprg/pdf/serial-win.pdf
DWORD dw;
// Open serial port
HANDLE serialHandle = CreateFile(
COM_PORT, // FileName
GENERIC_READ | GENERIC_WRITE, // DesiredAccess: { GENERIC_READ, GENERIC_WRITE, ... }
0, // ShareMode: If this parameter is zero ..., the file or device
// cannot be shared and cannot be opened again until the handle
// to the file or device is closed.
// { 0, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE }
nullptr, // SecurityAttributes: This parameter can be NULL
OPEN_EXISTING, // CreationDisposition: For devices other than files, this
// parameter is usually set to OPEN_EXISTING.
// { CREATE_ALWAYS, CREATE_NEW, OPEN_ALWAYS, OPEN_EXISTING, TRUNCATE_EXISTING }
FILE_FLAG_OVERLAPPED, // FlagsAndAttributes: The file or device attributes and flags,
// FILE_ATTRIBUTE_NORMAL being the most common default value for files.
//This parameter can also contain combinations of flags (FILE_FLAG_*) for
// control of file or device caching behavior, access modes, and other
// special-purpose flags.
// Some of the following file attributes and flags may only apply to
// files and not necessarily all other types of devices
// { FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ENCRYPTED, FILE_ATTRIBUTE_HIDDEN,
// FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_OFFLINE, FILE_ATTRIBUTE_READONLY,
// FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_TEMPORARY } | {
// FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_DELETE_ON_CLOSE, FILE_FLAG_NO_BUFFERING,
// FILE_FLAG_OPEN_NO_RECALL, FILE_FLAG_OPEN_REPARSE_POINT, FILE_FLAG_OVERLAPPED,
// FILE_FLAG_POSIX_SEMANTICS, FILE_FLAG_RANDOM_ACCESS, FILE_FLAG_SESSION_AWARE,
// FILE_FLAG_SEQUENTIAL_SCAN, FILE_FLAG_WRITE_THROUGH } | {
// SECURITY_ANONYMOUS, SECURITY_CONTEXT_TRACKING, SECURITY_DELEGATION,
// SECURITY_EFFECTIVE_ONLY, SECURITY_IDENTIFICATION, SECURITY_IMPERSONATION }
nullptr); // TemplateFile: A valid handle to a template file with the GENERIC_READ access right.
// This parameter can be NULL.
if (serialHandle == INVALID_HANDLE_VALUE)
{
dw = GetLastError();
std::cout << "Cannot open port \"" << COM_PORT << "\", error code = " << std::setw(2) << std::hex << dw << std::dec << " = " << dw << std::endl;
return 0;
}
// Set DCB values.
DCB serialParams = { 0 };
serialParams.DCBlength = sizeof(serialParams);
GetCommState(serialHandle, &serialParams);
LogDCB(serialParams, "defaults");
bool DCB_changed = false;
//serialParams.XonLim = 0x0800;
//serialParams.XoffLim = 0x0200;
serialParams.fDtrControl = DTR_CONTROL_ENABLE;
//serialParams.fRtsControl = RTS_CONTROL_HANDSHAKE;
//serialParams.XonChar = 0x11;
//serialParams.XoffChar = 0x13;
DCB_changed = true;
if (DCB_changed)
{
try
{
SetLastError(0);
SetCommState(serialHandle, &serialParams);
}
catch (...)
{
dw = GetLastError();
std::cout << std::endl << "SetCommState error code = " << std::setw(2) << std::hex << dw << std::dec << std::endl;
}
GetCommState(serialHandle, &serialParams);
LogDCB(serialParams, "changed");
}
// Reset buffers and raise DTR.
EscapeCommFunction(serialHandle, CLRDTR);
FlushFileBuffers(serialHandle);
EscapeCommFunction(serialHandle, SETDTR);
// Set timeouts
COMMTIMEOUTS timeout = { 0 };
timeout.ReadIntervalTimeout = 50;
timeout.ReadTotalTimeoutConstant = 50;
timeout.ReadTotalTimeoutMultiplier = 50;
timeout.WriteTotalTimeoutConstant = 50;
timeout.WriteTotalTimeoutMultiplier = 10;
SetCommTimeouts(serialHandle, &timeout);
OVERLAPPED overlapped;
char buffer[BUFFER_SIZE];
DWORD count;
BOOL result;
DWORD i;
DWORD errors;
COMSTAT comstat;
for (int tries = 0; tries < 10;)
{
result = ReadFile(
serialHandle, // File
&buffer, // Buffer
BUFFER_SIZE, // NumberOfBytesToRead
&count, // NumberOfBytesRead
&overlapped // Overlapped
);
if (result)
{
tries = 0;
for (i = 0; i < count; i++) OnRx(buffer[i]);
}
else
{
Sleep(100);
tries++;
}
if (ClearCommError(serialHandle, &errors, &comstat))
{
std::cout << std::endl
<< "errors code = 0x" << std::hex << errors << std::dec << std::endl;
std::cout << "comstat fCtsHole = " << comstat.fCtsHold << std::endl;
std::cout << " fDsrHold = " << comstat.fDsrHold << std::endl;
std::cout << " fRlsdHold = " << comstat.fRlsdHold << std::endl;
std::cout << " fXoffHold = " << comstat.fXoffHold << std::endl;
std::cout << " fXoffSent = " << comstat.fXoffSent << std::endl;
std::cout << " fEof = " << comstat.fEof << std::endl;
std::cout << " fTxim = " << comstat.fTxim << std::endl;
std::cout << " InQue = " << comstat.cbInQue << std::endl;
std::cout << " OutQue = " << comstat.cbOutQue << std::endl;
}
else
{
dw = GetLastError();
std::cout << std::endl << "SetCommState error code = 0x" << std::hex << dw << std::dec << " = " << dw << std::endl;
}
}
CloseHandle(serialHandle);
std::cout << "Press Enter to continue..." << std::endl;
std::cin.get();
return 0;
}