Skip to content

Instantly share code, notes, and snippets.

@dmh2000
Created May 24, 2019 00:44
Show Gist options
  • Save dmh2000/c776e6c5e27664008533c477427a2349 to your computer and use it in GitHub Desktop.
Save dmh2000/c776e6c5e27664008533c477427a2349 to your computer and use it in GitHub Desktop.
this demonstrates that ReadIOCompletion::GetOverlappedResult can return 0 bytes even though it appears it shouldn't
/*
build with visual studio 2019 (2017/15 should work too), x64 debug build
*/
#include <Windows.h>
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <cassert>
struct Baton {
HANDLE fd = INVALID_HANDLE_VALUE;
char *buffer = nullptr;
DWORD bufferLength = 0;
DWORD bytesRead = 0;
DWORD bytesToRead = 0;
DWORD offset = 0;
void *hThread = nullptr;
bool complete = false;
};
HANDLE open(const char* port) {
DCB dcb;
COMMTIMEOUTS tmo;
HANDLE fd;
BOOL b;
fd = CreateFile(port,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);
if (fd == INVALID_HANDLE_VALUE) {
return fd;
}
// setup DCB
memset(&dcb,0,sizeof(dcb));
dcb.DCBlength = sizeof(dcb);
GetCommState(fd,&dcb);
dcb.Parity = NOPARITY;
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.fOutxDsrFlow = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fBinary = TRUE;
dcb.BaudRate = 9600;
b = SetCommState(fd,&dcb);
if (b == FALSE) {
printf("%s:%d : %d\n",__FILE__,__LINE__,GetLastError());
CloseHandle(fd);
return INVALID_HANDLE_VALUE;
}
// set commtimeouts to block
memset(&tmo,0,sizeof(tmo));
tmo.ReadIntervalTimeout = 0;
tmo.ReadTotalTimeoutMultiplier = 0;
tmo.ReadTotalTimeoutConstant = 0;
tmo.WriteTotalTimeoutConstant = 0;
tmo.WriteTotalTimeoutMultiplier = 0;
b = SetCommTimeouts(fd,&tmo);
if (b == FALSE) {
printf("%s:%d : %d\n", __FILE__, __LINE__, GetLastError());
CloseHandle(fd);
return INVALID_HANDLE_VALUE;
}
// Remove garbage data in RX/TX queues
PurgeComm(fd, PURGE_RXCLEAR);
PurgeComm(fd, PURGE_TXCLEAR);
return fd;
}
void __stdcall ReadIOCompletion(DWORD errorCode, DWORD bytesTransferred, OVERLAPPED* ov) {
Baton* baton = static_cast<Baton*>(ov->hEvent);
if (errorCode) {
printf("%s:%d : %d\n", __FILE__, __LINE__, errorCode);
baton->complete = true;
return;
}
if (!GetOverlappedResult(baton->fd, ov, &bytesTransferred, TRUE)) {
printf("%s:%d : %d\n", __FILE__, __LINE__, GetLastError());
baton->complete = true;
return;
}
if (bytesTransferred) {
baton->bytesToRead -= bytesTransferred;
baton->bytesRead += bytesTransferred;
baton->offset += bytesTransferred;
}
// ====================================
// BYTES TRANFERRED HERE CAN BE ZERO!
// ====================================
if (bytesTransferred == 0) {
printf("%s:%d : %d\n", __FILE__, __LINE__, GetLastError());
//baton->complete = true;
//return;
}
// ReadFileEx and GetOverlappedResult retrieved only 1 byte. Read any additional data in the input
// buffer. Set the timeout to MAXDWORD in order to disable timeouts, so the read operation will
// return immediately no matter how much data is available.
COMMTIMEOUTS commTimeouts = {};
commTimeouts.ReadIntervalTimeout = MAXDWORD;
if (!SetCommTimeouts(baton->fd, &commTimeouts)) {
printf("%s:%d : %d\n", __FILE__, __LINE__, GetLastError());
baton->complete = true;
return;
}
// Store additional data after whatever data has already been read.
char* offsetPtr = baton->buffer + baton->offset;
// ReadFile, unlike ReadFileEx, needs an event in the overlapped structure.
memset(ov, 0, sizeof(OVERLAPPED));
ov->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
assert(ov->hEvent != nullptr);
if (!ReadFile(baton->fd, offsetPtr, baton->bytesToRead, &bytesTransferred, ov)) {
errorCode = GetLastError();
if (errorCode != ERROR_IO_PENDING) {
printf("%s:%d : %d\n", __FILE__, __LINE__, GetLastError());
baton->complete = true;
CloseHandle(ov->hEvent);
return;
}
if (!GetOverlappedResult(baton->fd, ov, &bytesTransferred, TRUE)) {
printf("%s:%d : %d\n", __FILE__, __LINE__, GetLastError());
baton->complete = true;
CloseHandle(ov->hEvent);
return;
}
}
CloseHandle(ov->hEvent);
baton->bytesToRead -= bytesTransferred;
baton->bytesRead += bytesTransferred;
baton->complete = true;
}
DWORD __stdcall ReadThread(LPVOID param) {
Baton* baton = static_cast<Baton*>(param);
DWORD lastError;
BOOL b;
OVERLAPPED* ov = new OVERLAPPED;
memset(ov, 0, sizeof(OVERLAPPED));
ov->hEvent = static_cast<void*>(baton);
while (!baton->complete) {
// Reset the read timeout to 0, so that it will block until more data arrives.
COMMTIMEOUTS commTimeouts = {};
commTimeouts.ReadIntervalTimeout = 0;
if (!SetCommTimeouts(baton->fd, &commTimeouts)) {
lastError = GetLastError();
printf("%s:%d : %d\n", __FILE__, __LINE__, GetLastError());
break;
}
// ReadFileEx doesn't use overlapped's hEvent, so it is reserved for user data.
ov->hEvent = static_cast<HANDLE>(baton);
char* offsetPtr = baton->buffer + baton->offset;
// ReadFileEx requires calling GetLastError even upon success. Clear the error beforehand.
SetLastError(0);
// Only read 1 byte, so that the callback will be triggered once any data arrives.
b = ReadFileEx(baton->fd, offsetPtr, 1, ov, ReadIOCompletion);
if (b == FALSE) {
printf("%s:%d : %d\n", __FILE__, __LINE__, GetLastError());
}
// Error codes when call is successful, such as ERROR_MORE_DATA.
lastError = GetLastError();
if (lastError != ERROR_SUCCESS) {
break;
}
// IOCompletion routine is only called once this thread is in an alertable wait state.
SleepEx(INFINITE, TRUE);
}
delete ov;
// Signal the main thread to run the callback.
ExitThread(0);
}
DWORD read(HANDLE fd,char *buffer, DWORD len)
{
Baton *baton = new Baton();
baton->fd = fd;
baton->buffer = buffer;
baton->bytesToRead = len;
baton->bytesRead = 0;
baton->complete = false;
baton->offset = 0;
baton->hThread = CreateThread(NULL, 0, ReadThread, baton,0,nullptr);
// wait for asyn io to complete
while (!baton->complete) {
Sleep(100);
}
return baton->bytesRead;
}
int main()
{
HANDLE fd;
fd = open("COM4");
if (fd == INVALID_HANDLE_VALUE) {
printf("%s:%d : %d\n", __FILE__, __LINE__, GetLastError());
return 1;
}
DWORD count;
char buffer[1024];
int lf;
lf = 0;
for (;;) {
count = read(fd,buffer,sizeof(buffer));
for (DWORD i = 0; i < count; ++i) {
printf("%02x ",(uint8_t)buffer[i]);
++lf;
if (lf > 30) {
lf = 0;
printf("\n");
}
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment