diff --git a/Nefarius.Peripherals.SerialPort/ModemStatus.cs b/Nefarius.Peripherals.SerialPort/ModemStatus.cs
index dc993c9..a7ad443 100644
--- a/Nefarius.Peripherals.SerialPort/ModemStatus.cs
+++ b/Nefarius.Peripherals.SerialPort/ModemStatus.cs
@@ -1,29 +1,36 @@
-using Nefarius.Peripherals.SerialPort.Win32PInvoke;
+using Windows.Win32.Devices.Communication;
-namespace Nefarius.Peripherals.SerialPort
+namespace Nefarius.Peripherals.SerialPort;
+
+///
+/// Represents the status of the modem control input signals.
+///
+public readonly struct ModemStatus
{
- ///
- /// Represents the status of the modem control input signals.
- ///
- public struct ModemStatus
+ private readonly uint _status;
+
+ internal ModemStatus(uint val)
{
- private readonly uint _status;
- internal ModemStatus(uint val) { _status = val; }
- ///
- /// Condition of the Clear To Send signal.
- ///
- public bool Cts { get { return ((_status & Win32Com.MS_CTS_ON) != 0); } }
- ///
- /// Condition of the Data Set Ready signal.
- ///
- public bool Dsr { get { return ((_status & Win32Com.MS_DSR_ON) != 0); } }
- ///
- /// Condition of the Receive Line Status Detection signal.
- ///
- public bool Rlsd { get { return ((_status & Win32Com.MS_RLSD_ON) != 0); } }
- ///
- /// Condition of the Ring Detection signal.
- ///
- public bool Ring { get { return ((_status & Win32Com.MS_RING_ON) != 0); } }
+ _status = val;
}
+
+ ///
+ /// Condition of the Clear To Send signal.
+ ///
+ public bool Cts => (_status & (uint)MODEM_STATUS_FLAGS.MS_CTS_ON) != 0;
+
+ ///
+ /// Condition of the Data Set Ready signal.
+ ///
+ public bool Dsr => (_status & (uint)MODEM_STATUS_FLAGS.MS_DSR_ON) != 0;
+
+ ///
+ /// Condition of the Receive Line Status Detection signal.
+ ///
+ public bool Rlsd => (_status & (uint)MODEM_STATUS_FLAGS.MS_RLSD_ON) != 0;
+
+ ///
+ /// Condition of the Ring Detection signal.
+ ///
+ public bool Ring => (_status & (uint)MODEM_STATUS_FLAGS.MS_RING_ON) != 0;
}
\ No newline at end of file
diff --git a/Nefarius.Peripherals.SerialPort/NativeMethods.txt b/Nefarius.Peripherals.SerialPort/NativeMethods.txt
index c2b548f..15077ca 100644
--- a/Nefarius.Peripherals.SerialPort/NativeMethods.txt
+++ b/Nefarius.Peripherals.SerialPort/NativeMethods.txt
@@ -13,4 +13,5 @@ EscapeCommFunction
GetCommModemStatus
GetOverlappedResult
ClearCommError
-GetCommProperties
\ No newline at end of file
+GetCommProperties
+WIN32_ERROR
\ No newline at end of file
diff --git a/Nefarius.Peripherals.SerialPort/Nefarius.Peripherals.SerialPort.csproj b/Nefarius.Peripherals.SerialPort/Nefarius.Peripherals.SerialPort.csproj
index ce80c6b..a071ba9 100644
--- a/Nefarius.Peripherals.SerialPort/Nefarius.Peripherals.SerialPort.csproj
+++ b/Nefarius.Peripherals.SerialPort/Nefarius.Peripherals.SerialPort.csproj
@@ -30,6 +30,7 @@
+
all
diff --git a/Nefarius.Peripherals.SerialPort/SerialPort.cs b/Nefarius.Peripherals.SerialPort/SerialPort.cs
index 673e2a5..2ca73fc 100644
--- a/Nefarius.Peripherals.SerialPort/SerialPort.cs
+++ b/Nefarius.Peripherals.SerialPort/SerialPort.cs
@@ -2,804 +2,869 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
-using Nefarius.Peripherals.SerialPort.Win32PInvoke;
+using Windows.Win32;
+using Windows.Win32.Devices.Communication;
+using Windows.Win32.Foundation;
+using Windows.Win32.Storage.FileSystem;
+using JetBrains.Annotations;
-namespace Nefarius.Peripherals.SerialPort
+namespace Nefarius.Peripherals.SerialPort;
+
+///
+/// PInvokeSerialPort main class.
+/// Borrowed from http://msdn.microsoft.com/en-us/magazine/cc301786.aspx ;)
+///
+public class SerialPort : IDisposable
{
+ ///
///
- /// PInvokeSerialPort main class.
- /// Borrowed from http://msdn.microsoft.com/en-us/magazine/cc301786.aspx ;)
+ /// For IDisposable
///
- public class SerialPort : IDisposable
+ public void Dispose()
{
- #region Private fields
+ Close();
+ }
- private readonly ManualResetEvent _writeEvent = new ManualResetEvent(false);
- private bool _auto;
- private bool _checkSends = true;
+ ///
+ /// Opens the com port and configures it with the required settings
+ ///
+ /// false if the port could not be opened
+ [UsedImplicitly]
+ public bool Open()
+ {
+ var portDcb = new DCB();
+ var commTimeouts = new COMMTIMEOUTS();
- private Handshake _handShake;
- private IntPtr _hPort;
- private bool _online;
- private IntPtr _ptrUwo = IntPtr.Zero;
- private Exception _rxException;
- private bool _rxExceptionReported;
- private Thread _rxThread;
- private int _stateBrk = 2;
- private int _stateDtr = 2;
- private int _stateRts = 2;
- private int _writeCount;
+ if (_online) return false;
- #endregion
+ _hPort = PInvoke.CreateFile(
+ PortName,
+ FILE_ACCESS_FLAGS.FILE_GENERIC_READ | FILE_ACCESS_FLAGS.FILE_GENERIC_WRITE,
+ 0,
+ null,
+ FILE_CREATION_DISPOSITION.OPEN_EXISTING, FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_OVERLAPPED,
+ null
+ );
- #region Public properties
-
- ///
- /// Class constructor
- ///
- public SerialPort(string portName)
+ if (_hPort.IsInvalid)
{
- PortName = portName;
+ if (Marshal.GetLastWin32Error() == (int)WIN32_ERROR.ERROR_ACCESS_DENIED) return false;
+ throw new CommPortException("Port Open Failure");
}
- ///
- ///
- /// Class constructor
- ///
- public SerialPort(string portName, int baudRate) : this(portName)
+ _online = true;
+
+ commTimeouts.WriteTotalTimeoutConstant = (uint)SendTimeoutConstant;
+ commTimeouts.WriteTotalTimeoutMultiplier = (uint)SendTimeoutMultiplier;
+
+ portDcb.Init(
+ Parity is Parity.Odd or Parity.Even,
+ TxFlowCts,
+ TxFlowDsr,
+ (int)UseDtr,
+ RxGateDsr,
+ !TxWhenRxXoff,
+ TxFlowX,
+ RxFlowX,
+ (int)UseRts
+ );
+ portDcb.BaudRate = (uint)BaudRate;
+ portDcb.ByteSize = (byte)DataBits;
+ portDcb.Parity = (DCB_PARITY)Parity;
+ portDcb.StopBits = (DCB_STOP_BITS)StopBits;
+ portDcb.XoffChar = new CHAR((byte)XoffChar);
+ portDcb.XonChar = new CHAR((byte)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)
{
- BaudRate = baudRate;
+ case HsOutput.None:
+ _stateDtr = 0;
+ break;
+ case HsOutput.Online:
+ _stateDtr = 1;
+ break;
}
- ///
- /// If true, the port will automatically re-open on next send if it was previously closed due
- /// to an error (default: false)
- ///
- public bool AutoReopen { get; set; }
-
- ///
- /// Baud Rate (default: 115200)
- ///
- /// Unsupported rates will throw "Bad settings".
- public int BaudRate { get; set; } = 115200;
-
- ///
- /// If true, subsequent Send commands wait for completion of earlier ones enabling the results
- /// to be checked. If false, errors, including timeouts, may not be detected, but performance
- /// may be better.
- ///
- public bool CheckAllSends { get; set; } = true;
-
- ///
- /// Number of databits 1..8 (default: 8) unsupported values will throw "Bad settings"
- ///
- public int DataBits { get; set; } = 8;
-
- ///
- /// The parity checking scheme (default: none)
- ///
- public Parity Parity { get; set; } = Parity.None;
-
- ///
- /// If true, Xon and Xoff characters are sent to control the data flow from the remote station (default: false)
- ///
- public bool RxFlowX { get; set; }
-
- ///
- /// If true, received characters are ignored unless DSR is asserted by the remote station (default: false)
- ///
- public bool RxGateDsr { get; set; }
-
- ///
- /// The number of free bytes in the reception queue at which flow is disabled (default: 2048)
- ///
- public int RxHighWater { get; set; } = 2048;
-
- ///
- /// The number of bytes in the reception queue at which flow is re-enabled (default: 512)
- ///
- public int RxLowWater { get; set; } = 512;
-
- ///
- /// Requested size for receive queue (default: 0 = use operating system default)
- ///
- public int RxQueue { get; set; }
-
- ///
- /// Constant. Max time for Send in ms = (Multiplier * Characters) + Constant (default: 0)
- ///
- public int SendTimeoutConstant { get; set; }
-
- ///
- /// Multiplier. Max time for Send in ms = (Multiplier * Characters) + Constant
- /// (default: 0 = No timeout)
- ///
- public int SendTimeoutMultiplier { get; set; }
-
- ///
- /// Number of stop bits (default: one)
- ///
- public StopBits StopBits { get; set; } = StopBits.One;
-
- ///
- /// If true, transmission is halted unless CTS is asserted by the remote station (default: false)
- ///
- public bool TxFlowCts { get; set; }
-
- ///
- /// If true, transmission is halted unless DSR is asserted by the remote station (default: false)
- ///
- public bool TxFlowDsr { get; set; }
-
- ///
- /// If true, transmission is halted when Xoff is received and restarted when Xon is received (default: false)
- ///
- public bool TxFlowX { get; set; }
-
- ///
- /// Requested size for transmit queue (default: 0 = use operating system default)
- ///
- public int TxQueue { get; set; }
-
- ///
- /// If false, transmission is suspended when this station has sent Xoff to the remote station (default: true)
- /// Set false if the remote station treats any character as an Xon.
- ///
- public bool TxWhenRxXoff { get; set; } = true;
-
- ///
- /// Specidies the use to which the DTR output is put (default: none)
- ///
- public HsOutput UseDtr { get; set; } = HsOutput.None;
-
- ///
- /// Specifies the use to which the RTS output is put (default: none)
- ///
- public HsOutput UseRts { get; set; } = HsOutput.None;
-
- ///
- /// The character used to signal Xoff for X flow control (default: DC3)
- ///
- public ASCII XoffChar { get; set; } = ASCII.DC3;
-
- ///
- /// The character used to signal Xon for X flow control (default: DC1)
- ///
- public ASCII XonChar { get; set; } = ASCII.DC1;
-
- ///
- /// True if online.
- ///
- public bool Online => _online && CheckOnline();
-
- ///
- /// True if the RTS pin is controllable via the RTS property
- ///
- protected bool RtSavailable => _stateRts < 2;
-
- ///
- /// Set the state of the RTS modem control output
- ///
- protected bool Rts
+ switch (UseRts)
{
- set
- {
- if (_stateRts > 1) return;
- CheckOnline();
- if (value)
- {
- if (Win32Com.EscapeCommFunction(_hPort, Win32Com.SETRTS))
- _stateRts = 1;
- else
- ThrowException("Unexpected Failure");
- }
- else
- {
- if (Win32Com.EscapeCommFunction(_hPort, Win32Com.CLRRTS))
- _stateRts = 1;
- else
- ThrowException("Unexpected Failure");
- }
- }
- get => _stateRts == 1;
+ case HsOutput.None:
+ _stateRts = 0;
+ break;
+ case HsOutput.Online:
+ _stateRts = 1;
+ break;
}
- ///
- /// True if the DTR pin is controllable via the DTR property
- ///
- protected bool DtrAvailable => _stateDtr < 2;
+ _checkSends = CheckAllSends;
- ///
- /// The state of the DTR modem control output
- ///
- protected bool Dtr
+ _writeOverlapped = new NativeOverlapped
{
- set
- {
- if (_stateDtr > 1) return;
- CheckOnline();
- if (value)
- {
- if (Win32Com.EscapeCommFunction(_hPort, Win32Com.SETDTR))
- _stateDtr = 1;
- else
- ThrowException("Unexpected Failure");
- }
- else
- {
- if (Win32Com.EscapeCommFunction(_hPort, Win32Com.CLRDTR))
- _stateDtr = 0;
- else
- ThrowException("Unexpected Failure");
- }
- }
- get => _stateDtr == 1;
- }
+ EventHandle = _checkSends ? _writeEvent.SafeWaitHandle.DangerousGetHandle() : IntPtr.Zero
+ };
- ///
- /// Assert or remove a break condition from the transmission line
- ///
- protected bool Break
+ _writeCount = 0;
+
+ _rxException = null;
+ _rxExceptionReported = false;
+
+ _rxThread = new Thread(ReceiveThread)
{
- set
- {
- if (_stateBrk > 1) return;
- CheckOnline();
- if (value)
- {
- if (Win32Com.EscapeCommFunction(_hPort, Win32Com.SETBREAK))
- _stateBrk = 0;
- else
- ThrowException("Unexpected Failure");
- }
- else
- {
- if (Win32Com.EscapeCommFunction(_hPort, Win32Com.CLRBREAK))
- _stateBrk = 0;
- else
- ThrowException("Unexpected Failure");
- }
- }
- get => _stateBrk == 1;
- }
+ Name = "CommBaseRx",
+ Priority = ThreadPriority.AboveNormal,
+ IsBackground = true
+ };
+ _rxThread.Start();
+ Thread.Sleep(1); //Give rx thread time to start. By documentation, 0 should work, but it does not!
- ///
- /// Port Name
- ///
- public string PortName { get; set; }
-
- public Handshake Handshake
- {
- get => _handShake;
- set
- {
- _handShake = value;
- switch (_handShake)
- {
- case Handshake.None:
- TxFlowCts = false;
- TxFlowDsr = false;
- TxFlowX = false;
- RxFlowX = false;
- UseRts = HsOutput.Online;
- UseDtr = HsOutput.Online;
- TxWhenRxXoff = true;
- RxGateDsr = false;
- break;
- case Handshake.XonXoff:
- TxFlowCts = false;
- TxFlowDsr = false;
- TxFlowX = true;
- RxFlowX = true;
- UseRts = HsOutput.Online;
- UseDtr = HsOutput.Online;
- TxWhenRxXoff = true;
- RxGateDsr = false;
- XonChar = ASCII.DC1;
- XoffChar = ASCII.DC3;
- break;
- case Handshake.CtsRts:
- TxFlowCts = true;
- TxFlowDsr = false;
- TxFlowX = false;
- RxFlowX = false;
- UseRts = HsOutput.Handshake;
- UseDtr = HsOutput.Online;
- TxWhenRxXoff = true;
- RxGateDsr = false;
- break;
- case Handshake.DsrDtr:
- TxFlowCts = false;
- TxFlowDsr = true;
- TxFlowX = false;
- RxFlowX = false;
- UseRts = HsOutput.Online;
- UseDtr = HsOutput.Handshake;
- TxWhenRxXoff = true;
- RxGateDsr = false;
- break;
- }
- }
- }
-
- #endregion
-
- ///
- ///
- /// For IDisposable
- ///
- public void Dispose()
- {
- Close();
- }
-
- ///
- /// Opens the com port and configures it with the required settings
- ///
- /// false if the port could not be opened
- public bool Open()
- {
- var portDcb = new DCB();
- var commTimeouts = new COMMTIMEOUTS();
- var wo = new OVERLAPPED();
-
- if (_online) return false;
-
- _hPort = Win32Com.CreateFile(PortName, Win32Com.GENERIC_READ | Win32Com.GENERIC_WRITE, 0, IntPtr.Zero,
- Win32Com.OPEN_EXISTING, Win32Com.FILE_FLAG_OVERLAPPED, IntPtr.Zero);
- if (_hPort == (IntPtr) Win32Com.INVALID_HANDLE_VALUE)
- {
- if (Marshal.GetLastWin32Error() == Win32Com.ERROR_ACCESS_DENIED) return false;
- throw new CommPortException("Port Open Failure");
- }
-
- _online = true;
-
- commTimeouts.ReadIntervalTimeout = 0;
- commTimeouts.ReadTotalTimeoutConstant = 0;
- commTimeouts.ReadTotalTimeoutMultiplier = 0;
- commTimeouts.WriteTotalTimeoutConstant = SendTimeoutConstant;
- commTimeouts.WriteTotalTimeoutMultiplier = SendTimeoutMultiplier;
- portDcb.Init(Parity == Parity.Odd || Parity == Parity.Even, TxFlowCts, TxFlowDsr,
- (int) UseDtr, RxGateDsr, !TxWhenRxXoff, TxFlowX, RxFlowX, (int) UseRts);
- portDcb.BaudRate = BaudRate;
- portDcb.ByteSize = (byte) DataBits;
- portDcb.Parity = (byte) Parity;
- portDcb.StopBits = (byte) StopBits;
- portDcb.XoffChar = (byte) XoffChar;
- portDcb.XonChar = (byte) XonChar;
- portDcb.XoffLim = (short) RxHighWater;
- portDcb.XonLim = (short) RxLowWater;
- if (RxQueue != 0 || TxQueue != 0)
- if (!Win32Com.SetupComm(_hPort, (uint) RxQueue, (uint) TxQueue))
- ThrowException("Bad queue settings");
- if (!Win32Com.SetCommState(_hPort, ref portDcb)) ThrowException("Bad com settings");
- if (!Win32Com.SetCommTimeouts(_hPort, ref commTimeouts)) ThrowException("Bad timeout settings");
-
- _stateBrk = 0;
- switch (UseDtr)
- {
- case HsOutput.None:
- _stateDtr = 0;
- break;
- case HsOutput.Online:
- _stateDtr = 1;
- break;
- }
-
- switch (UseRts)
- {
- case HsOutput.None:
- _stateRts = 0;
- break;
- case HsOutput.Online:
- _stateRts = 1;
- break;
- }
-
- _checkSends = CheckAllSends;
- wo.Offset = 0;
- wo.OffsetHigh = 0;
- wo.hEvent = _checkSends ? _writeEvent.SafeWaitHandle.DangerousGetHandle() : IntPtr.Zero;
- _ptrUwo = Marshal.AllocHGlobal(Marshal.SizeOf(wo));
- Marshal.StructureToPtr(wo, _ptrUwo, true);
- _writeCount = 0;
-
- _rxException = null;
- _rxExceptionReported = false;
-
- // TODO: utilize Task Parallel Library here
- _rxThread = new Thread(ReceiveThread)
- {
- Name = "CommBaseRx", Priority = ThreadPriority.AboveNormal, IsBackground = true
- };
-
- _rxThread.Start();
- Thread.Sleep(1); //Give rx thread time to start. By documentation, 0 should work, but it does not!
-
- _auto = false;
- if (AfterOpen())
- {
- _auto = AutoReopen;
- return true;
- }
-
- Close();
- return false;
- }
-
- ///
- /// Closes the com port.
- ///
- public void Close()
- {
- if (_online)
- {
- _auto = false;
- BeforeClose(false);
- InternalClose();
- _rxException = null;
- }
- }
-
- private void InternalClose()
- {
- Win32Com.CancelIo(_hPort);
- if (_rxThread != null)
- {
- _rxThread.Abort();
- _rxThread = null;
- }
-
- Win32Com.CloseHandle(_hPort);
- if (_ptrUwo != IntPtr.Zero) Marshal.FreeHGlobal(_ptrUwo);
- _stateRts = 2;
- _stateDtr = 2;
- _stateBrk = 2;
- _online = false;
- }
-
- ///
- /// Destructor (just in case)
- ///
- ~SerialPort()
- {
- Close();
- }
-
- ///
- /// Block until all bytes in the queue have been transmitted.
- ///
- public void Flush()
- {
- CheckOnline();
- CheckResult();
- }
-
- ///
- /// Use this to throw exceptions in derived classes. Correctly handles threading issues
- /// and closes the port if necessary.
- ///
- /// Description of fault
- protected void ThrowException(string reason)
- {
- if (Thread.CurrentThread == _rxThread) throw new CommPortException(reason);
- if (_online)
- {
- BeforeClose(true);
- InternalClose();
- }
-
- if (_rxException == null) throw new CommPortException(reason);
- throw new CommPortException(_rxException);
- }
-
- ///
- /// Queues bytes for transmission.
- ///
- /// Array of bytes to be sent
- public void Write(byte[] toSend)
- {
- uint sent;
- CheckOnline();
- CheckResult();
- _writeCount = toSend.GetLength(0);
- if (Win32Com.WriteFile(_hPort, toSend, (uint) _writeCount, out sent, _ptrUwo))
- {
- _writeCount -= (int) sent;
- }
- else
- {
- if (Marshal.GetLastWin32Error() != Win32Com.ERROR_IO_PENDING) ThrowException("Unexpected failure");
- }
- }
-
- ///
- /// Queues string for transmission.
- ///
- /// Array of bytes to be sent
- public void Write(string toSend)
- {
- Write(new ASCIIEncoding().GetBytes(toSend));
- }
-
- ///
- /// Queues a single byte for transmission.
- ///
- /// Byte to be sent
- public void Write(byte toSend)
- {
- var b = new byte[1];
- b[0] = toSend;
- Write(b);
- }
-
- ///
- /// Queues a single char for transmission.
- ///
- /// Byte to be sent
- public void Write(char toSend)
- {
- Write(toSend.ToString());
- }
-
- ///
- /// Queues string with a new line ("\r\n") for transmission.
- ///
- /// Array of bytes to be sent
- public void WriteLine(string toSend)
- {
- Write(new ASCIIEncoding().GetBytes(toSend + Environment.NewLine));
- }
-
- private void CheckResult()
- {
- if (_writeCount <= 0) return;
- uint sent;
- if (Win32Com.GetOverlappedResult(_hPort, _ptrUwo, out sent, _checkSends))
- {
- _writeCount -= (int) sent;
- if (_writeCount != 0) ThrowException("Send Timeout");
- }
- else
- {
- if (Marshal.GetLastWin32Error() != Win32Com.ERROR_IO_PENDING) ThrowException("Unexpected failure");
- }
- }
-
- ///
- /// Sends a protocol byte immediately ahead of any queued bytes.
- ///
- /// Byte to send
- /// False if an immediate byte is already scheduled and not yet sent
- public void SendImmediate(byte tosend)
- {
- CheckOnline();
- if (!Win32Com.TransmitCommChar(_hPort, tosend)) ThrowException("Transmission failure");
- }
-
- ///
- /// Gets the status of the modem control input signals.
- ///
- /// Modem status object
- protected ModemStatus GetModemStatus()
- {
- uint f;
-
- CheckOnline();
- if (!Win32Com.GetCommModemStatus(_hPort, out f)) ThrowException("Unexpected failure");
- return new ModemStatus(f);
- }
-
-
- ///
- /// Get the status of the queues
- ///
- /// Queue status object
- protected QueueStatus GetQueueStatus()
- {
- COMSTAT cs;
- COMMPROP cp;
- uint er;
-
- CheckOnline();
- if (!Win32Com.ClearCommError(_hPort, out er, out cs)) ThrowException("Unexpected failure");
- if (!Win32Com.GetCommProperties(_hPort, out cp)) ThrowException("Unexpected failure");
- return new QueueStatus(cs.Flags, cs.cbInQue, cs.cbOutQue, cp.dwCurrentRxQueue, cp.dwCurrentTxQueue);
- }
-
- ///
- /// Override this to provide processing after the port is opened (i.e. to configure remote
- /// device or just check presence).
- ///
- /// false to close the port again
- protected virtual bool AfterOpen()
+ _auto = false;
+ if (AfterOpen())
{
+ _auto = AutoReopen;
return true;
}
- ///
- /// Override this to provide processing prior to port closure.
- ///
- /// True if closing due to an error
- protected virtual void BeforeClose(bool error)
+ Close();
+
+ return false;
+ }
+
+ ///
+ /// Closes the com port.
+ ///
+ [UsedImplicitly]
+ public void Close()
+ {
+ if (_online)
{
+ _auto = false;
+ BeforeClose(false);
+ InternalClose();
+ _rxException = null;
+ }
+ }
+
+ private void InternalClose()
+ {
+ PInvoke.CancelIo(_hPort);
+ if (_rxThread != null)
+ {
+ _rxThread.Abort();
+ _rxThread = null;
}
- public event Action DataReceived;
+ _hPort.Close();
- ///
- /// Override this to process received bytes.
- ///
- /// The byte that was received
- protected void OnRxChar(byte ch)
+ _stateRts = 2;
+ _stateDtr = 2;
+ _stateBrk = 2;
+ _online = false;
+ }
+
+ ///
+ /// Destructor (just in case)
+ ///
+ ~SerialPort()
+ {
+ Close();
+ }
+
+ ///
+ /// Block until all bytes in the queue have been transmitted.
+ ///
+ [UsedImplicitly]
+ public void Flush()
+ {
+ CheckOnline();
+ CheckResult();
+ }
+
+ ///
+ /// Use this to throw exceptions in derived classes. Correctly handles threading issues
+ /// and closes the port if necessary.
+ ///
+ /// Description of fault
+ protected void ThrowException(string reason)
+ {
+ if (Thread.CurrentThread == _rxThread) throw new CommPortException(reason);
+ if (_online)
{
- DataReceived?.Invoke(ch);
+ BeforeClose(true);
+ InternalClose();
}
- ///
- /// Override this to take action when transmission is complete (i.e. all bytes have actually
- /// been sent, not just queued).
- ///
- protected virtual void OnTxDone()
+ if (_rxException == null) throw new CommPortException(reason);
+ throw new CommPortException(_rxException);
+ }
+
+ ///
+ /// Queues bytes for transmission.
+ ///
+ /// Array of bytes to be sent
+ [UsedImplicitly]
+ public unsafe void Write(byte[] toSend)
+ {
+ CheckOnline();
+ CheckResult();
+ _writeCount = toSend.GetLength(0);
+
+ fixed (byte* ptr = toSend)
+ fixed (NativeOverlapped* overlapped = &_writeOverlapped)
{
- }
-
- ///
- /// Override this to take action when a break condition is detected on the input line.
- ///
- protected virtual void OnBreak()
- {
- }
-
- ///
- /// Override this to take action when a ring condition is signaled by an attached modem.
- ///
- protected virtual void OnRing()
- {
- }
-
- ///
- /// Override this to take action when one or more modem status inputs change state
- ///
- /// The status inputs that have changed state
- /// The state of the status inputs
- protected virtual void OnStatusChange(ModemStatus mask, ModemStatus state)
- {
- }
-
- ///
- /// Override this to take action when the reception thread closes due to an exception being thrown.
- ///
- /// The exception which was thrown
- protected virtual void OnRxException(Exception e)
- {
- }
-
- private void ReceiveThread()
- {
- var buf = new byte[1];
-
- var sg = new AutoResetEvent(false);
- var ov = new OVERLAPPED();
- var unmanagedOv = Marshal.AllocHGlobal(Marshal.SizeOf(ov));
- ov.Offset = 0;
- ov.OffsetHigh = 0;
- ov.hEvent = sg.SafeWaitHandle.DangerousGetHandle();
- Marshal.StructureToPtr(ov, unmanagedOv, true);
-
- uint eventMask = 0;
- var uMask = Marshal.AllocHGlobal(Marshal.SizeOf(eventMask));
-
- try
+ uint sent;
+ if (PInvoke.WriteFile(_hPort, ptr, (uint)_writeCount, &sent, overlapped))
{
- while (true)
+ _writeCount -= (int)sent;
+ }
+ else
+ {
+ if (Marshal.GetLastWin32Error() != (int)WIN32_ERROR.ERROR_IO_PENDING)
+ ThrowException("Unexpected failure");
+ }
+ }
+ }
+
+ ///
+ /// Queues string for transmission.
+ ///
+ /// Array of bytes to be sent
+ [UsedImplicitly]
+ public void Write(string toSend)
+ {
+ Write(new ASCIIEncoding().GetBytes(toSend));
+ }
+
+ ///
+ /// Queues a single byte for transmission.
+ ///
+ /// Byte to be sent
+ [UsedImplicitly]
+ public void Write(byte toSend)
+ {
+ var b = new byte[1];
+ b[0] = toSend;
+ Write(b);
+ }
+
+ ///
+ /// Queues a single char for transmission.
+ ///
+ /// Byte to be sent
+ [UsedImplicitly]
+ public void Write(char toSend)
+ {
+ Write(toSend.ToString());
+ }
+
+ ///
+ /// Queues string with a new line ("\r\n") for transmission.
+ ///
+ /// Array of bytes to be sent
+ [UsedImplicitly]
+ public void WriteLine(string toSend)
+ {
+ Write(new ASCIIEncoding().GetBytes(toSend + Environment.NewLine));
+ }
+
+ private void CheckResult()
+ {
+ if (_writeCount <= 0) return;
+
+ if (PInvoke.GetOverlappedResult(_hPort, _writeOverlapped, out var sent, _checkSends))
+ {
+ _writeCount -= (int)sent;
+ if (_writeCount != 0) ThrowException("Send Timeout");
+ }
+ else
+ {
+ if (Marshal.GetLastWin32Error() != (int)WIN32_ERROR.ERROR_IO_PENDING) ThrowException("Unexpected failure");
+ }
+ }
+
+ ///
+ /// Sends a protocol byte immediately ahead of any queued bytes.
+ ///
+ /// Byte to send
+ /// False if an immediate byte is already scheduled and not yet sent
+ public void SendImmediate(byte toSend)
+ {
+ CheckOnline();
+ if (!PInvoke.TransmitCommChar(_hPort, new CHAR(toSend))) ThrowException("Transmission failure");
+ }
+
+ ///
+ /// Gets the status of the modem control input signals.
+ ///
+ /// Modem status object
+ /*protected ModemStatus GetModemStatus()
+ {
+ CheckOnline();
+ if (!PInvoke.GetCommModemStatus(_hPort, out var f)) ThrowException("Unexpected failure");
+ return new ModemStatus(f);
+ }
+ */
+
+ ///
+ /// Get the status of the queues
+ ///
+ /// Queue status object
+ /*protected QueueStatus GetQueueStatus()
+ {
+ COMSTAT cs;
+ COMMPROP cp;
+ uint er;
+
+ CheckOnline();
+ if (!PInvoke.ClearCommError(_hPort, out er, out cs)) ThrowException("Unexpected failure");
+ if (!PInvoke.GetCommProperties(_hPort, out cp)) ThrowException("Unexpected failure");
+ return new QueueStatus(cs.Flags, cs.cbInQue, cs.cbOutQue, cp.dwCurrentRxQueue, cp.dwCurrentTxQueue);
+ }*/
+
+ ///
+ /// Override this to provide processing after the port is opened (i.e. to configure remote
+ /// device or just check presence).
+ ///
+ /// false to close the port again
+ protected virtual bool AfterOpen()
+ {
+ return true;
+ }
+
+ ///
+ /// Override this to provide processing prior to port closure.
+ ///
+ /// True if closing due to an error
+ protected virtual void BeforeClose(bool error)
+ {
+ }
+
+ public event Action DataReceived;
+
+ ///
+ /// Override this to process received bytes.
+ ///
+ /// The byte that was received
+ protected void OnRxChar(byte ch)
+ {
+ DataReceived?.Invoke(ch);
+ }
+
+ ///
+ /// Override this to take action when transmission is complete (i.e. all bytes have actually
+ /// been sent, not just queued).
+ ///
+ protected virtual void OnTxDone()
+ {
+ }
+
+ ///
+ /// Override this to take action when a break condition is detected on the input line.
+ ///
+ protected virtual void OnBreak()
+ {
+ }
+
+ ///
+ /// Override this to take action when a ring condition is signaled by an attached modem.
+ ///
+ protected virtual void OnRing()
+ {
+ }
+
+ ///
+ /// Override this to take action when one or more modem status inputs change state
+ ///
+ /// The status inputs that have changed state
+ /// The state of the status inputs
+ protected virtual void OnStatusChange(ModemStatus mask, ModemStatus state)
+ {
+ }
+
+ ///
+ /// Override this to take action when the reception thread closes due to an exception being thrown.
+ ///
+ /// The exception which was thrown
+ protected virtual void OnRxException(Exception e)
+ {
+ }
+
+ private unsafe void ReceiveThread()
+ {
+ var buf = new byte[1];
+
+ var sg = new AutoResetEvent(false);
+ var ov = new NativeOverlapped
+ {
+ EventHandle = sg.SafeWaitHandle.DangerousGetHandle()
+ };
+
+ COMM_EVENT_MASK eventMask = 0;
+
+ try
+ {
+ while (true)
+ {
+ if (!PInvoke.SetCommMask(
+ _hPort,
+ COMM_EVENT_MASK.EV_RXCHAR |
+ COMM_EVENT_MASK.EV_TXEMPTY |
+ COMM_EVENT_MASK.EV_CTS |
+ 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 (!Win32Com.SetCommMask(_hPort,
- Win32Com.EV_RXCHAR | Win32Com.EV_TXEMPTY | Win32Com.EV_CTS | Win32Com.EV_DSR
- | Win32Com.EV_BREAK | Win32Com.EV_RLSD | Win32Com.EV_RING | Win32Com.EV_ERR))
- throw new CommPortException("IO Error [001]");
- Marshal.WriteInt32(uMask, 0);
- if (!Win32Com.WaitCommEvent(_hPort, uMask, unmanagedOv))
+ 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)
+ {
+ CLEAR_COMM_ERROR_FLAGS errs;
+
+ if (PInvoke.ClearCommError(_hPort, &errs, null))
{
- if (Marshal.GetLastWin32Error() == Win32Com.ERROR_IO_PENDING)
- sg.WaitOne();
- else
- throw new CommPortException("IO Error [002]");
+ var s = new StringBuilder("UART Error: ", 40);
+ if ((errs & CLEAR_COMM_ERROR_FLAGS.CE_FRAME) != 0) s = s.Append("Framing,");
+ if ((errs & CLEAR_COMM_ERROR_FLAGS.CE_BREAK) != 0) s = s.Append("Break,");
+ if ((errs & CLEAR_COMM_ERROR_FLAGS.CE_OVERRUN) != 0) s = s.Append("Overrun,");
+ if ((errs & CLEAR_COMM_ERROR_FLAGS.CE_RXOVER) != 0) s = s.Append("Receive Overflow,");
+ if ((errs & CLEAR_COMM_ERROR_FLAGS.CE_RXPARITY) != 0) s = s.Append("Parity,");
+
+ s.Length = s.Length - 1;
+
+ throw new CommPortException(s.ToString());
}
- eventMask = (uint) Marshal.ReadInt32(uMask);
- if ((eventMask & Win32Com.EV_ERR) != 0)
- {
- uint errs;
- if (Win32Com.ClearCommError(_hPort, out errs, IntPtr.Zero))
- {
- var s = new StringBuilder("UART Error: ", 40);
- if ((errs & Win32Com.CE_FRAME) != 0) s = s.Append("Framing,");
- if ((errs & Win32Com.CE_IOE) != 0) s = s.Append("IO,");
- if ((errs & Win32Com.CE_OVERRUN) != 0) s = s.Append("Overrun,");
- if ((errs & Win32Com.CE_RXOVER) != 0) s = s.Append("Receive Overflow,");
- if ((errs & Win32Com.CE_RXPARITY) != 0) s = s.Append("Parity,");
- if ((errs & Win32Com.CE_TXFULL) != 0) s = s.Append("Transmit Overflow,");
- s.Length = s.Length - 1;
- throw new CommPortException(s.ToString());
- }
+ throw new CommPortException("IO Error [003]");
+ }
- throw new CommPortException("IO Error [003]");
- }
-
- if ((eventMask & Win32Com.EV_RXCHAR) != 0)
+ if ((eventMask & COMM_EVENT_MASK.EV_RXCHAR) != 0)
+ {
+ uint gotBytes;
+ do
{
- uint gotbytes;
- do
+ fixed (byte* ptrBuffer = buf)
{
- if (!Win32Com.ReadFile(_hPort, buf, 1, out gotbytes, unmanagedOv))
+ if (!PInvoke.ReadFile(_hPort, ptrBuffer, 1, &gotBytes, &ov))
{
- if (Marshal.GetLastWin32Error() == Win32Com.ERROR_IO_PENDING)
+ if (Marshal.GetLastWin32Error() == (int)WIN32_ERROR.ERROR_IO_PENDING)
{
- Win32Com.CancelIo(_hPort);
- gotbytes = 0;
+ PInvoke.CancelIo(_hPort);
+ gotBytes = 0;
}
else
{
throw new CommPortException("IO Error [004]");
}
}
+ }
- if (gotbytes == 1) OnRxChar(buf[0]);
- } while (gotbytes > 0);
- }
-
- if ((eventMask & Win32Com.EV_TXEMPTY) != 0) OnTxDone();
- if ((eventMask & Win32Com.EV_BREAK) != 0) OnBreak();
-
- uint i = 0;
- if ((eventMask & Win32Com.EV_CTS) != 0) i |= Win32Com.MS_CTS_ON;
- if ((eventMask & Win32Com.EV_DSR) != 0) i |= Win32Com.MS_DSR_ON;
- if ((eventMask & Win32Com.EV_RLSD) != 0) i |= Win32Com.MS_RLSD_ON;
- if ((eventMask & Win32Com.EV_RING) != 0) i |= Win32Com.MS_RING_ON;
- if (i != 0)
- {
- uint f;
- if (!Win32Com.GetCommModemStatus(_hPort, out f)) throw new CommPortException("IO Error [005]");
- OnStatusChange(new ModemStatus(i), new ModemStatus(f));
- }
- }
- }
- catch (Exception e)
- {
- if (uMask != IntPtr.Zero) Marshal.FreeHGlobal(uMask);
- if (unmanagedOv != IntPtr.Zero) Marshal.FreeHGlobal(unmanagedOv);
- if (!(e is ThreadAbortException))
- {
- _rxException = e;
- OnRxException(e);
+ 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();
+
+ uint i = 0;
+
+ if ((eventMask & COMM_EVENT_MASK.EV_CTS) != 0) i |= (uint)MODEM_STATUS_FLAGS.MS_CTS_ON;
+ if ((eventMask & COMM_EVENT_MASK.EV_DSR) != 0) i |= (uint)MODEM_STATUS_FLAGS.MS_DSR_ON;
+ if ((eventMask & COMM_EVENT_MASK.EV_RLSD) != 0) i |= (uint)MODEM_STATUS_FLAGS.MS_RLSD_ON;
+ if ((eventMask & COMM_EVENT_MASK.EV_RING) != 0) i |= (uint)MODEM_STATUS_FLAGS.MS_RING_ON;
+
+ if (i != 0)
+ if (!PInvoke.GetCommModemStatus(_hPort, out var f))
+ throw new CommPortException("IO Error [005]");
+ // TODO: fix me! OnStatusChange(new ModemStatus(i), new ModemStatus(f));
}
}
-
- private bool CheckOnline()
+ catch (Exception e)
{
- if (_rxException != null && !_rxExceptionReported)
+ if (e is not ThreadAbortException)
{
- _rxExceptionReported = true;
- ThrowException("rx");
+ _rxException = e;
+ OnRxException(e);
}
+ }
+ }
- if (_online)
- {
- uint f;
- if (Win32Com.GetHandleInformation(_hPort, out f)) return true;
- ThrowException("Offline");
- return false;
- }
+ private bool CheckOnline()
+ {
+ if (_rxException != null && !_rxExceptionReported)
+ {
+ _rxExceptionReported = true;
+ ThrowException("rx");
+ }
- if (_auto)
- if (Open())
- return true;
+ if (_online)
+ {
+ uint f;
+ if (PInvoke.GetHandleInformation(_hPort, out f)) return true;
ThrowException("Offline");
return false;
}
+
+ if (_auto)
+ if (Open())
+ return true;
+ ThrowException("Offline");
+ return false;
}
+
+ #region Private fields
+
+ private readonly ManualResetEvent _writeEvent = new(false);
+ private bool _auto;
+ private bool _checkSends = true;
+
+ private Handshake _handShake;
+ private SafeHandle _hPort;
+ private bool _online;
+ private NativeOverlapped _writeOverlapped;
+ private Exception _rxException;
+ private bool _rxExceptionReported;
+ private Thread _rxThread;
+ private int _stateBrk = 2;
+ private int _stateDtr = 2;
+ private int _stateRts = 2;
+ private int _writeCount;
+
+ #endregion
+
+ #region Public properties
+
+ ///
+ /// Class constructor
+ ///
+ public SerialPort(string portName)
+ {
+ PortName = portName;
+ }
+
+ ///
+ ///
+ /// Class constructor
+ ///
+ public SerialPort(string portName, int baudRate) : this(portName)
+ {
+ BaudRate = baudRate;
+ }
+
+ ///
+ /// If true, the port will automatically re-open on next send if it was previously closed due
+ /// to an error (default: false)
+ ///
+ [UsedImplicitly]
+ public bool AutoReopen { get; set; }
+
+ ///
+ /// Baud Rate (default: 115200)
+ ///
+ /// Unsupported rates will throw "Bad settings".
+ [UsedImplicitly]
+ public int BaudRate { get; set; } = 115200;
+
+ ///
+ /// If true, subsequent Send commands wait for completion of earlier ones enabling the results
+ /// to be checked. If false, errors, including timeouts, may not be detected, but performance
+ /// may be better.
+ ///
+ [UsedImplicitly]
+ public bool CheckAllSends { get; set; } = true;
+
+ ///
+ /// Number of databits 1..8 (default: 8) unsupported values will throw "Bad settings"
+ ///
+ [UsedImplicitly]
+ public int DataBits { get; set; } = 8;
+
+ ///
+ /// The parity checking scheme (default: none)
+ ///
+ [UsedImplicitly]
+ public Parity Parity { get; set; } = Parity.None;
+
+ ///
+ /// If true, Xon and Xoff characters are sent to control the data flow from the remote station (default: false)
+ ///
+ [UsedImplicitly]
+ public bool RxFlowX { get; set; }
+
+ ///
+ /// If true, received characters are ignored unless DSR is asserted by the remote station (default: false)
+ ///
+ [UsedImplicitly]
+ public bool RxGateDsr { get; set; }
+
+ ///
+ /// The number of free bytes in the reception queue at which flow is disabled (default: 2048)
+ ///
+ [UsedImplicitly]
+ public int RxHighWater { get; set; } = 2048;
+
+ ///
+ /// The number of bytes in the reception queue at which flow is re-enabled (default: 512)
+ ///
+ [UsedImplicitly]
+ public int RxLowWater { get; set; } = 512;
+
+ ///
+ /// Requested size for receive queue (default: 0 = use operating system default)
+ ///
+ [UsedImplicitly]
+ public int RxQueue { get; set; }
+
+ ///
+ /// Constant. Max time for Send in ms = (Multiplier * Characters) + Constant (default: 0)
+ ///
+ [UsedImplicitly]
+ public int SendTimeoutConstant { get; set; }
+
+ ///
+ /// Multiplier. Max time for Send in ms = (Multiplier * Characters) + Constant
+ /// (default: 0 = No timeout)
+ ///
+ [UsedImplicitly]
+ public int SendTimeoutMultiplier { get; set; }
+
+ ///
+ /// Number of stop bits (default: one)
+ ///
+ [UsedImplicitly]
+ public StopBits StopBits { get; set; } = StopBits.One;
+
+ ///
+ /// If true, transmission is halted unless CTS is asserted by the remote station (default: false)
+ ///
+ [UsedImplicitly]
+ public bool TxFlowCts { get; set; }
+
+ ///
+ /// If true, transmission is halted unless DSR is asserted by the remote station (default: false)
+ ///
+ [UsedImplicitly]
+ public bool TxFlowDsr { get; set; }
+
+ ///
+ /// If true, transmission is halted when Xoff is received and restarted when Xon is received (default: false)
+ ///
+ [UsedImplicitly]
+ public bool TxFlowX { get; set; }
+
+ ///
+ /// Requested size for transmit queue (default: 0 = use operating system default)
+ ///
+ [UsedImplicitly]
+ public int TxQueue { get; set; }
+
+ ///
+ /// If false, transmission is suspended when this station has sent Xoff to the remote station (default: true)
+ /// Set false if the remote station treats any character as an Xon.
+ ///
+ [UsedImplicitly]
+ public bool TxWhenRxXoff { get; set; } = true;
+
+ ///
+ /// Specidies the use to which the DTR output is put (default: none)
+ ///
+ [UsedImplicitly]
+ public HsOutput UseDtr { get; set; } = HsOutput.None;
+
+ ///
+ /// Specifies the use to which the RTS output is put (default: none)
+ ///
+ [UsedImplicitly]
+ public HsOutput UseRts { get; set; } = HsOutput.None;
+
+ ///
+ /// The character used to signal Xoff for X flow control (default: DC3)
+ ///
+ [UsedImplicitly]
+ public ASCII XoffChar { get; set; } = ASCII.DC3;
+
+ ///
+ /// The character used to signal Xon for X flow control (default: DC1)
+ ///
+ [UsedImplicitly]
+ public ASCII XonChar { get; set; } = ASCII.DC1;
+
+ ///
+ /// True if online.
+ ///
+ [UsedImplicitly]
+ public bool Online => _online && CheckOnline();
+
+ ///
+ /// True if the RTS pin is controllable via the RTS property
+ ///
+ [UsedImplicitly]
+ protected bool RtSavailable => _stateRts < 2;
+
+ ///
+ /// Set the state of the RTS modem control output
+ ///
+ protected bool Rts
+ {
+ set
+ {
+ if (_stateRts > 1) return;
+ CheckOnline();
+ if (value)
+ {
+ if (PInvoke.EscapeCommFunction(_hPort, ESCAPE_COMM_FUNCTION.SETRTS))
+ _stateRts = 1;
+ else
+ ThrowException("Unexpected Failure");
+ }
+ else
+ {
+ if (PInvoke.EscapeCommFunction(_hPort, ESCAPE_COMM_FUNCTION.CLRRTS))
+ _stateRts = 1;
+ else
+ ThrowException("Unexpected Failure");
+ }
+ }
+ get => _stateRts == 1;
+ }
+
+ ///
+ /// True if the DTR pin is controllable via the DTR property
+ ///
+ protected bool DtrAvailable => _stateDtr < 2;
+
+ ///
+ /// The state of the DTR modem control output
+ ///
+ protected bool Dtr
+ {
+ set
+ {
+ if (_stateDtr > 1) return;
+ CheckOnline();
+ if (value)
+ {
+ if (PInvoke.EscapeCommFunction(_hPort, ESCAPE_COMM_FUNCTION.SETDTR))
+ _stateDtr = 1;
+ else
+ ThrowException("Unexpected Failure");
+ }
+ else
+ {
+ if (PInvoke.EscapeCommFunction(_hPort, ESCAPE_COMM_FUNCTION.CLRDTR))
+ _stateDtr = 0;
+ else
+ ThrowException("Unexpected Failure");
+ }
+ }
+ get => _stateDtr == 1;
+ }
+
+ ///
+ /// Assert or remove a break condition from the transmission line
+ ///
+ protected bool Break
+ {
+ set
+ {
+ if (_stateBrk > 1) return;
+ CheckOnline();
+ if (value)
+ {
+ if (PInvoke.EscapeCommFunction(_hPort, ESCAPE_COMM_FUNCTION.SETBREAK))
+ _stateBrk = 0;
+ else
+ ThrowException("Unexpected Failure");
+ }
+ else
+ {
+ if (PInvoke.EscapeCommFunction(_hPort, ESCAPE_COMM_FUNCTION.CLRBREAK))
+ _stateBrk = 0;
+ else
+ ThrowException("Unexpected Failure");
+ }
+ }
+ get => _stateBrk == 1;
+ }
+
+
+ ///
+ /// Port Name
+ ///
+ [UsedImplicitly]
+ public string PortName { get; set; }
+
+ public Handshake Handshake
+ {
+ get => _handShake;
+ set
+ {
+ _handShake = value;
+ switch (_handShake)
+ {
+ case Handshake.None:
+ TxFlowCts = false;
+ TxFlowDsr = false;
+ TxFlowX = false;
+ RxFlowX = false;
+ UseRts = HsOutput.Online;
+ UseDtr = HsOutput.Online;
+ TxWhenRxXoff = true;
+ RxGateDsr = false;
+ break;
+ case Handshake.XonXoff:
+ TxFlowCts = false;
+ TxFlowDsr = false;
+ TxFlowX = true;
+ RxFlowX = true;
+ UseRts = HsOutput.Online;
+ UseDtr = HsOutput.Online;
+ TxWhenRxXoff = true;
+ RxGateDsr = false;
+ XonChar = ASCII.DC1;
+ XoffChar = ASCII.DC3;
+ break;
+ case Handshake.CtsRts:
+ TxFlowCts = true;
+ TxFlowDsr = false;
+ TxFlowX = false;
+ RxFlowX = false;
+ UseRts = HsOutput.Handshake;
+ UseDtr = HsOutput.Online;
+ TxWhenRxXoff = true;
+ RxGateDsr = false;
+ break;
+ case Handshake.DsrDtr:
+ TxFlowCts = false;
+ TxFlowDsr = true;
+ TxFlowX = false;
+ RxFlowX = false;
+ UseRts = HsOutput.Online;
+ UseDtr = HsOutput.Handshake;
+ TxWhenRxXoff = true;
+ RxGateDsr = false;
+ break;
+ }
+ }
+ }
+
+ #endregion
}
\ No newline at end of file
diff --git a/Nefarius.Peripherals.SerialPort/Util.cs b/Nefarius.Peripherals.SerialPort/Util.cs
new file mode 100644
index 0000000..c0f22b0
--- /dev/null
+++ b/Nefarius.Peripherals.SerialPort/Util.cs
@@ -0,0 +1,23 @@
+using Windows.Win32.Devices.Communication;
+
+namespace Nefarius.Peripherals.SerialPort;
+
+internal static class DCBExtensions
+{
+ public static void Init(this DCB dcb, bool parity, bool outCts, bool outDsr, int dtr, bool inDsr, bool txc,
+ bool xOut,
+ bool xIn, int rts)
+ {
+ dcb.DCBlength = 28;
+ dcb._bitfield = 0x8001;
+ if (parity) dcb._bitfield |= 0x0002;
+ if (outCts) dcb._bitfield |= 0x0004;
+ if (outDsr) dcb._bitfield |= 0x0008;
+ dcb._bitfield |= (uint)((dtr & 0x0003) << 4);
+ if (inDsr) dcb._bitfield |= 0x0040;
+ if (txc) dcb._bitfield |= 0x0080;
+ if (xOut) dcb._bitfield |= 0x0100;
+ if (xIn) dcb._bitfield |= 0x0200;
+ dcb._bitfield |= (uint)((rts & 0x0003) << 12);
+ }
+}
\ No newline at end of file
diff --git a/Nefarius.Peripherals.SerialPort/Win32PInvoke/COMMPROP.cs b/Nefarius.Peripherals.SerialPort/Win32PInvoke/COMMPROP.cs
deleted file mode 100644
index bbd37be..0000000
--- a/Nefarius.Peripherals.SerialPort/Win32PInvoke/COMMPROP.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Nefarius.Peripherals.SerialPort.Win32PInvoke
-{
- [StructLayout(LayoutKind.Sequential)]
- internal struct COMMPROP
- {
- internal UInt16 wPacketLength;
- internal UInt16 wPacketVersion;
- internal UInt32 dwServiceMask;
- internal UInt32 dwReserved1;
- internal UInt32 dwMaxTxQueue;
- internal UInt32 dwMaxRxQueue;
- internal UInt32 dwMaxBaud;
- internal UInt32 dwProvSubType;
- internal UInt32 dwProvCapabilities;
- internal UInt32 dwSettableParams;
- internal UInt32 dwSettableBaud;
- internal UInt16 wSettableData;
- internal UInt16 wSettableStopParity;
- internal UInt32 dwCurrentTxQueue;
- internal UInt32 dwCurrentRxQueue;
- internal UInt32 dwProvSpec1;
- internal UInt32 dwProvSpec2;
- internal Byte wcProvChar;
- }
-}
\ No newline at end of file
diff --git a/Nefarius.Peripherals.SerialPort/Win32PInvoke/COMMTIMEOUTS.cs b/Nefarius.Peripherals.SerialPort/Win32PInvoke/COMMTIMEOUTS.cs
deleted file mode 100644
index 6f74e2b..0000000
--- a/Nefarius.Peripherals.SerialPort/Win32PInvoke/COMMTIMEOUTS.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Nefarius.Peripherals.SerialPort.Win32PInvoke
-{
- [StructLayout(LayoutKind.Sequential)]
- internal struct COMMTIMEOUTS
- {
- internal Int32 ReadIntervalTimeout;
- internal Int32 ReadTotalTimeoutMultiplier;
- internal Int32 ReadTotalTimeoutConstant;
- internal Int32 WriteTotalTimeoutMultiplier;
- internal Int32 WriteTotalTimeoutConstant;
- }
-}
\ No newline at end of file
diff --git a/Nefarius.Peripherals.SerialPort/Win32PInvoke/COMSTAT.cs b/Nefarius.Peripherals.SerialPort/Win32PInvoke/COMSTAT.cs
index 523182c..693bae2 100644
--- a/Nefarius.Peripherals.SerialPort/Win32PInvoke/COMSTAT.cs
+++ b/Nefarius.Peripherals.SerialPort/Win32PInvoke/COMSTAT.cs
@@ -1,20 +1,19 @@
using System;
using System.Runtime.InteropServices;
-namespace Nefarius.Peripherals.SerialPort.Win32PInvoke
+namespace Nefarius.Peripherals.SerialPort.Win32PInvoke;
+
+[StructLayout(LayoutKind.Sequential)]
+internal struct COMSTAT
{
- [StructLayout(LayoutKind.Sequential)]
- internal struct COMSTAT
- {
- internal const uint fCtsHold = 0x1;
- internal const uint fDsrHold = 0x2;
- internal const uint fRlsdHold = 0x4;
- internal const uint fXoffHold = 0x8;
- internal const uint fXoffSent = 0x10;
- internal const uint fEof = 0x20;
- internal const uint fTxim = 0x40;
- internal UInt32 Flags;
- internal UInt32 cbInQue;
- internal UInt32 cbOutQue;
- }
+ internal const uint fCtsHold = 0x1;
+ internal const uint fDsrHold = 0x2;
+ internal const uint fRlsdHold = 0x4;
+ internal const uint fXoffHold = 0x8;
+ internal const uint fXoffSent = 0x10;
+ internal const uint fEof = 0x20;
+ internal const uint fTxim = 0x40;
+ internal UInt32 Flags;
+ internal UInt32 cbInQue;
+ internal UInt32 cbOutQue;
}
\ No newline at end of file
diff --git a/Nefarius.Peripherals.SerialPort/Win32PInvoke/DCB.cs b/Nefarius.Peripherals.SerialPort/Win32PInvoke/DCB.cs
deleted file mode 100644
index ff3bee2..0000000
--- a/Nefarius.Peripherals.SerialPort/Win32PInvoke/DCB.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Nefarius.Peripherals.SerialPort.Win32PInvoke
-{
- [StructLayout(LayoutKind.Sequential)]
- internal struct DCB
- {
- internal Int32 DCBlength;
- internal Int32 BaudRate;
- internal Int32 PackedValues;
- internal Int16 wReserved;
- internal Int16 XonLim;
- internal Int16 XoffLim;
- internal Byte ByteSize;
- internal Byte Parity;
- internal Byte StopBits;
- internal Byte XonChar;
- internal Byte XoffChar;
- internal Byte ErrorChar;
- internal Byte EofChar;
- internal Byte EvtChar;
- internal Int16 wReserved1;
-
- internal void Init(bool parity, bool outCts, bool outDsr, int dtr, bool inDsr, bool txc, bool xOut,
- bool xIn, int rts)
- {
- DCBlength = 28; PackedValues = 0x8001;
- if (parity) PackedValues |= 0x0002;
- if (outCts) PackedValues |= 0x0004;
- if (outDsr) PackedValues |= 0x0008;
- PackedValues |= ((dtr & 0x0003) << 4);
- if (inDsr) PackedValues |= 0x0040;
- if (txc) PackedValues |= 0x0080;
- if (xOut) PackedValues |= 0x0100;
- if (xIn) PackedValues |= 0x0200;
- PackedValues |= ((rts & 0x0003) << 12);
-
- }
- }
-}
\ No newline at end of file
diff --git a/Nefarius.Peripherals.SerialPort/Win32PInvoke/OVERLAPPED.cs b/Nefarius.Peripherals.SerialPort/Win32PInvoke/OVERLAPPED.cs
deleted file mode 100644
index 8bd992a..0000000
--- a/Nefarius.Peripherals.SerialPort/Win32PInvoke/OVERLAPPED.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Nefarius.Peripherals.SerialPort.Win32PInvoke
-{
- [StructLayout(LayoutKind.Sequential)]
- internal struct OVERLAPPED
- {
- internal UIntPtr Internal;
- internal UIntPtr InternalHigh;
- internal UInt32 Offset;
- internal UInt32 OffsetHigh;
- internal IntPtr hEvent;
- }
-}
\ No newline at end of file
diff --git a/Nefarius.Peripherals.SerialPort/Win32PInvoke/Win32Com.cs b/Nefarius.Peripherals.SerialPort/Win32PInvoke/Win32Com.cs
deleted file mode 100644
index cd2cbb7..0000000
--- a/Nefarius.Peripherals.SerialPort/Win32PInvoke/Win32Com.cs
+++ /dev/null
@@ -1,155 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Nefarius.Peripherals.SerialPort.Win32PInvoke
-{
- internal class Win32Com
- {
- ///
- /// Opening Testing and Closing the Port Handle.
- ///
- [DllImport("kernel32.dll", SetLastError = true)]
- internal static extern IntPtr CreateFile(String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode,
- IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes,
- IntPtr hTemplateFile);
-
- //Constants for errors:
- internal const UInt32 ERROR_FILE_NOT_FOUND = 2;
- internal const UInt32 ERROR_INVALID_NAME = 123;
- internal const UInt32 ERROR_ACCESS_DENIED = 5;
- internal const UInt32 ERROR_IO_PENDING = 997;
-
- //Constants for return value:
- internal const Int32 INVALID_HANDLE_VALUE = -1;
-
- //Constants for dwFlagsAndAttributes:
- internal const UInt32 FILE_FLAG_OVERLAPPED = 0x40000000;
-
- //Constants for dwCreationDisposition:
- internal const UInt32 OPEN_EXISTING = 3;
-
- //Constants for dwDesiredAccess:
- internal const UInt32 GENERIC_READ = 0x80000000;
- internal const UInt32 GENERIC_WRITE = 0x40000000;
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean CloseHandle(IntPtr hObject);
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean GetHandleInformation(IntPtr hObject, out UInt32 lpdwFlags);
-
-
- ///
- /// Manipulating the communications settings.
- ///
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean GetCommState(IntPtr hFile, ref DCB lpDCB);
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean GetCommTimeouts(IntPtr hFile, out COMMTIMEOUTS lpCommTimeouts);
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean BuildCommDCBAndTimeouts(String lpDef, ref DCB lpDCB, ref COMMTIMEOUTS lpCommTimeouts);
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean SetCommState(IntPtr hFile, [In] ref DCB lpDCB);
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean SetCommTimeouts(IntPtr hFile, [In] ref COMMTIMEOUTS lpCommTimeouts);
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean SetupComm(IntPtr hFile, UInt32 dwInQueue, UInt32 dwOutQueue);
-
- ///
- /// Reading and writing.
- ///
- [DllImport("kernel32.dll", SetLastError = true)]
- internal static extern Boolean WriteFile(IntPtr fFile, Byte[] lpBuffer, UInt32 nNumberOfBytesToWrite,
- out UInt32 lpNumberOfBytesWritten, IntPtr lpOverlapped);
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean SetCommMask(IntPtr hFile, UInt32 dwEvtMask);
-
- // Constants for dwEvtMask:
- internal const UInt32 EV_RXCHAR = 0x0001;
- internal const UInt32 EV_RXFLAG = 0x0002;
- internal const UInt32 EV_TXEMPTY = 0x0004;
- internal const UInt32 EV_CTS = 0x0008;
- internal const UInt32 EV_DSR = 0x0010;
- internal const UInt32 EV_RLSD = 0x0020;
- internal const UInt32 EV_BREAK = 0x0040;
- internal const UInt32 EV_ERR = 0x0080;
- internal const UInt32 EV_RING = 0x0100;
- internal const UInt32 EV_PERR = 0x0200;
- internal const UInt32 EV_RX80FULL = 0x0400;
- internal const UInt32 EV_EVENT1 = 0x0800;
- internal const UInt32 EV_EVENT2 = 0x1000;
-
- [DllImport("kernel32.dll", SetLastError = true)]
- internal static extern Boolean WaitCommEvent(IntPtr hFile, IntPtr lpEvtMask, IntPtr lpOverlapped);
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean CancelIo(IntPtr hFile);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- internal static extern Boolean ReadFile(IntPtr hFile, [Out] Byte[] lpBuffer, UInt32 nNumberOfBytesToRead,
- out UInt32 nNumberOfBytesRead, IntPtr lpOverlapped);
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean TransmitCommChar(IntPtr hFile, Byte cChar);
-
- ///
- /// Control port functions.
- ///
- [DllImport("kernel32.dll")]
- internal static extern Boolean EscapeCommFunction(IntPtr hFile, UInt32 dwFunc);
-
- // Constants for dwFunc:
- internal const UInt32 SETXOFF = 1;
- internal const UInt32 SETXON = 2;
- internal const UInt32 SETRTS = 3;
- internal const UInt32 CLRRTS = 4;
- internal const UInt32 SETDTR = 5;
- internal const UInt32 CLRDTR = 6;
- internal const UInt32 RESETDEV = 7;
- internal const UInt32 SETBREAK = 8;
- internal const UInt32 CLRBREAK = 9;
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean GetCommModemStatus(IntPtr hFile, out UInt32 lpModemStat);
-
- // Constants for lpModemStat:
- internal const UInt32 MS_CTS_ON = 0x0010;
- internal const UInt32 MS_DSR_ON = 0x0020;
- internal const UInt32 MS_RING_ON = 0x0040;
- internal const UInt32 MS_RLSD_ON = 0x0080;
-
- ///
- /// Status Functions.
- ///
- [DllImport("kernel32.dll", SetLastError = true)]
- internal static extern Boolean GetOverlappedResult(IntPtr hFile, IntPtr lpOverlapped,
- out UInt32 nNumberOfBytesTransferred, Boolean bWait);
-
- [DllImport("kernel32.dll")]
- internal static extern Boolean ClearCommError(IntPtr hFile, out UInt32 lpErrors, IntPtr lpStat);
- [DllImport("kernel32.dll")]
- internal static extern Boolean ClearCommError(IntPtr hFile, out UInt32 lpErrors, out COMSTAT cs);
-
- //Constants for lpErrors:
- internal const UInt32 CE_RXOVER = 0x0001;
- internal const UInt32 CE_OVERRUN = 0x0002;
- internal const UInt32 CE_RXPARITY = 0x0004;
- internal const UInt32 CE_FRAME = 0x0008;
- internal const UInt32 CE_BREAK = 0x0010;
- internal const UInt32 CE_TXFULL = 0x0100;
- internal const UInt32 CE_PTO = 0x0200;
- internal const UInt32 CE_IOE = 0x0400;
- internal const UInt32 CE_DNS = 0x0800;
- internal const UInt32 CE_OOP = 0x1000;
- internal const UInt32 CE_MODE = 0x8000;
- [DllImport("kernel32.dll")]
- internal static extern Boolean GetCommProperties(IntPtr hFile, out COMMPROP cp);
- }
-}
\ No newline at end of file