Merge pull request #1 from nefarius/nefarius/feature/refactoring
Migration to CsWin32
This commit is contained in:
commit
5b8daef830
8
PInvokeSerialPort.sln → Nefarius.Peripherals.SerialPort.sln
Executable file → Normal file
8
PInvokeSerialPort.sln → Nefarius.Peripherals.SerialPort.sln
Executable file → Normal file
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 16
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 16.0.29613.14
|
VisualStudioVersion = 17.3.32825.248
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PInvokeSerialPort", "PInvokeSerialPort\PInvokeSerialPort.csproj", "{AEC711A5-AA9B-4127-A82C-C4D8FDA9741A}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nefarius.Peripherals.SerialPort", "Nefarius.Peripherals.SerialPort\Nefarius.Peripherals.SerialPort.csproj", "{AEC711A5-AA9B-4127-A82C-C4D8FDA9741A}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{99052083-B245-462F-8778-4C94662ABABE}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{99052083-B245-462F-8778-4C94662ABABE}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
@ -47,7 +47,6 @@ Global
|
|||||||
{928609B4-70AB-4D93-A43E-4BE75C279066}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{928609B4-70AB-4D93-A43E-4BE75C279066}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{928609B4-70AB-4D93-A43E-4BE75C279066}.Release|Any CPU.Build.0 = Release|Any CPU
|
{928609B4-70AB-4D93-A43E-4BE75C279066}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{928609B4-70AB-4D93-A43E-4BE75C279066}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
{928609B4-70AB-4D93-A43E-4BE75C279066}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
{928609B4-70AB-4D93-A43E-4BE75C279066}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
|
||||||
{928609B4-70AB-4D93-A43E-4BE75C279066}.Release|x86.ActiveCfg = Release|Any CPU
|
{928609B4-70AB-4D93-A43E-4BE75C279066}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Debug|Any CPU.ActiveCfg = Debug|x86
|
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Debug|Any CPU.ActiveCfg = Debug|x86
|
||||||
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
|
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
|
||||||
@ -56,7 +55,6 @@ Global
|
|||||||
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Debug|x86.Build.0 = Debug|x86
|
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Debug|x86.Build.0 = Debug|x86
|
||||||
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Release|Any CPU.ActiveCfg = Release|x86
|
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Release|Any CPU.ActiveCfg = Release|x86
|
||||||
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Release|Mixed Platforms.ActiveCfg = Release|x86
|
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Release|Mixed Platforms.ActiveCfg = Release|x86
|
||||||
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Release|Mixed Platforms.Build.0 = Release|x86
|
|
||||||
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Release|x86.ActiveCfg = Release|x86
|
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Release|x86.ActiveCfg = Release|x86
|
||||||
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Release|x86.Build.0 = Release|x86
|
{76FAB402-7515-4A9B-8605-4FEC0736C78A}.Release|x86.Build.0 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
41
Nefarius.Peripherals.SerialPort/ASCII.cs
Normal file
41
Nefarius.Peripherals.SerialPort/ASCII.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
namespace Nefarius.Peripherals.SerialPort;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Byte type with enumeration constants for ASCII control codes.
|
||||||
|
/// </summary>
|
||||||
|
public enum ASCII : byte
|
||||||
|
{
|
||||||
|
NULL = 0x00,
|
||||||
|
SOH = 0x01,
|
||||||
|
STH = 0x02,
|
||||||
|
ETX = 0x03,
|
||||||
|
EOT = 0x04,
|
||||||
|
ENQ = 0x05,
|
||||||
|
ACK = 0x06,
|
||||||
|
BELL = 0x07,
|
||||||
|
BS = 0x08,
|
||||||
|
HT = 0x09,
|
||||||
|
LF = 0x0A,
|
||||||
|
VT = 0x0B,
|
||||||
|
FF = 0x0C,
|
||||||
|
CR = 0x0D,
|
||||||
|
SO = 0x0E,
|
||||||
|
SI = 0x0F,
|
||||||
|
DC1 = 0x11,
|
||||||
|
DC2 = 0x12,
|
||||||
|
DC3 = 0x13,
|
||||||
|
DC4 = 0x14,
|
||||||
|
NAK = 0x15,
|
||||||
|
SYN = 0x16,
|
||||||
|
ETB = 0x17,
|
||||||
|
CAN = 0x18,
|
||||||
|
EM = 0x19,
|
||||||
|
SUB = 0x1A,
|
||||||
|
ESC = 0x1B,
|
||||||
|
FS = 0x1C,
|
||||||
|
GS = 0x1D,
|
||||||
|
RS = 0x1E,
|
||||||
|
US = 0x1F,
|
||||||
|
SP = 0x20,
|
||||||
|
DEL = 0x7F
|
||||||
|
}
|
25
Nefarius.Peripherals.SerialPort/CommPortException.cs
Normal file
25
Nefarius.Peripherals.SerialPort/CommPortException.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Nefarius.Peripherals.SerialPort;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exception used for all errors.
|
||||||
|
/// </summary>
|
||||||
|
public class CommPortException : ApplicationException
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for raising direct exceptions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="desc">Description of error</param>
|
||||||
|
public CommPortException(string desc) : base(desc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for re-raising exceptions from receive thread
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Inner exception raised on receive thread</param>
|
||||||
|
public CommPortException(Exception e) : base("Receive Thread Exception", e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
27
Nefarius.Peripherals.SerialPort/Handshake.cs
Normal file
27
Nefarius.Peripherals.SerialPort/Handshake.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace Nefarius.Peripherals.SerialPort;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Standard handshake methods
|
||||||
|
/// </summary>
|
||||||
|
public enum Handshake
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No handshaking
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Software handshaking using Xon / Xoff
|
||||||
|
/// </summary>
|
||||||
|
XonXoff,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hardware handshaking using CTS / RTS
|
||||||
|
/// </summary>
|
||||||
|
CtsRts,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hardware handshaking using DSR / DTR
|
||||||
|
/// </summary>
|
||||||
|
DsrDtr
|
||||||
|
}
|
27
Nefarius.Peripherals.SerialPort/HsOutput.cs
Normal file
27
Nefarius.Peripherals.SerialPort/HsOutput.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace Nefarius.Peripherals.SerialPort;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uses for RTS or DTR pins
|
||||||
|
/// </summary>
|
||||||
|
public enum HsOutput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Pin is asserted when this station is able to receive data.
|
||||||
|
/// </summary>
|
||||||
|
Handshake = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pin is asserted when this station is transmitting data (RTS on NT, 2000 or XP only).
|
||||||
|
/// </summary>
|
||||||
|
Gate = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pin is asserted when this station is online (port is open).
|
||||||
|
/// </summary>
|
||||||
|
Online = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pin is never asserted.
|
||||||
|
/// </summary>
|
||||||
|
None = 0
|
||||||
|
}
|
36
Nefarius.Peripherals.SerialPort/ModemStatus.cs
Normal file
36
Nefarius.Peripherals.SerialPort/ModemStatus.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using Windows.Win32.Devices.Communication;
|
||||||
|
|
||||||
|
namespace Nefarius.Peripherals.SerialPort;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the status of the modem control input signals.
|
||||||
|
/// </summary>
|
||||||
|
public readonly struct ModemStatus
|
||||||
|
{
|
||||||
|
private readonly uint _status;
|
||||||
|
|
||||||
|
internal ModemStatus(uint val)
|
||||||
|
{
|
||||||
|
_status = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Condition of the Clear To Send signal.
|
||||||
|
/// </summary>
|
||||||
|
public bool Cts => (_status & (uint)MODEM_STATUS_FLAGS.MS_CTS_ON) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Condition of the Data Set Ready signal.
|
||||||
|
/// </summary>
|
||||||
|
public bool Dsr => (_status & (uint)MODEM_STATUS_FLAGS.MS_DSR_ON) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Condition of the Receive Line Status Detection signal.
|
||||||
|
/// </summary>
|
||||||
|
public bool Rlsd => (_status & (uint)MODEM_STATUS_FLAGS.MS_RLSD_ON) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Condition of the Ring Detection signal.
|
||||||
|
/// </summary>
|
||||||
|
public bool Ring => (_status & (uint)MODEM_STATUS_FLAGS.MS_RING_ON) != 0;
|
||||||
|
}
|
17
Nefarius.Peripherals.SerialPort/NativeMethods.txt
Normal file
17
Nefarius.Peripherals.SerialPort/NativeMethods.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
CreateFile
|
||||||
|
GetHandleInformation
|
||||||
|
SetCommState
|
||||||
|
SetCommTimeouts
|
||||||
|
SetupComm
|
||||||
|
WriteFile
|
||||||
|
SetCommMask
|
||||||
|
WaitCommEvent
|
||||||
|
CancelIo
|
||||||
|
ReadFile
|
||||||
|
TransmitCommChar
|
||||||
|
EscapeCommFunction
|
||||||
|
GetCommModemStatus
|
||||||
|
GetOverlappedResult
|
||||||
|
ClearCommError
|
||||||
|
GetCommProperties
|
||||||
|
WIN32_ERROR
|
24
PInvokeSerialPort/PInvokeSerialPort.csproj → Nefarius.Peripherals.SerialPort/Nefarius.Peripherals.SerialPort.csproj
Executable file → Normal file
24
PInvokeSerialPort/PInvokeSerialPort.csproj → Nefarius.Peripherals.SerialPort/Nefarius.Peripherals.SerialPort.csproj
Executable file → Normal file
@ -3,16 +3,36 @@
|
|||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<Authors>Ebrahim Byagowi, Benjamin Höglinger-Stelzer</Authors>
|
<Authors>Ebrahim Byagowi, Benjamin Höglinger-Stelzer</Authors>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<PackageProjectUrl>https://github.com/nefarius/PInvokeSerialPort</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/nefarius/PInvokeSerialPort</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/nefarius/PInvokeSerialPort</RepositoryUrl>
|
<RepositoryUrl>https://github.com/nefarius/PInvokeSerialPort</RepositoryUrl>
|
||||||
<PackageId>Nefarius.PInvokeSerialPort</PackageId>
|
<PackageId>Nefarius.Peripherals.SerialPort</PackageId>
|
||||||
<Description>P/Invoke wrapper for Win32API serial port</Description>
|
<Description>P/Invoke wrapper for Win32API serial port</Description>
|
||||||
<Copyright>Copyright 2012-2017 Ebrahim Byagowi, 2018 Benjamin Höglinger-Stelzer</Copyright>
|
<Copyright>Copyright 2012-2017 Ebrahim Byagowi, 2018-2022 Benjamin Höglinger-Stelzer</Copyright>
|
||||||
<PackageIconUrl>https://raw.githubusercontent.com/Nefarius/PInvokeSerialPort/master/ProjectIcon.png</PackageIconUrl>
|
<PackageIconUrl>https://raw.githubusercontent.com/Nefarius/PInvokeSerialPort/master/ProjectIcon.png</PackageIconUrl>
|
||||||
<PackageLicenseUrl>https://raw.githubusercontent.com/nefarius/PInvokeSerialPort/master/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://raw.githubusercontent.com/nefarius/PInvokeSerialPort/master/LICENSE</PackageLicenseUrl>
|
||||||
<Version>1.0.0</Version>
|
<Version>1.0.0</Version>
|
||||||
|
<OutputPath>$(SolutionDir)bin\</OutputPath>
|
||||||
|
<PackageIcon>NSS-128x128.png</PackageIcon>
|
||||||
|
<PackageIconUrl />
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Properties\" />
|
<Folder Include="Properties\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\assets\NSS-128x128.png">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath></PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" />
|
||||||
|
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.63-beta">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
2
PInvokeSerialPort/Parity.cs → Nefarius.Peripherals.SerialPort/Parity.cs
Executable file → Normal file
2
PInvokeSerialPort/Parity.cs → Nefarius.Peripherals.SerialPort/Parity.cs
Executable file → Normal file
@ -1,4 +1,4 @@
|
|||||||
namespace PInvokeSerialPort
|
namespace Nefarius.Peripherals.SerialPort
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parity settings
|
/// Parity settings
|
4
PInvokeSerialPort/QueueStatus.cs → Nefarius.Peripherals.SerialPort/QueueStatus.cs
Executable file → Normal file
4
PInvokeSerialPort/QueueStatus.cs → Nefarius.Peripherals.SerialPort/QueueStatus.cs
Executable file → Normal file
@ -1,7 +1,7 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using PInvokeSerialPort.Win32PInvoke;
|
using Nefarius.Peripherals.SerialPort.Win32PInvoke;
|
||||||
|
|
||||||
namespace PInvokeSerialPort
|
namespace Nefarius.Peripherals.SerialPort
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the current condition of the port queues.
|
/// Represents the current condition of the port queues.
|
870
Nefarius.Peripherals.SerialPort/SerialPort.cs
Normal file
870
Nefarius.Peripherals.SerialPort/SerialPort.cs
Normal file
@ -0,0 +1,870 @@
|
|||||||
|
using System;
|
||||||
|
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 JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace Nefarius.Peripherals.SerialPort;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PInvokeSerialPort main class.
|
||||||
|
/// Borrowed from http://msdn.microsoft.com/en-us/magazine/cc301786.aspx ;)
|
||||||
|
/// </summary>
|
||||||
|
public class SerialPort : IDisposable
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>
|
||||||
|
/// For IDisposable
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the com port and configures it with the required settings
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>false if the port could not be opened</returns>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public bool Open()
|
||||||
|
{
|
||||||
|
var portDcb = new DCB();
|
||||||
|
var commTimeouts = new COMMTIMEOUTS();
|
||||||
|
|
||||||
|
if (_online) return false;
|
||||||
|
|
||||||
|
_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
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_hPort.IsInvalid)
|
||||||
|
{
|
||||||
|
if (Marshal.GetLastWin32Error() == (int)WIN32_ERROR.ERROR_ACCESS_DENIED) return false;
|
||||||
|
throw new CommPortException("Port Open Failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
_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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
_writeOverlapped = new NativeOverlapped
|
||||||
|
{
|
||||||
|
EventHandle = _checkSends ? _writeEvent.SafeWaitHandle.DangerousGetHandle() : IntPtr.Zero
|
||||||
|
};
|
||||||
|
|
||||||
|
_writeCount = 0;
|
||||||
|
|
||||||
|
_rxException = null;
|
||||||
|
_rxExceptionReported = false;
|
||||||
|
|
||||||
|
_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes the com port.
|
||||||
|
/// </summary>
|
||||||
|
[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;
|
||||||
|
}
|
||||||
|
|
||||||
|
_hPort.Close();
|
||||||
|
|
||||||
|
_stateRts = 2;
|
||||||
|
_stateDtr = 2;
|
||||||
|
_stateBrk = 2;
|
||||||
|
_online = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destructor (just in case)
|
||||||
|
/// </summary>
|
||||||
|
~SerialPort()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Block until all bytes in the queue have been transmitted.
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public void Flush()
|
||||||
|
{
|
||||||
|
CheckOnline();
|
||||||
|
CheckResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use this to throw exceptions in derived classes. Correctly handles threading issues
|
||||||
|
/// and closes the port if necessary.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reason">Description of fault</param>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues bytes for transmission.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="toSend">Array of bytes to be sent</param>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public unsafe void Write(byte[] toSend)
|
||||||
|
{
|
||||||
|
CheckOnline();
|
||||||
|
CheckResult();
|
||||||
|
_writeCount = toSend.GetLength(0);
|
||||||
|
|
||||||
|
fixed (byte* ptr = toSend)
|
||||||
|
fixed (NativeOverlapped* overlapped = &_writeOverlapped)
|
||||||
|
{
|
||||||
|
uint sent;
|
||||||
|
if (PInvoke.WriteFile(_hPort, ptr, (uint)_writeCount, &sent, overlapped))
|
||||||
|
{
|
||||||
|
_writeCount -= (int)sent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Marshal.GetLastWin32Error() != (int)WIN32_ERROR.ERROR_IO_PENDING)
|
||||||
|
ThrowException("Unexpected failure");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues string for transmission.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="toSend">Array of bytes to be sent</param>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public void Write(string toSend)
|
||||||
|
{
|
||||||
|
Write(new ASCIIEncoding().GetBytes(toSend));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues a single byte for transmission.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="toSend">Byte to be sent</param>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public void Write(byte toSend)
|
||||||
|
{
|
||||||
|
var b = new byte[1];
|
||||||
|
b[0] = toSend;
|
||||||
|
Write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues a single char for transmission.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="toSend">Byte to be sent</param>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public void Write(char toSend)
|
||||||
|
{
|
||||||
|
Write(toSend.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues string with a new line ("\r\n") for transmission.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="toSend">Array of bytes to be sent</param>
|
||||||
|
[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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a protocol byte immediately ahead of any queued bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="toSend">Byte to send</param>
|
||||||
|
/// <returns>False if an immediate byte is already scheduled and not yet sent</returns>
|
||||||
|
public void SendImmediate(byte toSend)
|
||||||
|
{
|
||||||
|
CheckOnline();
|
||||||
|
if (!PInvoke.TransmitCommChar(_hPort, new CHAR(toSend))) ThrowException("Transmission failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the status of the modem control input signals.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Modem status object</returns>
|
||||||
|
/*protected ModemStatus GetModemStatus()
|
||||||
|
{
|
||||||
|
CheckOnline();
|
||||||
|
if (!PInvoke.GetCommModemStatus(_hPort, out var f)) ThrowException("Unexpected failure");
|
||||||
|
return new ModemStatus(f);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the status of the queues
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Queue status object</returns>
|
||||||
|
/*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);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override this to provide processing after the port is opened (i.e. to configure remote
|
||||||
|
/// device or just check presence).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>false to close the port again</returns>
|
||||||
|
protected virtual bool AfterOpen()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override this to provide processing prior to port closure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="error">True if closing due to an error</param>
|
||||||
|
protected virtual void BeforeClose(bool error)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<byte> DataReceived;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override this to process received bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ch">The byte that was received</param>
|
||||||
|
protected void OnRxChar(byte ch)
|
||||||
|
{
|
||||||
|
DataReceived?.Invoke(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override this to take action when transmission is complete (i.e. all bytes have actually
|
||||||
|
/// been sent, not just queued).
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnTxDone()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override this to take action when a break condition is detected on the input line.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnBreak()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override this to take action when a ring condition is signaled by an attached modem.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnRing()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override this to take action when one or more modem status inputs change state
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mask">The status inputs that have changed state</param>
|
||||||
|
/// <param name="state">The state of the status inputs</param>
|
||||||
|
protected virtual void OnStatusChange(ModemStatus mask, ModemStatus state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override this to take action when the reception thread closes due to an exception being thrown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">The exception which was thrown</param>
|
||||||
|
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 (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))
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CommPortException("IO Error [003]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((eventMask & COMM_EVENT_MASK.EV_RXCHAR) != 0)
|
||||||
|
{
|
||||||
|
uint gotBytes;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
fixed (byte* ptrBuffer = buf)
|
||||||
|
{
|
||||||
|
if (!PInvoke.ReadFile(_hPort, ptrBuffer, 1, &gotBytes, &ov))
|
||||||
|
{
|
||||||
|
if (Marshal.GetLastWin32Error() == (int)WIN32_ERROR.ERROR_IO_PENDING)
|
||||||
|
{
|
||||||
|
PInvoke.CancelIo(_hPort);
|
||||||
|
gotBytes = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new CommPortException("IO Error [004]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (e is not ThreadAbortException)
|
||||||
|
{
|
||||||
|
_rxException = e;
|
||||||
|
OnRxException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckOnline()
|
||||||
|
{
|
||||||
|
if (_rxException != null && !_rxExceptionReported)
|
||||||
|
{
|
||||||
|
_rxExceptionReported = true;
|
||||||
|
ThrowException("rx");
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class constructor
|
||||||
|
/// </summary>
|
||||||
|
public SerialPort(string portName)
|
||||||
|
{
|
||||||
|
PortName = portName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>
|
||||||
|
/// Class constructor
|
||||||
|
/// </summary>
|
||||||
|
public SerialPort(string portName, int baudRate) : this(portName)
|
||||||
|
{
|
||||||
|
BaudRate = baudRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, the port will automatically re-open on next send if it was previously closed due
|
||||||
|
/// to an error (default: false)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public bool AutoReopen { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Baud Rate (default: 115200)
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Unsupported rates will throw "Bad settings".</remarks>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public int BaudRate { get; set; } = 115200;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public bool CheckAllSends { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of databits 1..8 (default: 8) unsupported values will throw "Bad settings"
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public int DataBits { get; set; } = 8;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parity checking scheme (default: none)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public Parity Parity { get; set; } = Parity.None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, Xon and Xoff characters are sent to control the data flow from the remote station (default: false)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public bool RxFlowX { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, received characters are ignored unless DSR is asserted by the remote station (default: false)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public bool RxGateDsr { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of free bytes in the reception queue at which flow is disabled (default: 2048)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public int RxHighWater { get; set; } = 2048;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of bytes in the reception queue at which flow is re-enabled (default: 512)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public int RxLowWater { get; set; } = 512;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Requested size for receive queue (default: 0 = use operating system default)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public int RxQueue { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constant. Max time for Send in ms = (Multiplier * Characters) + Constant (default: 0)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public int SendTimeoutConstant { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Multiplier. Max time for Send in ms = (Multiplier * Characters) + Constant
|
||||||
|
/// (default: 0 = No timeout)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public int SendTimeoutMultiplier { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of stop bits (default: one)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public StopBits StopBits { get; set; } = StopBits.One;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, transmission is halted unless CTS is asserted by the remote station (default: false)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public bool TxFlowCts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, transmission is halted unless DSR is asserted by the remote station (default: false)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public bool TxFlowDsr { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, transmission is halted when Xoff is received and restarted when Xon is received (default: false)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public bool TxFlowX { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Requested size for transmit queue (default: 0 = use operating system default)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public int TxQueue { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public bool TxWhenRxXoff { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specidies the use to which the DTR output is put (default: none)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public HsOutput UseDtr { get; set; } = HsOutput.None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the use to which the RTS output is put (default: none)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public HsOutput UseRts { get; set; } = HsOutput.None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The character used to signal Xoff for X flow control (default: DC3)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public ASCII XoffChar { get; set; } = ASCII.DC3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The character used to signal Xon for X flow control (default: DC1)
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public ASCII XonChar { get; set; } = ASCII.DC1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if online.
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public bool Online => _online && CheckOnline();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if the RTS pin is controllable via the RTS property
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
protected bool RtSavailable => _stateRts < 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the state of the RTS modem control output
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if the DTR pin is controllable via the DTR property
|
||||||
|
/// </summary>
|
||||||
|
protected bool DtrAvailable => _stateDtr < 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The state of the DTR modem control output
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assert or remove a break condition from the transmission line
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Port Name
|
||||||
|
/// </summary>
|
||||||
|
[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
|
||||||
|
}
|
2
PInvokeSerialPort/StopBits.cs → Nefarius.Peripherals.SerialPort/StopBits.cs
Executable file → Normal file
2
PInvokeSerialPort/StopBits.cs → Nefarius.Peripherals.SerialPort/StopBits.cs
Executable file → Normal file
@ -1,4 +1,4 @@
|
|||||||
namespace PInvokeSerialPort
|
namespace Nefarius.Peripherals.SerialPort
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stop bit settings
|
/// Stop bit settings
|
23
Nefarius.Peripherals.SerialPort/Util.cs
Normal file
23
Nefarius.Peripherals.SerialPort/Util.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
19
Nefarius.Peripherals.SerialPort/Win32PInvoke/COMSTAT.cs
Normal file
19
Nefarius.Peripherals.SerialPort/Win32PInvoke/COMSTAT.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Nefarius.Peripherals.SerialPort.Win32PInvoke;
|
||||||
|
|
||||||
|
[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;
|
||||||
|
}
|
@ -50,7 +50,7 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\PInvokeSerialPort\PInvokeSerialPort.csproj">
|
<ProjectReference Include="..\Nefarius.Peripherals.SerialPort\Nefarius.Peripherals.SerialPort.csproj">
|
||||||
<Project>{AEC711A5-AA9B-4127-A82C-C4D8FDA9741A}</Project>
|
<Project>{AEC711A5-AA9B-4127-A82C-C4D8FDA9741A}</Project>
|
||||||
<Name>PInvokeSerialPort</Name>
|
<Name>PInvokeSerialPort</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
<Compile Include="PInvokeSerialPortTest.cs" />
|
<Compile Include="PInvokeSerialPortTest.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\PInvokeSerialPort\PInvokeSerialPort.csproj">
|
<ProjectReference Include="..\Nefarius.Peripherals.SerialPort\Nefarius.Peripherals.SerialPort.csproj">
|
||||||
<Project>{AEC711A5-AA9B-4127-A82C-C4D8FDA9741A}</Project>
|
<Project>{AEC711A5-AA9B-4127-A82C-C4D8FDA9741A}</Project>
|
||||||
<Name>PInvokeSerialPort</Name>
|
<Name>PInvokeSerialPort</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO.Ports;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
namespace PInvokeSerialPort
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Byte type with enumeration constants for ASCII control codes.
|
|
||||||
/// </summary>
|
|
||||||
public enum ASCII : byte
|
|
||||||
{
|
|
||||||
NULL = 0x00, SOH = 0x01, STH = 0x02, ETX = 0x03, EOT = 0x04, ENQ = 0x05, ACK = 0x06, BELL = 0x07,
|
|
||||||
BS = 0x08, HT = 0x09, LF = 0x0A, VT = 0x0B, FF = 0x0C, CR = 0x0D, SO = 0x0E, SI = 0x0F, DC1 = 0x11,
|
|
||||||
DC2 = 0x12, DC3 = 0x13, DC4 = 0x14, NAK = 0x15, SYN = 0x16, ETB = 0x17, CAN = 0x18, EM = 0x19,
|
|
||||||
SUB = 0x1A, ESC = 0x1B, FS = 0x1C, GS = 0x1D, RS = 0x1E, US = 0x1F, SP = 0x20, DEL = 0x7F
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace PInvokeSerialPort
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Exception used for all errors.
|
|
||||||
/// </summary>
|
|
||||||
public class CommPortException : ApplicationException
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor for raising direct exceptions
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="desc">Description of error</param>
|
|
||||||
public CommPortException(string desc) : base(desc) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor for re-raising exceptions from receive thread
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="e">Inner exception raised on receive thread</param>
|
|
||||||
public CommPortException(Exception e) : base("Receive Thread Exception", e) { }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
namespace PInvokeSerialPort
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Standard handshake methods
|
|
||||||
/// </summary>
|
|
||||||
public enum Handshake
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// No handshaking
|
|
||||||
/// </summary>
|
|
||||||
None,
|
|
||||||
/// <summary>
|
|
||||||
/// Software handshaking using Xon / Xoff
|
|
||||||
/// </summary>
|
|
||||||
XonXoff,
|
|
||||||
/// <summary>
|
|
||||||
/// Hardware handshaking using CTS / RTS
|
|
||||||
/// </summary>
|
|
||||||
CtsRts,
|
|
||||||
/// <summary>
|
|
||||||
/// Hardware handshaking using DSR / DTR
|
|
||||||
/// </summary>
|
|
||||||
DsrDtr
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
namespace PInvokeSerialPort
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Uses for RTS or DTR pins
|
|
||||||
/// </summary>
|
|
||||||
public enum HsOutput
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Pin is asserted when this station is able to receive data.
|
|
||||||
/// </summary>
|
|
||||||
Handshake = 2,
|
|
||||||
/// <summary>
|
|
||||||
/// Pin is asserted when this station is transmitting data (RTS on NT, 2000 or XP only).
|
|
||||||
/// </summary>
|
|
||||||
Gate = 3,
|
|
||||||
/// <summary>
|
|
||||||
/// Pin is asserted when this station is online (port is open).
|
|
||||||
/// </summary>
|
|
||||||
Online = 1,
|
|
||||||
/// <summary>
|
|
||||||
/// Pin is never asserted.
|
|
||||||
/// </summary>
|
|
||||||
None = 0
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
using PInvokeSerialPort.Win32PInvoke;
|
|
||||||
|
|
||||||
namespace PInvokeSerialPort
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the status of the modem control input signals.
|
|
||||||
/// </summary>
|
|
||||||
public struct ModemStatus
|
|
||||||
{
|
|
||||||
private readonly uint _status;
|
|
||||||
internal ModemStatus(uint val) { _status = val; }
|
|
||||||
/// <summary>
|
|
||||||
/// Condition of the Clear To Send signal.
|
|
||||||
/// </summary>
|
|
||||||
public bool Cts { get { return ((_status & Win32Com.MS_CTS_ON) != 0); } }
|
|
||||||
/// <summary>
|
|
||||||
/// Condition of the Data Set Ready signal.
|
|
||||||
/// </summary>
|
|
||||||
public bool Dsr { get { return ((_status & Win32Com.MS_DSR_ON) != 0); } }
|
|
||||||
/// <summary>
|
|
||||||
/// Condition of the Receive Line Status Detection signal.
|
|
||||||
/// </summary>
|
|
||||||
public bool Rlsd { get { return ((_status & Win32Com.MS_RLSD_ON) != 0); } }
|
|
||||||
/// <summary>
|
|
||||||
/// Condition of the Ring Detection signal.
|
|
||||||
/// </summary>
|
|
||||||
public bool Ring { get { return ((_status & Win32Com.MS_RING_ON) != 0); } }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,805 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using PInvokeSerialPort.Win32PInvoke;
|
|
||||||
|
|
||||||
namespace PInvokeSerialPort
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// PInvokeSerialPort main class.
|
|
||||||
/// Borrowed from http://msdn.microsoft.com/en-us/magazine/cc301786.aspx ;)
|
|
||||||
/// </summary>
|
|
||||||
public class SerialPort : IDisposable
|
|
||||||
{
|
|
||||||
#region Private fields
|
|
||||||
|
|
||||||
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 Thread _rxThread;
|
|
||||||
private int _stateBrk = 2;
|
|
||||||
private int _stateDtr = 2;
|
|
||||||
private int _stateRts = 2;
|
|
||||||
private int _writeCount;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Class constructor
|
|
||||||
/// </summary>
|
|
||||||
public SerialPort(string portName)
|
|
||||||
{
|
|
||||||
PortName = portName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
/// <summary>
|
|
||||||
/// Class constructor
|
|
||||||
/// </summary>
|
|
||||||
public SerialPort(string portName, int baudRate) : this(portName)
|
|
||||||
{
|
|
||||||
BaudRate = baudRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, the port will automatically re-open on next send if it was previously closed due
|
|
||||||
/// to an error (default: false)
|
|
||||||
/// </summary>
|
|
||||||
public bool AutoReopen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Baud Rate (default: 115200)
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>Unsupported rates will throw "Bad settings".</remarks>
|
|
||||||
public int BaudRate { get; set; } = 115200;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
public bool CheckAllSends { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Number of databits 1..8 (default: 8) unsupported values will throw "Bad settings"
|
|
||||||
/// </summary>
|
|
||||||
public int DataBits { get; set; } = 8;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The parity checking scheme (default: none)
|
|
||||||
/// </summary>
|
|
||||||
public Parity Parity { get; set; } = Parity.None;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, Xon and Xoff characters are sent to control the data flow from the remote station (default: false)
|
|
||||||
/// </summary>
|
|
||||||
public bool RxFlowX { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, received characters are ignored unless DSR is asserted by the remote station (default: false)
|
|
||||||
/// </summary>
|
|
||||||
public bool RxGateDsr { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of free bytes in the reception queue at which flow is disabled (default: 2048)
|
|
||||||
/// </summary>
|
|
||||||
public int RxHighWater { get; set; } = 2048;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of bytes in the reception queue at which flow is re-enabled (default: 512)
|
|
||||||
/// </summary>
|
|
||||||
public int RxLowWater { get; set; } = 512;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Requested size for receive queue (default: 0 = use operating system default)
|
|
||||||
/// </summary>
|
|
||||||
public int RxQueue { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constant. Max time for Send in ms = (Multiplier * Characters) + Constant (default: 0)
|
|
||||||
/// </summary>
|
|
||||||
public int SendTimeoutConstant { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Multiplier. Max time for Send in ms = (Multiplier * Characters) + Constant
|
|
||||||
/// (default: 0 = No timeout)
|
|
||||||
/// </summary>
|
|
||||||
public int SendTimeoutMultiplier { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Number of stop bits (default: one)
|
|
||||||
/// </summary>
|
|
||||||
public StopBits StopBits { get; set; } = StopBits.One;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, transmission is halted unless CTS is asserted by the remote station (default: false)
|
|
||||||
/// </summary>
|
|
||||||
public bool TxFlowCts { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, transmission is halted unless DSR is asserted by the remote station (default: false)
|
|
||||||
/// </summary>
|
|
||||||
public bool TxFlowDsr { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, transmission is halted when Xoff is received and restarted when Xon is received (default: false)
|
|
||||||
/// </summary>
|
|
||||||
public bool TxFlowX { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Requested size for transmit queue (default: 0 = use operating system default)
|
|
||||||
/// </summary>
|
|
||||||
public int TxQueue { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
public bool TxWhenRxXoff { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Specidies the use to which the DTR output is put (default: none)
|
|
||||||
/// </summary>
|
|
||||||
public HsOutput UseDtr { get; set; } = HsOutput.None;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the use to which the RTS output is put (default: none)
|
|
||||||
/// </summary>
|
|
||||||
public HsOutput UseRts { get; set; } = HsOutput.None;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The character used to signal Xoff for X flow control (default: DC3)
|
|
||||||
/// </summary>
|
|
||||||
public ASCII XoffChar { get; set; } = ASCII.DC3;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The character used to signal Xon for X flow control (default: DC1)
|
|
||||||
/// </summary>
|
|
||||||
public ASCII XonChar { get; set; } = ASCII.DC1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// True if online.
|
|
||||||
/// </summary>
|
|
||||||
public bool Online => _online && CheckOnline();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// True if the RTS pin is controllable via the RTS property
|
|
||||||
/// </summary>
|
|
||||||
protected bool RtSavailable => _stateRts < 2;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the state of the RTS modem control output
|
|
||||||
/// </summary>
|
|
||||||
protected bool Rts
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// True if the DTR pin is controllable via the DTR property
|
|
||||||
/// </summary>
|
|
||||||
protected bool DtrAvailable => _stateDtr < 2;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The state of the DTR modem control output
|
|
||||||
/// </summary>
|
|
||||||
protected bool Dtr
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Assert or remove a break condition from the transmission line
|
|
||||||
/// </summary>
|
|
||||||
protected bool Break
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Port Name
|
|
||||||
/// </summary>
|
|
||||||
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
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
/// <summary>
|
|
||||||
/// For IDisposable
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Opens the com port and configures it with the required settings
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>false if the port could not be opened</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Closes the com port.
|
|
||||||
/// </summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Destructor (just in case)
|
|
||||||
/// </summary>
|
|
||||||
~SerialPort()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Block until all bytes in the queue have been transmitted.
|
|
||||||
/// </summary>
|
|
||||||
public void Flush()
|
|
||||||
{
|
|
||||||
CheckOnline();
|
|
||||||
CheckResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Use this to throw exceptions in derived classes. Correctly handles threading issues
|
|
||||||
/// and closes the port if necessary.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reason">Description of fault</param>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queues bytes for transmission.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="toSend">Array of bytes to be sent</param>
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queues string for transmission.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="toSend">Array of bytes to be sent</param>
|
|
||||||
public void Write(string toSend)
|
|
||||||
{
|
|
||||||
Write(new ASCIIEncoding().GetBytes(toSend));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queues a single byte for transmission.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="toSend">Byte to be sent</param>
|
|
||||||
public void Write(byte toSend)
|
|
||||||
{
|
|
||||||
var b = new byte[1];
|
|
||||||
b[0] = toSend;
|
|
||||||
Write(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queues a single char for transmission.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="toSend">Byte to be sent</param>
|
|
||||||
public void Write(char toSend)
|
|
||||||
{
|
|
||||||
Write(toSend.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queues string with a new line ("\r\n") for transmission.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="toSend">Array of bytes to be sent</param>
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends a protocol byte immediately ahead of any queued bytes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tosend">Byte to send</param>
|
|
||||||
/// <returns>False if an immediate byte is already scheduled and not yet sent</returns>
|
|
||||||
public void SendImmediate(byte tosend)
|
|
||||||
{
|
|
||||||
CheckOnline();
|
|
||||||
if (!Win32Com.TransmitCommChar(_hPort, tosend)) ThrowException("Transmission failure");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the status of the modem control input signals.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Modem status object</returns>
|
|
||||||
protected ModemStatus GetModemStatus()
|
|
||||||
{
|
|
||||||
uint f;
|
|
||||||
|
|
||||||
CheckOnline();
|
|
||||||
if (!Win32Com.GetCommModemStatus(_hPort, out f)) ThrowException("Unexpected failure");
|
|
||||||
return new ModemStatus(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the status of the queues
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Queue status object</returns>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Override this to provide processing after the port is opened (i.e. to configure remote
|
|
||||||
/// device or just check presence).
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>false to close the port again</returns>
|
|
||||||
protected virtual bool AfterOpen()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Override this to provide processing prior to port closure.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="error">True if closing due to an error</param>
|
|
||||||
protected virtual void BeforeClose(bool error)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public event Action<byte> DataReceived;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Override this to process received bytes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ch">The byte that was received</param>
|
|
||||||
protected void OnRxChar(byte ch)
|
|
||||||
{
|
|
||||||
DataReceived?.Invoke(ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Override this to take action when transmission is complete (i.e. all bytes have actually
|
|
||||||
/// been sent, not just queued).
|
|
||||||
/// </summary>
|
|
||||||
protected virtual void OnTxDone()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Override this to take action when a break condition is detected on the input line.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual void OnBreak()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Override this to take action when a ring condition is signaled by an attached modem.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual void OnRing()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Override this to take action when one or more modem status inputs change state
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mask">The status inputs that have changed state</param>
|
|
||||||
/// <param name="state">The state of the status inputs</param>
|
|
||||||
protected virtual void OnStatusChange(ModemStatus mask, ModemStatus state)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Override this to take action when the reception thread closes due to an exception being thrown.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="e">The exception which was thrown</param>
|
|
||||||
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
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
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]");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((eventMask & Win32Com.EV_RXCHAR) != 0)
|
|
||||||
{
|
|
||||||
uint gotbytes;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (!Win32Com.ReadFile(_hPort, buf, 1, out gotbytes, unmanagedOv))
|
|
||||||
{
|
|
||||||
if (Marshal.GetLastWin32Error() == Win32Com.ERROR_IO_PENDING)
|
|
||||||
{
|
|
||||||
Win32Com.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckOnline()
|
|
||||||
{
|
|
||||||
if (_rxException != null && !_rxExceptionReported)
|
|
||||||
{
|
|
||||||
_rxExceptionReported = true;
|
|
||||||
ThrowException("rx");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_online)
|
|
||||||
{
|
|
||||||
uint f;
|
|
||||||
if (Win32Com.GetHandleInformation(_hPort, out f)) return true;
|
|
||||||
ThrowException("Offline");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_auto)
|
|
||||||
if (Open())
|
|
||||||
return true;
|
|
||||||
ThrowException("Offline");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace PInvokeSerialPort.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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace PInvokeSerialPort.Win32PInvoke
|
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
internal struct COMMTIMEOUTS
|
|
||||||
{
|
|
||||||
internal Int32 ReadIntervalTimeout;
|
|
||||||
internal Int32 ReadTotalTimeoutMultiplier;
|
|
||||||
internal Int32 ReadTotalTimeoutConstant;
|
|
||||||
internal Int32 WriteTotalTimeoutMultiplier;
|
|
||||||
internal Int32 WriteTotalTimeoutConstant;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace PInvokeSerialPort.Win32PInvoke
|
|
||||||
{
|
|
||||||
[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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace PInvokeSerialPort.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);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace PInvokeSerialPort.Win32PInvoke
|
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
internal struct OVERLAPPED
|
|
||||||
{
|
|
||||||
internal UIntPtr Internal;
|
|
||||||
internal UIntPtr InternalHigh;
|
|
||||||
internal UInt32 Offset;
|
|
||||||
internal UInt32 OffsetHigh;
|
|
||||||
internal IntPtr hEvent;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,155 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace PInvokeSerialPort.Win32PInvoke
|
|
||||||
{
|
|
||||||
internal class Win32Com
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Opening Testing and Closing the Port Handle.
|
|
||||||
/// </summary>
|
|
||||||
[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);
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Manipulating the communications settings.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
[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);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reading and writing.
|
|
||||||
/// </summary>
|
|
||||||
[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);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Control port functions.
|
|
||||||
/// </summary>
|
|
||||||
[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;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Status Functions.
|
|
||||||
/// </summary>
|
|
||||||
[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);
|
|
||||||
}
|
|
||||||
}
|
|
4
README.md
Executable file → Normal file
4
README.md
Executable file → Normal file
@ -1,4 +1,6 @@
|
|||||||
# P/Invoke wrapper for Win32API serial port
|
<img src="assets/NSS-128x128.png" align="right" />
|
||||||
|
|
||||||
|
# Nefarius.Peripherals.SerialPort
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
version: 1.0.{build}
|
version: 1.0.{build}
|
||||||
image: Visual Studio 2019
|
image: Visual Studio 2022
|
||||||
configuration:
|
configuration:
|
||||||
- Release
|
- Release
|
||||||
install:
|
install:
|
||||||
|
BIN
assets/NSS-128x128.png
Normal file
BIN
assets/NSS-128x128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.8 KiB |
Loading…
Reference in New Issue
Block a user