-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
In .NET 6 and earlier, when System.IO.Ports.SerialPort.Read times out, it will throw a TimeoutException. In .NET 7, it will throw an IOException with an HResult of 0x800705B4 (1460: ERROR_TIMEOUT)
Reproduction Steps
Use the following code (substitute your own valid serial port values)
- VS2022 or .NET 7 SDK on Windows.
- Nuget Reference to
System.IO.Portsversion 7.0.0 (OK with eariler versions too) - Target
net7.0to see it fail, ornet6.0to see it work.
using System;
using System.IO;
using System.IO.Ports;
class Program
{
static void Main()
{
var serialPort = new SerialPort("COM42", 115200, Parity.None, 8, StopBits.One)
{
DtrEnable = false,
WriteTimeout = 500,
ReadTimeout = 500,
};
serialPort.Open();
var data = new byte[100];
try
{
var numRead = serialPort.Read(data, 0, data.Length);
Console.WriteLine("Read {0}", numRead);
}
catch (TimeoutException)
{
Console.WriteLine(".NET <=6 Timed out");
}
catch (IOException e)
{
Console.WriteLine(".NET 7: IO {0} HR:0x{1:X}", e.HResult & 0xFFFF, e.HResult);
}
}
}
Expected behavior
Output of .NET <=6 Timed out
Actual behavior
Output of .NET 7: IO 1460 HR:0x800705B4
Regression?
Yes. Worked for .NET 6 and 5.
You can easily test this but switching target framework. If you switch to .NET 6 you get the TimeoutException, if you switch to .NET 7 you get the IOException
Known Workarounds
Code catching the TimeoutException would have to catch the IOException check the HResult and handle it the same way.
Configuration
.net 7.0.101 Windows 11, Target X64 or x86 has the problem.
Debug or release doesn't seem to matter.
Ubuntu WSL2 with 7.0.101 does not have the problem.
Other information
This seems to be caused by changed in overlapped IO introduced in .net7
The difference can be observed here:
runtime/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.Windows.cs
Line 1505 in d099f07
| SerialStreamAsyncResult asyncResult = |
On .net6, the errorCode is 0, on .net7 it's 1460
The call stack invoking this function is very different on .net 6 and 7.
On .net7, AsyncFSCallback is getttiong called from PortableThreadPool and the error is set here:
runtime/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.IO.Windows.cs
Line 262 in d099f07
| errorCode = Interop.NtDll.RtlNtStatusToDosError((int)ntStatus); |
On .net6, it's getting called from _IOCompletionCallback.PerformIOCompletionCallback The stack stops there, and I didn't dig any deeper to see where it's getting its parameters from.