More clean-up

This commit is contained in:
Benjamin Höglinger-Stelzer 2024-07-13 14:59:01 +02:00
parent fa8e74a822
commit 286994c2b6
3 changed files with 165 additions and 51 deletions

View File

@ -1,4 +1,5 @@
CreateFile CreateFile
FILE_ACCESS_RIGHTS
GetHandleInformation GetHandleInformation
SetCommState SetCommState
SetCommTimeouts SetCommTimeouts

View File

@ -34,5 +34,6 @@
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.123"> <PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.123">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Windows.SDK.Win32Metadata" Version="61.0.15-preview" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,12 +1,16 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using Windows.Win32; using Windows.Win32;
using Windows.Win32.Devices.Communication; using Windows.Win32.Devices.Communication;
using Windows.Win32.Foundation; using Windows.Win32.Foundation;
using Windows.Win32.Storage.FileSystem; using Windows.Win32.Storage.FileSystem;
using Microsoft.Win32.SafeHandles; using Microsoft.Win32.SafeHandles;
using Nefarius.Peripherals.SerialPort.Win32PInvoke; using Nefarius.Peripherals.SerialPort.Win32PInvoke;
namespace Nefarius.Peripherals.SerialPort; namespace Nefarius.Peripherals.SerialPort;
@ -14,6 +18,7 @@ namespace Nefarius.Peripherals.SerialPort;
/// <summary> /// <summary>
/// Wrapper class around a serial (COM, RS-232) port. /// Wrapper class around a serial (COM, RS-232) port.
/// </summary> /// </summary>
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public partial class SerialPort : IDisposable public partial class SerialPort : IDisposable
{ {
private readonly ManualResetEvent _writeEvent = new(false); private readonly ManualResetEvent _writeEvent = new(false);
@ -64,18 +69,25 @@ public partial class SerialPort : IDisposable
/// <returns>false if the port could not be opened</returns> /// <returns>false if the port could not be opened</returns>
public bool Open() public bool Open()
{ {
var portDcb = new DCB(); DCB portDcb = new();
var commTimeouts = new COMMTIMEOUTS(); COMMTIMEOUTS commTimeouts = new();
if (_online) return false; if (_online)
{
return false;
}
_hPort = PInvoke.CreateFile(PortName, _hPort = PInvoke.CreateFile(PortName,
FILE_ACCESS_FLAGS.FILE_GENERIC_READ | FILE_ACCESS_FLAGS.FILE_GENERIC_WRITE, 0, (uint)(FILE_ACCESS_RIGHTS.FILE_GENERIC_READ | FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE), 0,
null, FILE_CREATION_DISPOSITION.OPEN_EXISTING, FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_OVERLAPPED, null); null, FILE_CREATION_DISPOSITION.OPEN_EXISTING, FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_OVERLAPPED, null);
if (_hPort.IsInvalid) if (_hPort.IsInvalid)
{ {
if (Marshal.GetLastWin32Error() == (int)WIN32_ERROR.ERROR_ACCESS_DENIED) return false; if (Marshal.GetLastWin32Error() == (int)WIN32_ERROR.ERROR_ACCESS_DENIED)
{
return false;
}
throw new CommPortException("Port Open Failure"); throw new CommPortException("Port Open Failure");
} }
@ -92,20 +104,28 @@ public partial class SerialPort : IDisposable
portDcb.ByteSize = (byte)DataBits; portDcb.ByteSize = (byte)DataBits;
portDcb.Parity = (DCB_PARITY)Parity; portDcb.Parity = (DCB_PARITY)Parity;
portDcb.StopBits = (DCB_STOP_BITS)StopBits; portDcb.StopBits = (DCB_STOP_BITS)StopBits;
portDcb.XoffChar = (CHAR)(byte)XoffChar; portDcb.XoffChar = new CHAR((sbyte)XoffChar);
portDcb.XonChar = (CHAR)(byte)XonChar; portDcb.XonChar = new CHAR((sbyte)XonChar);
portDcb.XoffLim = (ushort)RxHighWater; portDcb.XoffLim = (ushort)RxHighWater;
portDcb.XonLim = (ushort)RxLowWater; portDcb.XonLim = (ushort)RxLowWater;
if (RxQueue != 0 || TxQueue != 0) if (RxQueue != 0 || TxQueue != 0)
{
if (!PInvoke.SetupComm(_hPort, (uint)RxQueue, (uint)TxQueue)) if (!PInvoke.SetupComm(_hPort, (uint)RxQueue, (uint)TxQueue))
{
ThrowException("Bad queue settings"); ThrowException("Bad queue settings");
}
}
if (!PInvoke.SetCommState(_hPort, portDcb)) if (!PInvoke.SetCommState(_hPort, portDcb))
{
ThrowException("Bad com settings"); ThrowException("Bad com settings");
}
if (!PInvoke.SetCommTimeouts(_hPort, commTimeouts)) if (!PInvoke.SetCommTimeouts(_hPort, commTimeouts))
{
ThrowException("Bad timeout settings"); ThrowException("Bad timeout settings");
}
_stateBrk = 0; _stateBrk = 0;
switch (UseDtr) switch (UseDtr)
@ -135,12 +155,9 @@ public partial class SerialPort : IDisposable
_rxException = null; _rxException = null;
_rxExceptionReported = false; _rxExceptionReported = false;
// TODO: utilize Task Parallel Library here
_rxThread = new Thread(ReceiveThread) _rxThread = new Thread(ReceiveThread)
{ {
Name = "CommBaseRx", Name = "CommBaseRx", Priority = ThreadPriority.AboveNormal, IsBackground = true
Priority = ThreadPriority.AboveNormal,
IsBackground = true
}; };
_rxThread.Start(); _rxThread.Start();
@ -211,14 +228,22 @@ public partial class SerialPort : IDisposable
/// <param name="reason">Description of fault</param> /// <param name="reason">Description of fault</param>
protected void ThrowException(string reason) protected void ThrowException(string reason)
{ {
if (Thread.CurrentThread == _rxThread) throw new CommPortException(reason); if (Thread.CurrentThread == _rxThread)
{
throw new CommPortException(reason);
}
if (_online) if (_online)
{ {
BeforeClose(true); BeforeClose(true);
InternalClose(); InternalClose();
} }
if (_rxException == null) throw new CommPortException(reason); if (_rxException == null)
{
throw new CommPortException(reason);
}
throw new CommPortException(_rxException); throw new CommPortException(_rxException);
} }
@ -228,7 +253,6 @@ public partial class SerialPort : IDisposable
/// <param name="toSend">Array of bytes to be sent</param> /// <param name="toSend">Array of bytes to be sent</param>
public unsafe void Write(byte[] toSend) public unsafe void Write(byte[] toSend)
{ {
uint sent;
CheckOnline(); CheckOnline();
CheckResult(); CheckResult();
_writeCount = toSend.GetLength(0); _writeCount = toSend.GetLength(0);
@ -236,6 +260,7 @@ public partial class SerialPort : IDisposable
fixed (byte* ptr = toSend) fixed (byte* ptr = toSend)
fixed (NativeOverlapped* ptrOl = &_ptrUwo) fixed (NativeOverlapped* ptrOl = &_ptrUwo)
{ {
uint sent;
if (PInvoke.WriteFile(_hPort, ptr, (uint)_writeCount, &sent, ptrOl)) if (PInvoke.WriteFile(_hPort, ptr, (uint)_writeCount, &sent, ptrOl))
{ {
_writeCount -= (int)sent; _writeCount -= (int)sent;
@ -243,7 +268,9 @@ public partial class SerialPort : IDisposable
else else
{ {
if (Marshal.GetLastWin32Error() != (int)WIN32_ERROR.ERROR_IO_PENDING) if (Marshal.GetLastWin32Error() != (int)WIN32_ERROR.ERROR_IO_PENDING)
{
ThrowException("Unexpected failure"); ThrowException("Unexpected failure");
}
} }
} }
} }
@ -263,7 +290,7 @@ public partial class SerialPort : IDisposable
/// <param name="toSend">Byte to be sent</param> /// <param name="toSend">Byte to be sent</param>
public void Write(byte toSend) public void Write(byte toSend)
{ {
var b = new byte[1]; byte[] b = new byte[1];
b[0] = toSend; b[0] = toSend;
Write(b); Write(b);
} }
@ -288,15 +315,25 @@ public partial class SerialPort : IDisposable
private void CheckResult() private void CheckResult()
{ {
if (_writeCount <= 0) return; if (_writeCount <= 0)
if (PInvoke.GetOverlappedResult(_hPort, _ptrUwo, out var sent, _checkSends)) {
return;
}
if (PInvoke.GetOverlappedResult(_hPort, _ptrUwo, out uint sent, _checkSends))
{ {
_writeCount -= (int)sent; _writeCount -= (int)sent;
if (_writeCount != 0) ThrowException("Send Timeout"); if (_writeCount != 0)
{
ThrowException("Send Timeout");
}
} }
else else
{ {
if (Marshal.GetLastWin32Error() != (int)WIN32_ERROR.ERROR_IO_PENDING) ThrowException("Unexpected failure"); if (Marshal.GetLastWin32Error() != (int)WIN32_ERROR.ERROR_IO_PENDING)
{
ThrowException("Unexpected failure");
}
} }
} }
@ -308,7 +345,10 @@ public partial class SerialPort : IDisposable
public void SendImmediate(byte tosend) public void SendImmediate(byte tosend)
{ {
CheckOnline(); CheckOnline();
if (!Win32Com.TransmitCommChar(_hPort.DangerousGetHandle(), tosend)) ThrowException("Transmission failure"); if (!Win32Com.TransmitCommChar(_hPort.DangerousGetHandle(), tosend))
{
ThrowException("Transmission failure");
}
} }
/// <summary> /// <summary>
@ -318,7 +358,11 @@ public partial class SerialPort : IDisposable
protected ModemStatus GetModemStatus() protected ModemStatus GetModemStatus()
{ {
CheckOnline(); CheckOnline();
if (!Win32Com.GetCommModemStatus(_hPort.DangerousGetHandle(), out var f)) ThrowException("Unexpected failure"); if (!Win32Com.GetCommModemStatus(_hPort.DangerousGetHandle(), out uint f))
{
ThrowException("Unexpected failure");
}
return new ModemStatus((MODEM_STATUS_FLAGS)f); return new ModemStatus((MODEM_STATUS_FLAGS)f);
} }
@ -329,15 +373,19 @@ public partial class SerialPort : IDisposable
protected unsafe QueueStatus GetQueueStatus() protected unsafe QueueStatus GetQueueStatus()
{ {
COMSTAT cs; COMSTAT cs;
var cp = new COMMPROP(); COMMPROP cp = new();
CLEAR_COMM_ERROR_FLAGS er; CLEAR_COMM_ERROR_FLAGS er;
CheckOnline(); CheckOnline();
if (!PInvoke.ClearCommError(_hPort, &er, &cs)) if (!PInvoke.ClearCommError(_hPort, &er, &cs))
{
ThrowException("Unexpected failure"); ThrowException("Unexpected failure");
}
if (!PInvoke.GetCommProperties(_hPort, ref cp)) if (!PInvoke.GetCommProperties(_hPort, &cp))
{
ThrowException("Unexpected failure"); ThrowException("Unexpected failure");
}
return new QueueStatus(cs._bitfield, cs.cbInQue, cs.cbOutQue, cp.dwCurrentRxQueue, cp.dwCurrentTxQueue); return new QueueStatus(cs._bitfield, cs.cbInQue, cs.cbOutQue, cp.dwCurrentRxQueue, cp.dwCurrentTxQueue);
} }
@ -412,13 +460,10 @@ public partial class SerialPort : IDisposable
private unsafe void ReceiveThread() private unsafe void ReceiveThread()
{ {
var buf = stackalloc byte[1]; byte* buf = stackalloc byte[1];
var sg = new AutoResetEvent(false); AutoResetEvent sg = new(false);
var ov = new NativeOverlapped NativeOverlapped ov = new() { EventHandle = sg.SafeWaitHandle.DangerousGetHandle() };
{
EventHandle = sg.SafeWaitHandle.DangerousGetHandle()
};
COMM_EVENT_MASK eventMask = 0; COMM_EVENT_MASK eventMask = 0;
@ -431,14 +476,20 @@ public partial class SerialPort : IDisposable
COMM_EVENT_MASK.EV_DSR COMM_EVENT_MASK.EV_DSR
| COMM_EVENT_MASK.EV_BREAK | COMM_EVENT_MASK.EV_RLSD | COMM_EVENT_MASK.EV_RING | | COMM_EVENT_MASK.EV_BREAK | COMM_EVENT_MASK.EV_RLSD | COMM_EVENT_MASK.EV_RING |
COMM_EVENT_MASK.EV_ERR)) COMM_EVENT_MASK.EV_ERR))
{
throw new CommPortException("IO Error [001]"); throw new CommPortException("IO Error [001]");
}
if (!PInvoke.WaitCommEvent(_hPort, ref eventMask, &ov)) if (!PInvoke.WaitCommEvent(_hPort, ref eventMask, &ov))
{ {
if (Marshal.GetLastWin32Error() == (int)WIN32_ERROR.ERROR_IO_PENDING) if (Marshal.GetLastWin32Error() == (int)WIN32_ERROR.ERROR_IO_PENDING)
{
sg.WaitOne(); sg.WaitOne();
}
else else
{
throw new CommPortException("IO Error [002]"); throw new CommPortException("IO Error [002]");
}
} }
if ((eventMask & COMM_EVENT_MASK.EV_ERR) != 0) if ((eventMask & COMM_EVENT_MASK.EV_ERR) != 0)
@ -446,13 +497,37 @@ public partial class SerialPort : IDisposable
CLEAR_COMM_ERROR_FLAGS errs; CLEAR_COMM_ERROR_FLAGS errs;
if (PInvoke.ClearCommError(_hPort, &errs, null)) if (PInvoke.ClearCommError(_hPort, &errs, null))
{ {
var s = new StringBuilder("UART Error: ", 40); StringBuilder s = new("UART Error: ", 40);
if (((uint)errs & Win32Com.CE_FRAME) != 0) s = s.Append("Framing,"); if (((uint)errs & Win32Com.CE_FRAME) != 0)
if (((uint)errs & Win32Com.CE_IOE) != 0) s = s.Append("IO,"); {
if (((uint)errs & Win32Com.CE_OVERRUN) != 0) s = s.Append("Overrun,"); s = s.Append("Framing,");
if (((uint)errs & Win32Com.CE_RXOVER) != 0) s = s.Append("Receive Overflow,"); }
if (((uint)errs & Win32Com.CE_RXPARITY) != 0) s = s.Append("Parity,");
if (((uint)errs & Win32Com.CE_TXFULL) != 0) s = s.Append("Transmit Overflow,"); if (((uint)errs & Win32Com.CE_IOE) != 0)
{
s = s.Append("IO,");
}
if (((uint)errs & Win32Com.CE_OVERRUN) != 0)
{
s = s.Append("Overrun,");
}
if (((uint)errs & Win32Com.CE_RXOVER) != 0)
{
s = s.Append("Receive Overflow,");
}
if (((uint)errs & Win32Com.CE_RXPARITY) != 0)
{
s = s.Append("Parity,");
}
if (((uint)errs & Win32Com.CE_TXFULL) != 0)
{
s = s.Append("Transmit Overflow,");
}
s.Length -= 1; s.Length -= 1;
throw new CommPortException(s.ToString()); throw new CommPortException(s.ToString());
} }
@ -462,15 +537,15 @@ public partial class SerialPort : IDisposable
if ((eventMask & COMM_EVENT_MASK.EV_RXCHAR) != 0) if ((eventMask & COMM_EVENT_MASK.EV_RXCHAR) != 0)
{ {
uint gotbytes; uint gotBytes;
do do
{ {
if (!PInvoke.ReadFile(_hPort, buf, 1, &gotbytes, &ov)) if (!PInvoke.ReadFile(_hPort, buf, 1, &gotBytes, &ov))
{ {
if (Marshal.GetLastWin32Error() == (int)WIN32_ERROR.ERROR_IO_PENDING) if (Marshal.GetLastWin32Error() == (int)WIN32_ERROR.ERROR_IO_PENDING)
{ {
Win32Com.CancelIo(_hPort.DangerousGetHandle()); Win32Com.CancelIo(_hPort.DangerousGetHandle());
gotbytes = 0; gotBytes = 0;
} }
else else
{ {
@ -478,30 +553,59 @@ public partial class SerialPort : IDisposable
} }
} }
if (gotbytes == 1) OnRxChar(buf[0]); if (gotBytes == 1)
} while (gotbytes > 0); {
OnRxChar(buf[0]);
}
} while (gotBytes > 0);
} }
if ((eventMask & COMM_EVENT_MASK.EV_TXEMPTY) != 0) OnTxDone(); if ((eventMask & COMM_EVENT_MASK.EV_TXEMPTY) != 0)
if ((eventMask & COMM_EVENT_MASK.EV_BREAK) != 0) OnBreak(); {
OnTxDone();
}
if ((eventMask & COMM_EVENT_MASK.EV_BREAK) != 0)
{
OnBreak();
}
uint i = 0; uint i = 0;
if ((eventMask & COMM_EVENT_MASK.EV_CTS) != 0) i |= Win32Com.MS_CTS_ON; if ((eventMask & COMM_EVENT_MASK.EV_CTS) != 0)
if ((eventMask & COMM_EVENT_MASK.EV_DSR) != 0) i |= Win32Com.MS_DSR_ON; {
if ((eventMask & COMM_EVENT_MASK.EV_RLSD) != 0) i |= Win32Com.MS_RLSD_ON; i |= Win32Com.MS_CTS_ON;
if ((eventMask & COMM_EVENT_MASK.EV_RING) != 0) i |= Win32Com.MS_RING_ON; }
if ((eventMask & COMM_EVENT_MASK.EV_DSR) != 0)
{
i |= Win32Com.MS_DSR_ON;
}
if ((eventMask & COMM_EVENT_MASK.EV_RLSD) != 0)
{
i |= Win32Com.MS_RLSD_ON;
}
if ((eventMask & COMM_EVENT_MASK.EV_RING) != 0)
{
i |= Win32Com.MS_RING_ON;
}
if (i != 0) if (i != 0)
{ {
uint f; uint f;
if (!Win32Com.GetCommModemStatus(_hPort.DangerousGetHandle(), out f)) if (!Win32Com.GetCommModemStatus(_hPort.DangerousGetHandle(), out f))
{
throw new CommPortException("IO Error [005]"); throw new CommPortException("IO Error [005]");
}
OnStatusChange(new ModemStatus((MODEM_STATUS_FLAGS)i), new ModemStatus((MODEM_STATUS_FLAGS)f)); OnStatusChange(new ModemStatus((MODEM_STATUS_FLAGS)i), new ModemStatus((MODEM_STATUS_FLAGS)f));
} }
} }
} }
catch (Exception e) catch (Exception e)
{ {
if (!(e is ThreadAbortException)) if (e is not ThreadAbortException)
{ {
_rxException = e; _rxException = e;
OnRxException(e); OnRxException(e);
@ -519,16 +623,24 @@ public partial class SerialPort : IDisposable
if (_online) if (_online)
{ {
uint f; if (Win32Com.GetHandleInformation(_hPort.DangerousGetHandle(), out uint _))
if (Win32Com.GetHandleInformation(_hPort.DangerousGetHandle(), out f)) return true; {
return true;
}
ThrowException("Offline"); ThrowException("Offline");
return false; return false;
} }
if (_auto) if (_auto)
{
if (Open()) if (Open())
{
return true; return true;
}
}
ThrowException("Offline"); ThrowException("Offline");
return false; return false;
} }
} }