Я делаю простое тестовое приложение RS-232 на С#. После проблем с .NET SerialPort
я решил напрямую вызывать Win32 API.
Я использую перекрывающийся ввод-вывод. Он работает 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
составляет 1 мс, все остальные равны нулю.
Затем приложение циклически переключает DTR с EscapeCommFunction
на CLRDTR
, затем на 200 мс засыпает и с EscapeCommFunction
на SETDTR
.
Все это хорошо настраивает COM-порт. При подключении к оболочке ввод-вывод работает отлично, потому что сообщения короткие. Когда сообщения имеют значительную длину, скажем, 30 байт за раз, я сталкиваюсь с проблемами с перекрывающимися ответами ввода-вывода на ReadFile
.
Мой код rx ниже.
Проблема Данные поступают правильно, перекрывающаяся операция завершается должным образом, длина перекрывающейся операции всегда верна, но ioBuffer
30% времени не заполняется и остается нулевой.
Кажется, это пинвок. Хотя у меня были проблемы с использованием .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)
{
}
И пинвок подпись
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadFile(
IntPtr hFile,
[Out] byte[] lpBuffer,
int nNumberOfBytesToRead,
[Out] out int lpNumberOfBytesRead,
ref NativeOverlapped lpOverlapped
);