From d08819797a823bea5ab7544881e670b1599af0a6 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 16 Apr 2026 11:53:16 +0200 Subject: [PATCH 1/2] Refactor COM port read error handling Removed error handling for GetOverlappedResult and added a note about ov->hEvent. --- src/serialport_win.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/serialport_win.cpp b/src/serialport_win.cpp index 7e79216a..511b81d8 100644 --- a/src/serialport_win.cpp +++ b/src/serialport_win.cpp @@ -442,12 +442,6 @@ void __stdcall ReadIOCompletion(DWORD errorCode, DWORD bytesTransferred, OVERLAP } DWORD lastError; - if (!GetOverlappedResult(int2handle(baton->fd), ov, &bytesTransferred, TRUE)) { - lastError = GetLastError(); - ErrorCodeToString("Reading from COM port (GetOverlappedResult)", lastError, baton->errorString); - baton->complete = true; - return; - } if (bytesTransferred) { baton->bytesToRead -= bytesTransferred; baton->bytesRead += bytesTransferred; @@ -455,7 +449,7 @@ void __stdcall ReadIOCompletion(DWORD errorCode, DWORD bytesTransferred, OVERLAP } if (!baton->bytesToRead) { baton->complete = true; - CloseHandle(ov->hEvent); + // Note: ov->hEvent is a baton pointer, not a Windows handle — do not CloseHandle return; } From fcf05f3f01cd72033c60cd488f59b41e49f6004a Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sat, 18 Apr 2026 13:06:28 +0200 Subject: [PATCH 2/2] Refactor WriteIOCompletion and WriteThread error handling Updated error handling and comments in WriteIOCompletion and WriteThread functions to clarify usage of bytesTransferred and GetOverlappedResult. --- src/serialport_win.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/serialport_win.cpp b/src/serialport_win.cpp index 511b81d8..7f8bac6f 100644 --- a/src/serialport_win.cpp +++ b/src/serialport_win.cpp @@ -326,18 +326,28 @@ bool IsClosingHandle(int fd) { void __stdcall WriteIOCompletion(DWORD errorCode, DWORD bytesTransferred, OVERLAPPED* ov) { WriteBaton* baton = static_cast(ov->hEvent); - DWORD bytesWritten; - if (!GetOverlappedResult(int2handle(baton->fd), ov, &bytesWritten, TRUE)) { - errorCode = GetLastError(); - ErrorCodeToString("Writing to COM port (GetOverlappedResult)", errorCode, baton->errorString); + + if (errorCode) { + ErrorCodeToString("Writing to COM port (WriteIOCompletion)", errorCode, baton->errorString); baton->complete = true; return; } - if (bytesWritten) { - baton->offset += bytesWritten; + + // bytesTransferred is already provided by the APC completion callback. + // Do NOT call GetOverlappedResult here — MSDN explicitly states: + // "Do not use GetOverlappedResult for I/O operations that use + // ReadFileEx or WriteFileEx completion routines." + // The overlapped's hEvent holds a WriteBaton pointer (not a Windows event + // handle), so GetOverlappedResult fails with ERROR_INVALID_HANDLE on + // drivers that inspect hEvent (e.g. usbser.sys used by USB serial chips). + if (bytesTransferred) { + baton->offset += bytesTransferred; if (baton->offset >= baton->bufferLength) { baton->complete = true; } + } else { + // Zero-byte completion with no error — avoid hanging in WriteThread. + baton->complete = true; } } @@ -441,6 +451,14 @@ void __stdcall ReadIOCompletion(DWORD errorCode, DWORD bytesTransferred, OVERLAP return; } + // bytesTransferred is already provided by the APC completion callback. + // Do NOT call GetOverlappedResult here — MSDN explicitly states: + // "Do not use GetOverlappedResult for I/O operations that use + // ReadFileEx or WriteFileEx completion routines." + // The overlapped's hEvent holds a ReadBaton pointer (not a Windows event + // handle), so GetOverlappedResult fails with ERROR_INVALID_HANDLE on + // drivers that inspect hEvent (e.g. usbser.sys used by ESP32 native USB). + DWORD lastError; if (bytesTransferred) { baton->bytesToRead -= bytesTransferred;