diff --git a/PInvokeSerialPort/SerialPort.cs b/PInvokeSerialPort/SerialPort.cs index fcfc0d0..7f5de2c 100755 --- a/PInvokeSerialPort/SerialPort.cs +++ b/PInvokeSerialPort/SerialPort.cs @@ -7,27 +7,144 @@ using PInvokeSerialPort.Win32PInvoke; namespace PInvokeSerialPort { /// - /// PInvokeSerialPort main class. - /// Borrowed from http://msdn.microsoft.com/en-us/magazine/cc301786.aspx ;) + /// PInvokeSerialPort main class. + /// Borrowed from http://msdn.microsoft.com/en-us/magazine/cc301786.aspx ;) /// public class SerialPort : IDisposable { - private IntPtr _hPort; - private IntPtr _ptrUwo = IntPtr.Zero; - private Thread _rxThread; - private bool _online; + private readonly ManualResetEvent _writeEvent = new ManualResetEvent(false); private bool _auto; private bool _checkSends = true; + + private Handshake _handShake; + private IntPtr _hPort; + private bool _online; + private IntPtr _ptrUwo = IntPtr.Zero; private Exception _rxException; private bool _rxExceptionReported; - private int _writeCount; - private readonly ManualResetEvent _writeEvent = new ManualResetEvent(false); - private int _stateRts = 2; - private int _stateDtr = 2; + private Thread _rxThread; private int _stateBrk = 2; + private int _stateDtr = 2; + private int _stateRts = 2; + private int _writeCount; /// - /// Class contructor + /// 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; + + /// + /// Baud Rate (default: 2400) unsupported rates will throw "Bad settings" + /// + public int BaudRate = 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 = true; + + /// + /// Number of databits 1..8 (default: 8) unsupported values will throw "Bad settings" + /// + public int DataBits = 8; + + /// + /// The parity checking scheme (default: none) + /// + public Parity Parity = Parity.None; + + /// + /// If true, Xon and Xoff characters are sent to control the data flow from the remote station (default: false) + /// + public bool RxFlowX; + + /// + /// If true, received characters are ignored unless DSR is asserted by the remote station (default: false) + /// + public bool RxGateDsr; + + /// + /// The number of free bytes in the reception queue at which flow is disabled (default: 2048) + /// + public int RxHighWater = 2048; + + /// + /// The number of bytes in the reception queue at which flow is re-enabled (default: 512) + /// + public int RxLowWater = 512; + + /// + /// Requested size for receive queue (default: 0 = use operating system default) + /// + public int RxQueue; + + /// + /// Constant. Max time for Send in ms = (Multiplier * Characters) + Constant (default: 0) + /// + public int SendTimeoutConstant; + + /// + /// Multiplier. Max time for Send in ms = (Multiplier * Characters) + Constant + /// (default: 0 = No timeout) + /// + public int SendTimeoutMultiplier; + + /// + /// Number of stop bits (default: one) + /// + public StopBits StopBits = StopBits.One; + + /// + /// If true, transmission is halted unless CTS is asserted by the remote station (default: false) + /// + public bool TxFlowCts; + + /// + /// If true, transmission is halted unless DSR is asserted by the remote station (default: false) + /// + public bool TxFlowDsr; + + /// + /// If true, transmission is halted when Xoff is received and restarted when Xon is received (default: false) + /// + public bool TxFlowX; + + /// + /// Requested size for transmit queue (default: 0 = use operating system default) + /// + public int TxQueue; + + /// + /// 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 = true; + + /// + /// Specidies the use to which the DTR output is put (default: none) + /// + public HsOutput UseDtr = HsOutput.None; + + /// + /// Specifies the use to which the RTS output is put (default: none) + /// + public HsOutput UseRts = HsOutput.None; + + /// + /// The character used to signal Xoff for X flow control (default: DC3) + /// + public ASCII XoffChar = ASCII.DC3; + + /// + /// The character used to signal Xon for X flow control (default: DC1) + /// + public ASCII XonChar = ASCII.DC1; + + /// + /// Class contructor /// public SerialPort(string portName) { @@ -35,7 +152,7 @@ namespace PInvokeSerialPort } /// - /// Class contructor + /// Class contructor /// public SerialPort(string portName, int baudRate) { @@ -44,293 +161,17 @@ namespace PInvokeSerialPort } /// - /// Opens the com port and configures it with the required settings + /// True if online. /// - /// 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; - if (UseDtr == HsOutput.None) _stateDtr = 0; - if (UseDtr == HsOutput.Online) _stateDtr = 1; - if (UseRts == HsOutput.None) _stateRts = 0; - if (UseRts == HsOutput.Online) _stateRts = 1; - - _checkSends = CheckAllSends; - wo.Offset = 0; - wo.OffsetHigh = 0; - wo.hEvent = _checkSends ? _writeEvent.Handle : IntPtr.Zero; - _ptrUwo = Marshal.AllocHGlobal(Marshal.SizeOf(wo)); - Marshal.StructureToPtr(wo, _ptrUwo, true); - _writeCount = 0; - - _rxException = null; - _rxExceptionReported = false; - _rxThread = new Thread(ReceiveThread) - { - Name = "CommBaseRx", - Priority = ThreadPriority.AboveNormal - }; - //If not set to true, my application process will not exit completely after UI closed - _rxThread.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; - } + public bool Online => _online && CheckOnline(); /// - /// Closes the com port. + /// True if the RTS pin is controllable via the RTS property /// - 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; - } + protected bool RtSavailable => _stateRts < 2; /// - /// For IDisposable - /// - public void Dispose() { Close(); } - - /// - /// Destructor (just in case) - /// - ~SerialPort() { Close(); } - - /// - /// True if online. - /// - public bool Online { get { return _online && CheckOnline(); } } - - /// - /// 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"); - } - - /// - /// Delay processing. - /// - /// Milliseconds to delay by - protected void Sleep(int milliseconds) - { - Thread.Sleep(milliseconds); - } - - /// - /// 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); - } - - /// - /// True if the RTS pin is controllable via the RTS property - /// - protected bool RtSavailable { get { return (_stateRts < 2); } } - - /// - /// Set the state of the RTS modem control output + /// Set the state of the RTS modem control output /// protected bool Rts { @@ -353,19 +194,16 @@ namespace PInvokeSerialPort ThrowException("Unexpected Failure"); } } - get - { - return (_stateRts == 1); - } + get => _stateRts == 1; } /// - /// True if the DTR pin is controllable via the DTR property + /// True if the DTR pin is controllable via the DTR property /// - protected bool DtrAvailable { get { return (_stateDtr < 2); } } + protected bool DtrAvailable => _stateDtr < 2; /// - /// The state of the DTR modem control output + /// The state of the DTR modem control output /// protected bool Dtr { @@ -388,14 +226,11 @@ namespace PInvokeSerialPort ThrowException("Unexpected Failure"); } } - get - { - return (_stateDtr == 1); - } + get => _stateDtr == 1; } /// - /// Assert or remove a break condition from the transmission line + /// Assert or remove a break condition from the transmission line /// protected bool Break { @@ -418,28 +253,368 @@ namespace PInvokeSerialPort ThrowException("Unexpected Failure"); } } - get + get => _stateBrk == 1; + } + + + /// + /// Port Name + /// + public string PortName { get; set; } + + public Handshake Handshake + { + get => _handShake; + set { - return (_stateBrk == 1); + _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; + } } } /// - /// Override this to provide processing after the port is openned (i.e. to configure remote - /// device or just check presence). + /// 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; + if (UseDtr == HsOutput.None) _stateDtr = 0; + if (UseDtr == HsOutput.Online) _stateDtr = 1; + if (UseRts == HsOutput.None) _stateRts = 0; + if (UseRts == HsOutput.Online) _stateRts = 1; + + _checkSends = CheckAllSends; + wo.Offset = 0; + wo.OffsetHigh = 0; + wo.hEvent = _checkSends ? _writeEvent.Handle : IntPtr.Zero; + _ptrUwo = Marshal.AllocHGlobal(Marshal.SizeOf(wo)); + Marshal.StructureToPtr(wo, _ptrUwo, true); + _writeCount = 0; + + _rxException = null; + _rxExceptionReported = false; + _rxThread = new Thread(ReceiveThread) + { + Name = "CommBaseRx", + Priority = ThreadPriority.AboveNormal + }; + //If not set to true, my application process will not exit completely after UI closed + _rxThread.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"); + } + + /// + /// Delay processing. + /// + /// Milliseconds to delay by + protected void Sleep(int milliseconds) + { + Thread.Sleep(milliseconds); + } + + /// + /// 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 openned (i.e. to configure remote + /// device or just check presence). /// /// false to close the port again - protected virtual bool AfterOpen() { return true; } + protected virtual bool AfterOpen() + { + return true; + } /// - /// Override this to provide processing prior to port closure. + /// Override this to provide processing prior to port closure. /// /// True if closing due to an error - protected virtual void BeforeClose(bool error) { } + protected virtual void BeforeClose(bool error) + { + } public event Action DataReceived; + /// - /// Override this to process received bytes. + /// Override this to process received bytes. /// /// The byte that was received protected void OnRxChar(byte ch) @@ -449,42 +624,53 @@ namespace PInvokeSerialPort } /// - /// Override this to take action when transmission is complete (i.e. all bytes have actually - /// been sent, not just queued). + /// Override this to take action when transmission is complete (i.e. all bytes have actually + /// been sent, not just queued). /// - protected virtual void OnTxDone() { } + protected virtual void OnTxDone() + { + } /// - /// Override this to take action when a break condition is detected on the input line. + /// Override this to take action when a break condition is detected on the input line. /// - protected virtual void OnBreak() { } + protected virtual void OnBreak() + { + } /// - /// Override this to take action when a ring condition is signalled by an attached modem. + /// Override this to take action when a ring condition is signalled by an attached modem. /// - protected virtual void OnRing() { } + protected virtual void OnRing() + { + } /// - /// Override this to take action when one or more modem status inputs change state + /// 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) { } + protected virtual void OnStatusChange(ModemStatus mask, ModemStatus state) + { + } /// - /// Override this to take action when the reception thread closes due to an exception being thrown. + /// 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) { } + protected virtual void OnRxException(Exception e) + { + } private void ReceiveThread() { - var buf = new Byte[1]; + 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.Offset = 0; + ov.OffsetHigh = 0; ov.hEvent = sg.Handle; Marshal.StructureToPtr(ov, unmanagedOv, true); @@ -495,27 +681,23 @@ namespace PInvokeSerialPort { while (true) { - if (!Win32Com.SetCommMask(_hPort, Win32Com.EV_RXCHAR | Win32Com.EV_TXEMPTY | Win32Com.EV_CTS | Win32Com.EV_DSR + 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() == Win32Com.ERROR_IO_PENDING) - { sg.WaitOne(); - } else - { throw new CommPortException("IO Error [002]"); - } } - eventMask = (uint)Marshal.ReadInt32(uMask); + + eventMask = (uint) Marshal.ReadInt32(uMask); if ((eventMask & Win32Com.EV_ERR) != 0) { - UInt32 errs; + uint errs; if (Win32Com.ClearCommError(_hPort, out errs, IntPtr.Zero)) { var s = new StringBuilder("UART Error: ", 40); @@ -528,8 +710,10 @@ namespace PInvokeSerialPort s.Length = s.Length - 1; throw new CommPortException(s.ToString()); } + throw new CommPortException("IO Error [003]"); } + if ((eventMask & Win32Com.EV_RXCHAR) != 0) { uint gotbytes; @@ -547,14 +731,12 @@ namespace PInvokeSerialPort 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_TXEMPTY) != 0) OnTxDone(); if ((eventMask & Win32Com.EV_BREAK) != 0) OnBreak(); uint i = 0; @@ -584,11 +766,12 @@ namespace PInvokeSerialPort private bool CheckOnline() { - if ((_rxException != null) && (!_rxExceptionReported)) + if (_rxException != null && !_rxExceptionReported) { _rxExceptionReported = true; ThrowException("rx"); } + if (_online) { uint f; @@ -596,147 +779,12 @@ namespace PInvokeSerialPort ThrowException("Offline"); return false; } + if (_auto) - { - if (Open()) return true; - } + if (Open()) + return true; ThrowException("Offline"); return false; } - - - - /// - /// Port Name - /// - public string PortName { get; set; } - /// - /// Baud Rate (default: 2400) unsupported rates will throw "Bad settings" - /// - public int BaudRate = 115200; - /// - /// The parity checking scheme (default: none) - /// - public Parity Parity = Parity.None; - /// - /// Number of databits 1..8 (default: 8) unsupported values will throw "Bad settings" - /// - public int DataBits = 8; - /// - /// Number of stop bits (default: one) - /// - public StopBits StopBits = StopBits.One; - /// - /// If true, transmission is halted unless CTS is asserted by the remote station (default: false) - /// - public bool TxFlowCts; - /// - /// If true, transmission is halted unless DSR is asserted by the remote station (default: false) - /// - public bool TxFlowDsr; - /// - /// If true, transmission is halted when Xoff is received and restarted when Xon is received (default: false) - /// - public bool TxFlowX; - /// - /// 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 = true; - /// - /// If true, received characters are ignored unless DSR is asserted by the remote station (default: false) - /// - public bool RxGateDsr; - /// - /// If true, Xon and Xoff characters are sent to control the data flow from the remote station (default: false) - /// - public bool RxFlowX; - /// - /// Specifies the use to which the RTS output is put (default: none) - /// - public HsOutput UseRts = HsOutput.None; - /// - /// Specidies the use to which the DTR output is put (default: none) - /// - public HsOutput UseDtr = HsOutput.None; - /// - /// The character used to signal Xon for X flow control (default: DC1) - /// - public ASCII XonChar = ASCII.DC1; - /// - /// The character used to signal Xoff for X flow control (default: DC3) - /// - public ASCII XoffChar = ASCII.DC3; - /// - /// The number of free bytes in the reception queue at which flow is disabled (default: 2048) - /// - public int RxHighWater = 2048; - /// - /// The number of bytes in the reception queue at which flow is re-enabled (default: 512) - /// - public int RxLowWater = 512; - /// - /// Multiplier. Max time for Send in ms = (Multiplier * Characters) + Constant - /// (default: 0 = No timeout) - /// - public int SendTimeoutMultiplier; - /// - /// Constant. Max time for Send in ms = (Multiplier * Characters) + Constant (default: 0) - /// - public int SendTimeoutConstant; - /// - /// Requested size for receive queue (default: 0 = use operating system default) - /// - public int RxQueue; - /// - /// Requested size for transmit queue (default: 0 = use operating system default) - /// - public int TxQueue; - /// - /// 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; - - /// - /// 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 = true; - - private Handshake _handShake; - public Handshake Handshake - { - get { return _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; - } - } - } } } \ No newline at end of file