@ -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;
/// <summary>
/// Wrapper class around a serial (COM, RS-232) port.
/// </summary>
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public partial class SerialPort : IDisposable
{
private readonly ManualResetEvent _writeEvent = new ( false ) ;
@ -64,18 +69,25 @@ public partial class SerialPort : IDisposable
/// <returns>false if the port could not be opened</returns>
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_FLAG S . FILE_GENERIC_READ | FILE_ACCESS_FLAG S . FILE_GENERIC_WRITE , 0 ,
( uint ) ( FILE_ACCESS_RIGHT S . FILE_GENERIC_READ | FILE_ACCESS_RIGHT S . 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 ( ( s byte) XoffChar ) ;
portDcb . XonChar = new CHAR ( ( s 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 )
@ -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
/// <param name="reason">Description of fault</param>
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
/// <param name="toSend">Array of bytes to be sent</param>
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
/// <param name="toSend">Byte to be sent</param>
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" ) ;
}
}
/// <summary>
@ -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 ) )
{
va r 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," ) ;
StringBuilde r 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 gotb ytes ;
uint gotB ytes ;
do
{
if ( ! PInvoke . ReadFile ( _hPort , buf , 1 , & gotb ytes , & ov ) )
if ( ! PInvoke . ReadFile ( _hPort , buf , 1 , & gotB ytes , & ov ) )
{
if ( Marshal . GetLastWin32Error ( ) = = ( int ) WIN32_ERROR . ERROR_IO_PENDING )
{
Win32Com . CancelIo ( _hPort . DangerousGetHandle ( ) ) ;
gotb ytes = 0 ;
gotB ytes = 0 ;
}
else
{
@ -478,30 +553,59 @@ public partial class SerialPort : IDisposable
}
}
if ( gotb ytes = = 1 ) OnRxChar ( buf [ 0 ] ) ;
} while ( gotbytes > 0 ) ;
if ( gotB ytes = = 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 ;
}
}
}