【发布时间】:2020-03-11 16:43:28
【问题描述】:
我正在用 C# 制作一个简单的 RS-232 测试应用程序。在 .NET SerialPort 遇到问题后,我决定直接调用 Win32 API。
我正在使用重叠 I/O。它有 70% 的时间都在工作,而其他时间入站数据没有写入缓冲区。
我的测试应用以这种方式打开 COM 端口:
_handle = CreateFile(
@"\\.\COM1",
FileAccess.ReadWrite,
FileShare.None,
IntPtr.Zero,
FileMode.Open,
EFileAttributes.Overlapped,
IntPtr.Zero
);
注意重叠的标志。
然后应用设置 COM 参数。 GetCommState,设置波特率,0 停止位,无奇偶校验,禁用所有流控制和 RTS 和 DTR(除波特之外的所有这些都是默认值),然后SetCommState。
接下来应用通过SetCommTimeouts设置超时,ReadIntervalTimeout是1ms,其他都是零。
然后应用程序将 DTR 通过 EscapeCommFunction 循环到 CLRDTR,然后进行 200 毫秒的睡眠,然后将 EscapeCommFunction 循环到 SETDTR。
所有这些都很好地设置了 COM 端口。当连接到 shell 时,I/O 工作得很好,因为消息很短。当消息具有任何显着长度时,例如一次 30 个字节,我会在 ReadFile 上遇到重叠 I/O 响应的问题。
我的 rx 代码如下。
问题 数据正确进入,重叠操作按预期完成,重叠操作的长度始终正确,但ioBuffer 30% 的时间未填充并保留全部为零。
似乎是pinvoke。虽然我在使用 .NET SerialPort 时遇到问题,但丢失这样的数据不是问题。
有人发现有问题吗?
DataReceivedArgs args = new DataReceivedArgs();
ManualResetEvent completionEvent = new ManualResetEvent(false);
NativeOverlapped nol = new NativeOverlapped();
nol.EventHandle = completionEvent.SafeWaitHandle.DangerousGetHandle();
int dontCare = 0;
try
{
for (; ; )
{
completionEvent.Reset();
uint bytesRead = 0;
nol.InternalHigh = IntPtr.Zero;
nol.InternalLow = IntPtr.Zero;
nol.OffsetHigh = 0;
nol.OffsetLow = 0;
byte[] ioBuffer = new byte[1024];
if (!ReadFile(_handle.DangerousGetHandle(), ioBuffer, ioBuffer.Length, out dontCare, ref nol))
{
int lastError = Marshal.GetLastWin32Error();
if (lastError != OperationInProgress)
{
if (lastError != ErrorInvalidHandle)
{
Win32Exception ex = new Win32Exception(lastError);
MessageBox.Show("ReadFile failed. Error: " + ex.Message);
}
break;
}
completionEvent.WaitOne();
// Have tried sleeping here to see if there is timing involved, no luck
if (!GetOverlappedResult(_handle.DangerousGetHandle(), ref nol, out bytesRead, true))
{
bytesRead = 0;
}
}
else
{
throw new IOException();
}
if (bytesRead > 0)
{
byte[] sizedBuffer = new byte[bytesRead];
Array.Copy(ioBuffer, 0, sizedBuffer, 0, bytesRead);
args.Data = sizedBuffer;
DataReceived?.Invoke(this, args);
}
}
}
catch (ThreadAbortException)
{
}
和pinvoke签名
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadFile(
IntPtr hFile,
[Out] byte[] lpBuffer,
int nNumberOfBytesToRead,
[Out] out int lpNumberOfBytesRead,
ref NativeOverlapped lpOverlapped
);
【问题讨论】:
-
为什么不解决您对 SerialPort 类的问题? docs.microsoft.com/en-us/dotnet/api/system.io.ports.serialport
-
SerialPort 有几个设计问题。您可以在网上搜索以了解它们是什么。我用一个非常简单的用例解决了几个众所周知的问题,即通知不可靠和 UI 线程死锁。
标签: c# serial-port pinvoke overlapped-io