Skip to content

Commit

Permalink
SerialPortStream: Don't close the port on ERROR_HANDLE_EOF
Browse files Browse the repository at this point in the history
It is observed that WinError.ERROR_HANDLE_EOF can occur in normal circumstances and should not result in the serial port stream closing down.

Issue: #142
Issue: DOTNET-975
  • Loading branch information
jcurl committed Mar 4, 2024
1 parent 81aff4d commit 956636d
Showing 1 changed file with 72 additions and 42 deletions.
114 changes: 72 additions & 42 deletions code/Native/Windows/CommOverlappedIo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © Jason Curl 2012-2023
// Copyright © Jason Curl 2012-2024
// Sources at https://github.com/jcurl/SerialPortStream
// Licensed under the Microsoft Public License (Ms-PL)

Expand Down Expand Up @@ -457,20 +457,7 @@ private void OverlappedIoThreadMainLoop()
} else if (whandles[ev] == m_ReadEvent) {
result = Kernel32.GetOverlappedResult(m_ComPortHandle, ref readOverlapped, out bytes, true);
if (!result) {
int win32Error = Marshal.GetLastWin32Error();
int hr = Marshal.GetHRForLastWin32Error();
// Should never get ERROR_IO_PENDING, as this method is only called when the event is triggered.
if (win32Error != WinError.ERROR_OPERATION_ABORTED || bytes > 0) {
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Error))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Error,
"{0}: SerialThread: Overlapped ReadFile() error {1} bytes {2}", m_Name, win32Error, bytes);
Marshal.ThrowExceptionForHR(hr);
} else {
// ERROR_OPERATION_ABORTED may be caused by CancelIo or PurgeComm
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Verbose))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Verbose,
"{0}: SerialThread: Overlapped ReadFile() error {1} bytes {2}", m_Name, win32Error, bytes);
}
ProcessReadEventError(bytes);
} else {
ProcessReadEvent(bytes);
}
Expand All @@ -481,19 +468,7 @@ private void OverlappedIoThreadMainLoop()
} else if (whandles[ev] == m_WriteEvent) {
result = Kernel32.GetOverlappedResult(m_ComPortHandle, ref writeOverlapped, out bytes, true);
if (!result) {
int win32Error = Marshal.GetLastWin32Error();
int hr = Marshal.GetHRForLastWin32Error();
// Should never get ERROR_IO_PENDING, as this method is only called when the event is triggered.
if (win32Error != WinError.ERROR_OPERATION_ABORTED || bytes > 0) {
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Error))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Error,
"{0}: SerialThread: Overlapped WriteFile() error {1} bytes {2}", m_Name, win32Error, bytes);
Marshal.ThrowExceptionForHR(hr);
} else {
// ERROR_OPERATION_ABORTED may be caused by CancelIo or PurgeComm
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Verbose))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Verbose, "{0}: SerialThread: Overlapped WriteFile() error {1} bytes {2}", m_Name, win32Error, bytes);
}
ProcessWriteEventError(bytes);
} else {
ProcessWriteEvent(bytes);
}
Expand All @@ -508,20 +483,7 @@ private void OverlappedIoThreadMainLoop()
m_PurgePending = true;
result = Kernel32.PurgeComm(m_ComPortHandle,
Kernel32.PurgeFlags.PURGE_TXABORT | Kernel32.PurgeFlags.PURGE_TXCLEAR);
if (!result) {
int win32Error = Marshal.GetLastWin32Error();
int hr = Marshal.GetHRForLastWin32Error();
if (win32Error != WinError.ERROR_OPERATION_ABORTED) {
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Error))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Error,
"{0}: SerialThread: PurgeComm() error {1}", m_Name, win32Error);
Marshal.ThrowExceptionForHR(hr);
} else {
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Verbose))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Verbose,
"{0}: SerialThread: PurgeComm() error {1}", m_Name, win32Error);
}
}
if (!result) ProcessPurgeCommError();
} else {
lock (m_Buffer.WriteLock) {
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Verbose))
Expand Down Expand Up @@ -737,6 +699,33 @@ private void ProcessReadEvent(uint bytes)
}
}

private void ProcessReadEventError(uint bytes)
{
int win32Error = Marshal.GetLastWin32Error();
int hr = Marshal.GetHRForLastWin32Error();

// Should never get ERROR_IO_PENDING, as this method is only called when the event is triggered.
if (bytes == 0) {
switch (win32Error) {
case WinError.ERROR_OPERATION_ABORTED:
// ERROR_OPERATION_ABORTED may be caused by CancelIo or PurgeComm
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Verbose))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Verbose,
"{0}: SerialThread: Overlapped ReadFile() error {1} bytes {2}", m_Name, win32Error, bytes);
return;
case WinError.ERROR_HANDLE_EOF:
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Information))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Information,
"{0}: SerialThread: Overlapped ReadFile() error {1} bytes {2}", m_Name, win32Error, bytes);
return;
}
}
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Error))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Error,
"{0}: SerialThread: Overlapped ReadFile() error {1} bytes {2}", m_Name, win32Error, bytes);
Marshal.ThrowExceptionForHR(hr);
}

/// <summary>
/// Check if we should WriteFile() and update buffers if serial data is immediately cached by driver.
/// </summary>
Expand Down Expand Up @@ -826,6 +815,47 @@ private void ProcessWriteEvent(uint bytes)
}
}

private void ProcessWriteEventError(uint bytes)
{
int win32Error = Marshal.GetLastWin32Error();
int hr = Marshal.GetHRForLastWin32Error();

if (bytes == 0) {
switch (win32Error) {
case WinError.ERROR_OPERATION_ABORTED:
// ERROR_OPERATION_ABORTED may be caused by CancelIo or PurgeComm
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Verbose))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Verbose,
"{0}: SerialThread: Overlapped WriteFile() error {1} bytes {2}", m_Name, win32Error, bytes);
return;
}
}
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Error))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Error,
"{0}: SerialThread: Overlapped WriteFile() error {1} bytes {2}", m_Name, win32Error, bytes);
Marshal.ThrowExceptionForHR(hr);
}

private void ProcessPurgeCommError()
{
int win32Error = Marshal.GetLastWin32Error();
int hr = Marshal.GetHRForLastWin32Error();

switch (win32Error) {
case WinError.ERROR_OPERATION_ABORTED:
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Verbose))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Verbose,
"{0}: SerialThread: PurgeComm() error {1}", m_Name, win32Error);
return;
default:
if (m_Log.ShouldTrace(System.Diagnostics.TraceEventType.Error))
m_Log.TraceEvent(System.Diagnostics.TraceEventType.Error,
"{0}: SerialThread: PurgeComm() error {1}", m_Name, win32Error);
Marshal.ThrowExceptionForHR(hr);
break;
}
}

#region Event Handling
public event EventHandler<CommEventArgs> CommEvent;

Expand Down

0 comments on commit 956636d

Please sign in to comment.