From 286994c2b680f01f6c59c1e9049f270e490885b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=B6glinger-Stelzer?= Date: Sat, 13 Jul 2024 14:59:01 +0200 Subject: [PATCH] More clean-up --- .../NativeMethods.txt | 1 + .../Nefarius.Peripherals.SerialPort.csproj | 1 + Nefarius.Peripherals.SerialPort/SerialPort.cs | 214 +++++++++++++----- 3 files changed, 165 insertions(+), 51 deletions(-) diff --git a/Nefarius.Peripherals.SerialPort/NativeMethods.txt b/Nefarius.Peripherals.SerialPort/NativeMethods.txt index e754077..aa60595 100644 --- a/Nefarius.Peripherals.SerialPort/NativeMethods.txt +++ b/Nefarius.Peripherals.SerialPort/NativeMethods.txt @@ -1,4 +1,5 @@ CreateFile +FILE_ACCESS_RIGHTS GetHandleInformation SetCommState SetCommTimeouts diff --git a/Nefarius.Peripherals.SerialPort/Nefarius.Peripherals.SerialPort.csproj b/Nefarius.Peripherals.SerialPort/Nefarius.Peripherals.SerialPort.csproj index db2f479..0a6e29e 100644 --- a/Nefarius.Peripherals.SerialPort/Nefarius.Peripherals.SerialPort.csproj +++ b/Nefarius.Peripherals.SerialPort/Nefarius.Peripherals.SerialPort.csproj @@ -34,5 +34,6 @@ all + \ No newline at end of file diff --git a/Nefarius.Peripherals.SerialPort/SerialPort.cs b/Nefarius.Peripherals.SerialPort/SerialPort.cs index 04f018f..fbe1613 100644 --- a/Nefarius.Peripherals.SerialPort/SerialPort.cs +++ b/Nefarius.Peripherals.SerialPort/SerialPort.cs @@ -1,12 +1,16 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Text; using System.Threading; + using Windows.Win32; using Windows.Win32.Devices.Communication; using Windows.Win32.Foundation; using Windows.Win32.Storage.FileSystem; + using Microsoft.Win32.SafeHandles; + using Nefarius.Peripherals.SerialPort.Win32PInvoke; namespace Nefarius.Peripherals.SerialPort; @@ -14,6 +18,7 @@ namespace Nefarius.Peripherals.SerialPort; /// /// Wrapper class around a serial (COM, RS-232) port. /// +[SuppressMessage("ReSharper", "UnusedMember.Global")] public partial class SerialPort : IDisposable { private readonly ManualResetEvent _writeEvent = new(false); @@ -64,18 +69,25 @@ public partial class SerialPort : IDisposable /// false if the port could not be opened public bool Open() { - var portDcb = new DCB(); - var commTimeouts = new COMMTIMEOUTS(); + DCB portDcb = new(); + COMMTIMEOUTS commTimeouts = new(); - if (_online) return false; + if (_online) + { + return false; + } _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); 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"); } @@ -92,20 +104,28 @@ public partial class SerialPort : IDisposable portDcb.ByteSize = (byte)DataBits; portDcb.Parity = (DCB_PARITY)Parity; portDcb.StopBits = (DCB_STOP_BITS)StopBits; - portDcb.XoffChar = (CHAR)(byte)XoffChar; - portDcb.XonChar = (CHAR)(byte)XonChar; + portDcb.XoffChar = new CHAR((sbyte)XoffChar); + portDcb.XonChar = new CHAR((sbyte)XonChar); portDcb.XoffLim = (ushort)RxHighWater; portDcb.XonLim = (ushort)RxLowWater; if (RxQueue != 0 || TxQueue != 0) + { if (!PInvoke.SetupComm(_hPort, (uint)RxQueue, (uint)TxQueue)) + { ThrowException("Bad queue settings"); + } + } if (!PInvoke.SetCommState(_hPort, portDcb)) + { ThrowException("Bad com settings"); + } if (!PInvoke.SetCommTimeouts(_hPort, commTimeouts)) + { ThrowException("Bad timeout settings"); + } _stateBrk = 0; switch (UseDtr) @@ -135,12 +155,9 @@ public partial class SerialPort : IDisposable _rxException = null; _rxExceptionReported = false; - // TODO: utilize Task Parallel Library here _rxThread = new Thread(ReceiveThread) { - Name = "CommBaseRx", - Priority = ThreadPriority.AboveNormal, - IsBackground = true + Name = "CommBaseRx", Priority = ThreadPriority.AboveNormal, IsBackground = true }; _rxThread.Start(); @@ -211,14 +228,22 @@ public partial class SerialPort : IDisposable /// Description of fault protected void ThrowException(string reason) { - if (Thread.CurrentThread == _rxThread) throw new CommPortException(reason); + if (Thread.CurrentThread == _rxThread) + { + throw new CommPortException(reason); + } + if (_online) { BeforeClose(true); InternalClose(); } - if (_rxException == null) throw new CommPortException(reason); + if (_rxException == null) + { + throw new CommPortException(reason); + } + throw new CommPortException(_rxException); } @@ -228,7 +253,6 @@ public partial class SerialPort : IDisposable /// Array of bytes to be sent public unsafe void Write(byte[] toSend) { - uint sent; CheckOnline(); CheckResult(); _writeCount = toSend.GetLength(0); @@ -236,6 +260,7 @@ public partial class SerialPort : IDisposable fixed (byte* ptr = toSend) fixed (NativeOverlapped* ptrOl = &_ptrUwo) { + uint sent; if (PInvoke.WriteFile(_hPort, ptr, (uint)_writeCount, &sent, ptrOl)) { _writeCount -= (int)sent; @@ -243,7 +268,9 @@ public partial class SerialPort : IDisposable else { if (Marshal.GetLastWin32Error() != (int)WIN32_ERROR.ERROR_IO_PENDING) + { ThrowException("Unexpected failure"); + } } } } @@ -263,7 +290,7 @@ public partial class SerialPort : IDisposable /// Byte to be sent public void Write(byte toSend) { - var b = new byte[1]; + byte[] b = new byte[1]; b[0] = toSend; Write(b); } @@ -288,15 +315,25 @@ public partial class SerialPort : IDisposable private void CheckResult() { - if (_writeCount <= 0) return; - if (PInvoke.GetOverlappedResult(_hPort, _ptrUwo, out var sent, _checkSends)) + if (_writeCount <= 0) + { + return; + } + + if (PInvoke.GetOverlappedResult(_hPort, _ptrUwo, out uint sent, _checkSends)) { _writeCount -= (int)sent; - if (_writeCount != 0) ThrowException("Send Timeout"); + if (_writeCount != 0) + { + ThrowException("Send Timeout"); + } } 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) { CheckOnline(); - if (!Win32Com.TransmitCommChar(_hPort.DangerousGetHandle(), tosend)) ThrowException("Transmission failure"); + if (!Win32Com.TransmitCommChar(_hPort.DangerousGetHandle(), tosend)) + { + ThrowException("Transmission failure"); + } } /// @@ -318,7 +358,11 @@ public partial class SerialPort : IDisposable protected ModemStatus GetModemStatus() { 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); } @@ -329,15 +373,19 @@ public partial class SerialPort : IDisposable protected unsafe QueueStatus GetQueueStatus() { COMSTAT cs; - var cp = new COMMPROP(); + COMMPROP cp = new(); CLEAR_COMM_ERROR_FLAGS er; CheckOnline(); if (!PInvoke.ClearCommError(_hPort, &er, &cs)) + { ThrowException("Unexpected failure"); + } - if (!PInvoke.GetCommProperties(_hPort, ref cp)) + if (!PInvoke.GetCommProperties(_hPort, &cp)) + { ThrowException("Unexpected failure"); + } 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() { - var buf = stackalloc byte[1]; + byte* buf = stackalloc byte[1]; - var sg = new AutoResetEvent(false); - var ov = new NativeOverlapped - { - EventHandle = sg.SafeWaitHandle.DangerousGetHandle() - }; + AutoResetEvent sg = new(false); + NativeOverlapped ov = new() { EventHandle = sg.SafeWaitHandle.DangerousGetHandle() }; COMM_EVENT_MASK eventMask = 0; @@ -431,14 +476,20 @@ public partial class SerialPort : IDisposable COMM_EVENT_MASK.EV_DSR | COMM_EVENT_MASK.EV_BREAK | COMM_EVENT_MASK.EV_RLSD | COMM_EVENT_MASK.EV_RING | COMM_EVENT_MASK.EV_ERR)) + { throw new CommPortException("IO Error [001]"); + } if (!PInvoke.WaitCommEvent(_hPort, ref eventMask, &ov)) { if (Marshal.GetLastWin32Error() == (int)WIN32_ERROR.ERROR_IO_PENDING) + { sg.WaitOne(); + } else + { throw new CommPortException("IO Error [002]"); + } } if ((eventMask & COMM_EVENT_MASK.EV_ERR) != 0) @@ -446,13 +497,37 @@ public partial class SerialPort : IDisposable CLEAR_COMM_ERROR_FLAGS errs; if (PInvoke.ClearCommError(_hPort, &errs, null)) { - var s = new StringBuilder("UART Error: ", 40); - if (((uint)errs & Win32Com.CE_FRAME) != 0) s = s.Append("Framing,"); - 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,"); + StringBuilder s = new("UART Error: ", 40); + if (((uint)errs & Win32Com.CE_FRAME) != 0) + { + s = s.Append("Framing,"); + } + + 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; throw new CommPortException(s.ToString()); } @@ -462,15 +537,15 @@ public partial class SerialPort : IDisposable if ((eventMask & COMM_EVENT_MASK.EV_RXCHAR) != 0) { - uint gotbytes; + uint gotBytes; 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) { Win32Com.CancelIo(_hPort.DangerousGetHandle()); - gotbytes = 0; + gotBytes = 0; } else { @@ -478,30 +553,59 @@ public partial class SerialPort : IDisposable } } - if (gotbytes == 1) OnRxChar(buf[0]); - } while (gotbytes > 0); + if (gotBytes == 1) + { + OnRxChar(buf[0]); + } + } while (gotBytes > 0); } - if ((eventMask & COMM_EVENT_MASK.EV_TXEMPTY) != 0) OnTxDone(); - if ((eventMask & COMM_EVENT_MASK.EV_BREAK) != 0) OnBreak(); + if ((eventMask & COMM_EVENT_MASK.EV_TXEMPTY) != 0) + { + OnTxDone(); + } + + if ((eventMask & COMM_EVENT_MASK.EV_BREAK) != 0) + { + OnBreak(); + } uint i = 0; - if ((eventMask & COMM_EVENT_MASK.EV_CTS) != 0) i |= Win32Com.MS_CTS_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 ((eventMask & COMM_EVENT_MASK.EV_CTS) != 0) + { + i |= Win32Com.MS_CTS_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) { uint f; if (!Win32Com.GetCommModemStatus(_hPort.DangerousGetHandle(), out f)) + { throw new CommPortException("IO Error [005]"); + } + OnStatusChange(new ModemStatus((MODEM_STATUS_FLAGS)i), new ModemStatus((MODEM_STATUS_FLAGS)f)); } } } catch (Exception e) { - if (!(e is ThreadAbortException)) + if (e is not ThreadAbortException) { _rxException = e; OnRxException(e); @@ -519,16 +623,24 @@ public partial class SerialPort : IDisposable if (_online) { - uint f; - if (Win32Com.GetHandleInformation(_hPort.DangerousGetHandle(), out f)) return true; + if (Win32Com.GetHandleInformation(_hPort.DangerousGetHandle(), out uint _)) + { + return true; + } + ThrowException("Offline"); return false; } if (_auto) + { if (Open()) + { return true; + } + } + ThrowException("Offline"); return false; } -} \ No newline at end of file +}