You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1517 lines
46 KiB
1517 lines
46 KiB
using System; |
|
using System.Runtime.InteropServices; |
|
using System.Text; |
|
using System.Threading; |
|
using System.Collections.Concurrent; |
|
|
|
//JH 1.1: Version 1.1 changes labelled thus. |
|
//JH 1.2: Version 1.2 changes labelled thus. |
|
//JH 1.3: Version 1.3 changes labelled thus. |
|
|
|
namespace JHCommBase |
|
{ |
|
|
|
/// <summary> |
|
/// Lowest level Com driver handling all Win32 API calls and processing send and receive in terms of |
|
/// individual bytes. Used as a base class for higher level drivers. |
|
/// </summary> |
|
public class CommBase : IDisposable |
|
{ |
|
private IntPtr hPort; |
|
private IntPtr ptrUWO = IntPtr.Zero; |
|
private Thread rxThread = null; |
|
private bool online = false; |
|
private bool auto = false; |
|
private bool checkSends = true; |
|
private Exception rxException = null; |
|
private bool rxExceptionReported = false; |
|
private int writeCount = 0; |
|
private ManualResetEvent writeEvent = new ManualResetEvent(false); |
|
//JH 1.2: Added below to improve robustness of thread start-up. |
|
private ManualResetEvent startEvent = new ManualResetEvent(false); |
|
private int stateRTS = 2; |
|
private int stateDTR = 2; |
|
private int stateBRK = 2; |
|
//JH 1.3: Added to support the new congestion detection scheme (following two lines): |
|
private bool[] empty = new bool[1]; |
|
private bool dataQueued = false; |
|
|
|
private ConcurrentQueue<Byte> Que = null; |
|
|
|
public delegate void OnSend(); |
|
public static OnSend onTx; |
|
public delegate void OnRecv(byte ch); |
|
public static OnRecv onRx; |
|
public delegate void OnRecvPack(Byte[] arr); |
|
public static OnRecvPack onRxPack; |
|
|
|
public CommBase(String port, int bitrate, Parity parity = Parity.none, StopBits stopbits = StopBits.one, int databits = 8) |
|
{ |
|
CommSet.port = port; |
|
CommSet.baudRate = bitrate; |
|
CommSet.parity = parity; |
|
CommSet.dataBits = databits; |
|
CommSet.stopBits = stopbits; |
|
CommSet.autoReopen = true; |
|
} |
|
|
|
/// <summary> |
|
/// Parity settings |
|
/// </summary> |
|
public enum Parity |
|
{ |
|
/// <summary> |
|
/// Characters do not have a parity bit. |
|
/// </summary> |
|
none = 0, |
|
/// <summary> |
|
/// If there are an odd number of 1s in the data bits, the parity bit is 1. |
|
/// </summary> |
|
odd = 1, |
|
/// <summary> |
|
/// If there are an even number of 1s in the data bits, the parity bit is 1. |
|
/// </summary> |
|
even = 2, |
|
/// <summary> |
|
/// The parity bit is always 1. |
|
/// </summary> |
|
mark = 3, |
|
/// <summary> |
|
/// The parity bit is always 0. |
|
/// </summary> |
|
space = 4 |
|
}; |
|
|
|
/// <summary> |
|
/// Stop bit settings |
|
/// </summary> |
|
public enum StopBits |
|
{ |
|
/// <summary> |
|
/// Line is asserted for 1 bit duration at end of each character |
|
/// </summary> |
|
one = 0, |
|
/// <summary> |
|
/// Line is asserted for 1.5 bit duration at end of each character |
|
/// </summary> |
|
onePointFive = 1, |
|
/// <summary> |
|
/// Line is asserted for 2 bit duration at end of each character |
|
/// </summary> |
|
two = 2 |
|
}; |
|
|
|
/// <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 |
|
}; |
|
|
|
/// <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 |
|
} |
|
|
|
/// <summary> |
|
/// Set the public fields to supply settings to CommBase. |
|
/// </summary> |
|
private static class CommSet |
|
{ |
|
/// <summary> |
|
/// Port Name (default: "COM1:") |
|
/// </summary> |
|
public static string port = "COM1:"; |
|
/// <summary> |
|
/// Baud Rate (default: 2400) unsupported rates will throw "Bad settings" |
|
/// </summary> |
|
public static int baudRate = 2400; |
|
/// <summary> |
|
/// The parity checking scheme (default: none) |
|
/// </summary> |
|
public static Parity parity = Parity.none; |
|
/// <summary> |
|
/// Number of databits 1..8 (default: 8) unsupported values will throw "Bad settings" |
|
/// </summary> |
|
public static int dataBits = 8; |
|
/// <summary> |
|
/// Number of stop bits (default: one) |
|
/// </summary> |
|
public static StopBits stopBits = StopBits.one; |
|
/// <summary> |
|
/// If true, transmission is halted unless CTS is asserted by the remote station (default: false) |
|
/// </summary> |
|
public static bool txFlowCTS = false; |
|
/// <summary> |
|
/// If true, transmission is halted unless DSR is asserted by the remote station (default: false) |
|
/// </summary> |
|
public static bool txFlowDSR = false; |
|
/// <summary> |
|
/// If true, transmission is halted when Xoff is received and restarted when Xon is received (default: false) |
|
/// </summary> |
|
public static bool txFlowX = false; |
|
/// <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 static bool txWhenRxXoff = true; |
|
/// <summary> |
|
/// If true, received characters are ignored unless DSR is asserted by the remote station (default: false) |
|
/// </summary> |
|
public static bool rxGateDSR = false; |
|
/// <summary> |
|
/// If true, Xon and Xoff characters are sent to control the data flow from the remote station (default: false) |
|
/// </summary> |
|
public static bool rxFlowX = false; |
|
/// <summary> |
|
/// Specifies the use to which the RTS output is put (default: none) |
|
/// </summary> |
|
public static HSOutput useRTS = HSOutput.none; |
|
/// <summary> |
|
/// Specidies the use to which the DTR output is put (default: none) |
|
/// </summary> |
|
public static HSOutput useDTR = HSOutput.none; |
|
/// <summary> |
|
/// The character used to signal Xon for X flow control (default: DC1) |
|
/// </summary> |
|
public static ASCII XonChar = ASCII.DC1; |
|
/// <summary> |
|
/// The character used to signal Xoff for X flow control (default: DC3) |
|
/// </summary> |
|
public static ASCII XoffChar = ASCII.DC3; |
|
//JH 1.2: Next two defaults changed to 0 to use new defaulting mechanism dependant on queue size. |
|
/// <summary> |
|
/// The number of free bytes in the reception queue at which flow is disabled |
|
/// (Default: 0 = Set to 1/10th of actual rxQueue size) |
|
/// </summary> |
|
public static int rxHighWater = 0; |
|
/// <summary> |
|
/// The number of bytes in the reception queue at which flow is re-enabled |
|
/// (Default: 0 = Set to 1/10th of actual rxQueue size) |
|
/// </summary> |
|
public static int rxLowWater = 0; |
|
/// <summary> |
|
/// Multiplier. Max time for Send in ms = (Multiplier * Characters) + Constant |
|
/// (default: 0 = No timeout) |
|
/// </summary> |
|
public static uint sendTimeoutMultiplier = 0; |
|
/// <summary> |
|
/// Constant. Max time for Send in ms = (Multiplier * Characters) + Constant (default: 0) |
|
/// </summary> |
|
public static uint sendTimeoutConstant = 0; |
|
/// <summary> |
|
/// Requested size for receive queue (default: 0 = use operating system default) |
|
/// </summary> |
|
public static int rxQueue = 0; |
|
/// <summary> |
|
/// Requested size for transmit queue (default: 0 = use operating system default) |
|
/// </summary> |
|
public static int txQueue = 0; |
|
/// <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 static bool autoReopen = false; |
|
|
|
/// <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 static bool checkAllSends = true; |
|
|
|
/// <summary> |
|
/// Pre-configures settings for most modern devices: 8 databits, 1 stop bit, no parity and |
|
/// one of the common handshake protocols. Change individual settings later if necessary. |
|
/// </summary> |
|
/// <param name="Port">The port to use (i.e. "COM1:")</param> |
|
/// <param name="Baud">The baud rate</param> |
|
/// <param name="Hs">The handshake protocol</param> |
|
public static void SetStandard(string Port, int Baud, Handshake Hs) |
|
{ |
|
dataBits = 8; stopBits = StopBits.one; parity = Parity.none; |
|
port = Port; baudRate = Baud; |
|
switch (Hs) |
|
{ |
|
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; |
|
} |
|
} |
|
} |
|
|
|
//JH 1.3: Corrected STH -> STX (Thanks - Johan Thelin!) |
|
/// <summary> |
|
/// Byte type with enumeration constants for ASCII control codes. |
|
/// </summary> |
|
public enum ASCII : byte |
|
{ |
|
NULL = 0x00,SOH = 0x01,STX = 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 |
|
} |
|
|
|
|
|
//JH 1.3: Added AltName function, PortStatus enum and IsPortAvailable function. |
|
|
|
/// <summary> |
|
/// Returns the alternative name of a com port i.e. \\.\COM1 for COM1: |
|
/// Some systems require this form for double or more digit com port numbers. |
|
/// </summary> |
|
/// <param name="s">Name in form COM1 or COM1:</param> |
|
/// <returns>Name in form \\.\COM1</returns> |
|
private string AltName(string s) |
|
{ |
|
string r = s.Trim(); |
|
if (s.EndsWith(":")) s = s.Substring(0, s.Length - 1); |
|
if (s.StartsWith(@"\")) return s; |
|
return @"\\.\" + s; |
|
} |
|
|
|
/// <summary> |
|
/// Availability status of a port |
|
/// </summary> |
|
public enum PortStatus |
|
{ |
|
/// <summary> |
|
/// Port exists but is unavailable (may be open to another program) |
|
/// </summary> |
|
unavailable = 0, |
|
/// <summary> |
|
/// Available for use |
|
/// </summary> |
|
available = 1, |
|
/// <summary> |
|
/// Port does not exist |
|
/// </summary> |
|
absent = -1 |
|
} |
|
|
|
/// <summary> |
|
/// Tests the availability of a named comm port. |
|
/// </summary> |
|
/// <param name="s">Name of port</param> |
|
/// <returns>Availability of port</returns> |
|
public PortStatus IsPortAvailable(string s) |
|
{ |
|
IntPtr h; |
|
|
|
h = Win32Com.CreateFile(s, Win32Com.GENERIC_READ | Win32Com.GENERIC_WRITE, 0, IntPtr.Zero, |
|
Win32Com.OPEN_EXISTING, Win32Com.FILE_FLAG_OVERLAPPED, IntPtr.Zero); |
|
if (h == (IntPtr)Win32Com.INVALID_HANDLE_VALUE) |
|
{ |
|
if (Marshal.GetLastWin32Error() == Win32Com.ERROR_ACCESS_DENIED) |
|
{ |
|
return PortStatus.unavailable; |
|
} |
|
else |
|
{ |
|
//JH 1.3: Automatically try AltName if supplied name fails: |
|
h = Win32Com.CreateFile(AltName(s), Win32Com.GENERIC_READ | Win32Com.GENERIC_WRITE, 0, IntPtr.Zero, |
|
Win32Com.OPEN_EXISTING, Win32Com.FILE_FLAG_OVERLAPPED, IntPtr.Zero); |
|
if (h == (IntPtr)Win32Com.INVALID_HANDLE_VALUE) |
|
{ |
|
if (Marshal.GetLastWin32Error() == Win32Com.ERROR_ACCESS_DENIED) |
|
{ |
|
return PortStatus.unavailable; |
|
} |
|
else |
|
{ |
|
return PortStatus.absent; |
|
} |
|
} |
|
} |
|
} |
|
Win32Com.CloseHandle(h); |
|
return PortStatus.available; |
|
} |
|
|
|
/// <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() |
|
{ |
|
Win32Com.DCB PortDCB = new Win32Com.DCB(); |
|
Win32Com.COMMTIMEOUTS CommTimeouts = new Win32Com.COMMTIMEOUTS(); |
|
Win32Com.OVERLAPPED wo = new Win32Com.OVERLAPPED(); |
|
Win32Com.COMMPROP cp; |
|
Que = new ConcurrentQueue<byte>(); |
|
|
|
if (online) return false; |
|
|
|
hPort = Win32Com.CreateFile(CommSet.port, 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; |
|
} |
|
else |
|
{ |
|
//JH 1.3: Try alternative name form if main one fails: |
|
hPort = Win32Com.CreateFile(AltName(CommSet.port), 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; |
|
} |
|
else |
|
{ |
|
throw new CommPortException("Port Open Failure"); |
|
} |
|
} |
|
} |
|
} |
|
|
|
online = true; |
|
|
|
//JH1.1: Changed from 0 to "magic number" to give instant return on ReadFile: |
|
CommTimeouts.ReadIntervalTimeout = Win32Com.MAXDWORD; |
|
CommTimeouts.ReadTotalTimeoutConstant = 0; |
|
CommTimeouts.ReadTotalTimeoutMultiplier = 0; |
|
|
|
//JH1.2: 0 does not seem to mean infinite on non-NT platforms, so default it to 10 |
|
//seconds per byte which should be enough for anyone. |
|
if (CommSet.sendTimeoutMultiplier == 0) |
|
{ |
|
if (System.Environment.OSVersion.Platform == System.PlatformID.Win32NT) |
|
{ |
|
CommTimeouts.WriteTotalTimeoutMultiplier = 0; |
|
} |
|
else |
|
{ |
|
CommTimeouts.WriteTotalTimeoutMultiplier = 10000; |
|
} |
|
} |
|
else |
|
{ |
|
CommTimeouts.WriteTotalTimeoutMultiplier = CommSet.sendTimeoutMultiplier; |
|
} |
|
CommTimeouts.WriteTotalTimeoutConstant = CommSet.sendTimeoutConstant; |
|
|
|
PortDCB.init(((CommSet.parity == Parity.odd) || (CommSet.parity == Parity.even)), CommSet.txFlowCTS, CommSet.txFlowDSR, |
|
(int)CommSet.useDTR, CommSet.rxGateDSR, !CommSet.txWhenRxXoff, CommSet.txFlowX, CommSet.rxFlowX, (int)CommSet.useRTS); |
|
PortDCB.BaudRate = CommSet.baudRate; |
|
PortDCB.ByteSize = (byte)CommSet.dataBits; |
|
PortDCB.Parity = (byte)CommSet.parity; |
|
PortDCB.StopBits = (byte)CommSet.stopBits; |
|
PortDCB.XoffChar = (byte)CommSet.XoffChar; |
|
PortDCB.XonChar = (byte)CommSet.XonChar; |
|
if ((CommSet.rxQueue != 0) || (CommSet.txQueue != 0)) |
|
if (!Win32Com.SetupComm(hPort, (uint)CommSet.rxQueue, (uint)CommSet.txQueue)) ThrowException("Bad queue settings"); |
|
|
|
//JH 1.2: Defaulting mechanism for handshake thresholds - prevents problems of setting specific |
|
//defaults which may violate the size of the actually granted queue. If the user specifically sets |
|
//these values, it's their problem! |
|
if ((CommSet.rxLowWater == 0) || (CommSet.rxHighWater == 0)) |
|
{ |
|
if (!Win32Com.GetCommProperties(hPort, out cp)) cp.dwCurrentRxQueue = 0; |
|
if (cp.dwCurrentRxQueue > 0) |
|
{ |
|
//If we can determine the queue size, default to 1/10th, 8/10ths, 1/10th. |
|
//Note that HighWater is measured from top of queue. |
|
PortDCB.XoffLim = PortDCB.XonLim = (short)((int)cp.dwCurrentRxQueue / 10); |
|
} |
|
else |
|
{ |
|
//If we do not know the queue size, set very low defaults for safety. |
|
PortDCB.XoffLim = PortDCB.XonLim = 8; |
|
} |
|
} |
|
else |
|
{ |
|
PortDCB.XoffLim = (short)CommSet.rxHighWater; |
|
PortDCB.XonLim = (short)CommSet.rxLowWater; |
|
} |
|
|
|
if (!Win32Com.SetCommState(hPort, ref PortDCB)) ThrowException("Bad com settings"); |
|
if (!Win32Com.SetCommTimeouts(hPort, ref CommTimeouts)) ThrowException("Bad timeout settings"); |
|
|
|
stateBRK = 0; |
|
if (CommSet.useDTR == HSOutput.none) stateDTR = 0; |
|
if (CommSet.useDTR == HSOutput.online) stateDTR = 1; |
|
if (CommSet.useRTS == HSOutput.none) stateRTS = 0; |
|
if (CommSet.useRTS == HSOutput.online) stateRTS = 1; |
|
|
|
checkSends = CommSet.checkAllSends; |
|
wo.Offset = 0; |
|
wo.OffsetHigh = 0; |
|
if (checkSends) |
|
wo.hEvent = writeEvent.Handle; |
|
else |
|
wo.hEvent = IntPtr.Zero; |
|
|
|
ptrUWO = Marshal.AllocHGlobal(Marshal.SizeOf(wo)); |
|
|
|
Marshal.StructureToPtr(wo, ptrUWO, true); |
|
writeCount = 0; |
|
//JH1.3: |
|
empty[0] = true; |
|
dataQueued = false; |
|
|
|
rxException = null; |
|
rxExceptionReported = false; |
|
rxThread = new Thread(new ThreadStart(this.ReceiveThread)); |
|
rxThread.Name = "CommBaseRx"; |
|
rxThread.Priority = ThreadPriority.AboveNormal; |
|
rxThread.Start(); |
|
|
|
//JH1.2: More robust thread start-up wait. |
|
startEvent.WaitOne(500, false); |
|
|
|
auto = false; |
|
if (AfterOpen()) |
|
{ |
|
auto = CommSet.autoReopen; |
|
return true; |
|
} |
|
else |
|
{ |
|
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(); |
|
//JH 1.3: Improve robustness of Close in case were followed by Open: |
|
rxThread.Join(100); |
|
rxThread = null; |
|
} |
|
Win32Com.CloseHandle(hPort); |
|
if (ptrUWO != IntPtr.Zero) Marshal.FreeHGlobal(ptrUWO); |
|
stateRTS = 2; |
|
stateDTR = 2; |
|
stateBRK = 2; |
|
online = false; |
|
} |
|
|
|
/// <summary> |
|
/// For IDisposable |
|
/// </summary> |
|
public void Dispose() {Close();} |
|
|
|
/// <summary> |
|
/// Destructor (just in case) |
|
/// </summary> |
|
~CommBase() {Close();} |
|
|
|
/// <summary> |
|
/// True if online. |
|
/// </summary> |
|
public bool Online {get {if (!online) return false; else return CheckOnline();}} |
|
|
|
/// <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); |
|
} |
|
else |
|
{ |
|
if (online) |
|
{ |
|
BeforeClose(true); |
|
InternalClose(); |
|
} |
|
if (rxException == null) |
|
{ |
|
throw new CommPortException(reason); |
|
} |
|
else |
|
{ |
|
throw new CommPortException(rxException); |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Queues bytes for transmission. |
|
/// </summary> |
|
/// <param name="tosend">Array of bytes to be sent</param> |
|
public void Send(byte[] tosend) { |
|
uint sent = 0; |
|
CheckOnline(); |
|
CheckResult(); |
|
writeCount = tosend.GetLength(0); |
|
if (Win32Com.WriteFile(hPort, tosend, (uint)writeCount, out sent, ptrUWO)) |
|
{ |
|
writeCount -= (int)sent; |
|
OnTxDone(); |
|
} |
|
else |
|
{ |
|
if (Marshal.GetLastWin32Error() != Win32Com.ERROR_IO_PENDING) ThrowException("Send failed"); |
|
//JH1.3: |
|
dataQueued = true; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Queues a single byte for transmission. |
|
/// </summary> |
|
/// <param name="tosend">Byte to be sent</param> |
|
public void Send(byte tosend) |
|
{ |
|
byte[] b = new byte[1]; |
|
b[0] = tosend; |
|
Send(b); |
|
} |
|
|
|
private void CheckResult() |
|
{ |
|
uint sent = 0; |
|
|
|
//JH 1.3: Fixed a number of problems working with checkSends == false. Byte counting was unreliable because |
|
//occasionally GetOverlappedResult would return true with a completion having missed one or more previous |
|
//completions. The test for ERROR_IO_INCOMPLETE was incorrectly for ERROR_IO_PENDING instead. |
|
|
|
if (writeCount > 0) |
|
{ |
|
if (Win32Com.GetOverlappedResult(hPort, ptrUWO, out sent, checkSends)) |
|
{ |
|
if (checkSends) |
|
{ |
|
writeCount -= (int)sent; |
|
if (writeCount != 0) ThrowException("Send Timeout"); |
|
writeCount = 0; |
|
} |
|
} |
|
else |
|
{ |
|
if (Marshal.GetLastWin32Error() != Win32Com.ERROR_IO_INCOMPLETE) ThrowException("Write Error"); |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Sends a protocol byte immediately ahead of any queued bytes. |
|
/// </summary> |
|
/// <param name="tosend">Byte to send</param> |
|
public void SendImmediate(byte tosend) { |
|
CheckOnline(); |
|
if (!Win32Com.TransmitCommChar(hPort, tosend)) |
|
ThrowException("Transmission failure"); |
|
else |
|
OnTxDone(); |
|
} |
|
|
|
/// <summary> |
|
/// Delay processing. |
|
/// </summary> |
|
/// <param name="milliseconds">Milliseconds to delay by</param> |
|
protected void Sleep(int milliseconds) |
|
{ |
|
Thread.Sleep(milliseconds); |
|
} |
|
|
|
/// <summary> |
|
/// Represents the status of the modem control input signals. |
|
/// </summary> |
|
public struct ModemStatus |
|
{ |
|
private 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);}} |
|
} |
|
|
|
/// <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> |
|
/// Represents the current condition of the port queues. |
|
/// </summary> |
|
public struct QueueStatus |
|
{ |
|
private uint status; |
|
private uint inQueue; |
|
private uint outQueue; |
|
private uint inQueueSize; |
|
private uint outQueueSize; |
|
|
|
internal QueueStatus(uint stat, uint inQ, uint outQ, uint inQs, uint outQs) |
|
{status = stat; inQueue = inQ; outQueue = outQ; inQueueSize = inQs; outQueueSize = outQs;} |
|
/// <summary> |
|
/// Output is blocked by CTS handshaking. |
|
/// </summary> |
|
public bool ctsHold {get{return ((status & Win32Com.COMSTAT.fCtsHold) != 0);}} |
|
/// <summary> |
|
/// Output is blocked by DRS handshaking. |
|
/// </summary> |
|
public bool dsrHold {get{return ((status & Win32Com.COMSTAT.fDsrHold) != 0);}} |
|
/// <summary> |
|
/// Output is blocked by RLSD handshaking. |
|
/// </summary> |
|
public bool rlsdHold {get{return ((status & Win32Com.COMSTAT.fRlsdHold) != 0);}} |
|
/// <summary> |
|
/// Output is blocked because software handshaking is enabled and XOFF was received. |
|
/// </summary> |
|
public bool xoffHold {get{return ((status & Win32Com.COMSTAT.fXoffHold) != 0);}} |
|
/// <summary> |
|
/// Output was blocked because XOFF was sent and this station is not yet ready to receive. |
|
/// </summary> |
|
public bool xoffSent {get{return ((status & Win32Com.COMSTAT.fXoffSent) != 0);}} |
|
|
|
/// <summary> |
|
/// There is a character waiting for transmission in the immediate buffer. |
|
/// </summary> |
|
public bool immediateWaiting {get{return ((status & Win32Com.COMSTAT.fTxim) != 0);}} |
|
|
|
/// <summary> |
|
/// Number of bytes waiting in the input queue. |
|
/// </summary> |
|
public long InQueue {get{return (long)inQueue;}} |
|
/// <summary> |
|
/// Number of bytes waiting for transmission. |
|
/// </summary> |
|
public long OutQueue {get{return (long)outQueue;}} |
|
/// <summary> |
|
/// Total size of input queue (0 means information unavailable) |
|
/// </summary> |
|
public long InQueueSize {get{return (long)inQueueSize;}} |
|
/// <summary> |
|
/// Total size of output queue (0 means information unavailable) |
|
/// </summary> |
|
public long OutQueueSize {get{return (long)outQueueSize;}} |
|
|
|
public override string ToString() |
|
{ |
|
StringBuilder m = new StringBuilder("The reception queue is ", 60); |
|
if (inQueueSize == 0) |
|
{ |
|
m.Append("of unknown size and "); |
|
} |
|
else |
|
{ |
|
m.Append(inQueueSize.ToString() + " bytes long and "); |
|
} |
|
if (inQueue == 0) |
|
{ |
|
m.Append("is empty."); |
|
} |
|
else if (inQueue == 1) |
|
{ |
|
m.Append("contains 1 byte."); |
|
} |
|
else |
|
{ |
|
m.Append("contains "); |
|
m.Append(inQueue.ToString()); |
|
m.Append(" bytes."); |
|
} |
|
m.Append(" The transmission queue is "); |
|
if (outQueueSize == 0) |
|
{ |
|
m.Append("of unknown size and "); |
|
} |
|
else |
|
{ |
|
m.Append(outQueueSize.ToString() + " bytes long and "); |
|
} |
|
if (outQueue == 0) |
|
{ |
|
m.Append("is empty"); |
|
} |
|
else if (outQueue == 1) |
|
{ |
|
m.Append("contains 1 byte. It is "); |
|
} |
|
else |
|
{ |
|
m.Append("contains "); |
|
m.Append(outQueue.ToString()); |
|
m.Append(" bytes. It is "); |
|
} |
|
if (outQueue > 0) |
|
{ |
|
if (ctsHold || dsrHold || rlsdHold || xoffHold || xoffSent) |
|
{ |
|
m.Append("holding on"); |
|
if (ctsHold) m.Append(" CTS"); |
|
if (dsrHold) m.Append(" DSR"); |
|
if (rlsdHold) m.Append(" RLSD"); |
|
if (xoffHold) m.Append(" Rx XOff"); |
|
if (xoffSent) m.Append(" Tx XOff"); |
|
} |
|
else |
|
{ |
|
m.Append("pumping data"); |
|
} |
|
} |
|
m.Append(". The immediate buffer is "); |
|
if (immediateWaiting) |
|
m.Append("full."); |
|
else |
|
m.Append("empty."); |
|
return m.ToString(); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Get the status of the queues |
|
/// </summary> |
|
/// <returns>Queue status object</returns> |
|
protected QueueStatus GetQueueStatus() |
|
{ |
|
Win32Com.COMSTAT cs; |
|
Win32Com.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); |
|
} |
|
|
|
// JH 1.3. Added for this version. |
|
/// <summary> |
|
/// Test if the line is congested (data being queued for send faster than it is being dequeued) |
|
/// This detects if baud rate is too slow or if handshaking is not allowing enough transmission |
|
/// time. It should be called at reasonably long fixed intervals. If data has been sent during |
|
/// the interval, congestion is reported if the queue was never empty during the interval. |
|
/// </summary> |
|
/// <returns>True if congested</returns> |
|
protected bool IsCongested() |
|
{ |
|
bool e; |
|
if (!dataQueued) return false; |
|
lock(empty) {e = empty[0]; empty[0] = false;} |
|
dataQueued = false; |
|
return !e; |
|
} |
|
|
|
/// <summary> |
|
/// True if the RTS pin is controllable via the RTS property |
|
/// </summary> |
|
protected bool RTSavailable { get { return (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)) |
|
//JH 1.3: Was 1, should be 0: |
|
stateRTS = 0; |
|
else |
|
ThrowException("Unexpected Failure"); |
|
} |
|
} |
|
get { |
|
return (stateRTS == 1); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// True if the DTR pin is controllable via the DTR property |
|
/// </summary> |
|
protected bool DTRavailable { get { return (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 { |
|
return (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 { |
|
return (stateBRK == 1); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Override this to provide settings. (NB this is called during Open method) |
|
/// </summary> |
|
/// <returns>CommSet, or derived object with required settings initialised</returns> |
|
|
|
/// <summary> |
|
/// Override this to provide processing after the port is openned (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) {} |
|
|
|
/// <summary> |
|
/// Override this to process received bytes. |
|
/// </summary> |
|
/// <param name="ch">The byte that was received</param> |
|
public virtual void OnRxChar(byte ch) |
|
{ |
|
if (onRx != null) |
|
onRx(ch); |
|
} |
|
|
|
/// <summary> |
|
/// Override this to take action when transmission is complete (i.e. all bytes have actually |
|
/// been sent, not just queued). |
|
/// </summary> |
|
private void OnTxDone() |
|
{ |
|
if (onTx != null) |
|
onTx(); |
|
} |
|
|
|
/// <summary> |
|
/// Override this to take action when a break condition is detected on the input line. |
|
/// </summary> |
|
private void OnBreak() |
|
{ |
|
if (Que.Count != 0) |
|
{ |
|
// Console.Write($"OnBreak break que={Que.Count} "); |
|
Byte[] bb = new Byte[Que.Count]; |
|
int i = 0; |
|
while (Que.Count > 0) |
|
{ |
|
Byte b = 0; |
|
Que.TryDequeue(out b); |
|
bb[i++] = b; |
|
} |
|
//bb.ToList<Byte>().ForEach(b => Console.Write($"0x{b:X2} ")); |
|
// Console.WriteLine(); |
|
if (onRxPack != null) |
|
onRxPack(bb); |
|
} |
|
} |
|
|
|
//JH 1.3: Deleted OnRing() which was never called: use OnStatusChange instead (Thanks Jim Foster) |
|
|
|
/// <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() |
|
{ |
|
byte[] buf = new Byte[1]; |
|
uint gotbytes; |
|
bool starting; |
|
|
|
starting = true; |
|
AutoResetEvent sg = new AutoResetEvent(false); |
|
Win32Com.OVERLAPPED ov = new Win32Com.OVERLAPPED(); |
|
|
|
IntPtr unmanagedOv; |
|
IntPtr uMask; |
|
uint eventMask = 0; |
|
unmanagedOv = Marshal.AllocHGlobal(Marshal.SizeOf(ov)); |
|
uMask = Marshal.AllocHGlobal(Marshal.SizeOf(eventMask)); |
|
|
|
ov.Offset = 0; ov.OffsetHigh = 0; |
|
ov.hEvent = sg.Handle; |
|
Marshal.StructureToPtr(ov, unmanagedOv, true); |
|
|
|
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); |
|
//JH 1.2: Tells the main thread that this thread is ready for action. |
|
if (starting) |
|
{ |
|
startEvent.Set(); |
|
starting = false; |
|
} |
|
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) |
|
{ |
|
UInt32 errs; |
|
if (Win32Com.ClearCommError(hPort, out errs, IntPtr.Zero)) |
|
{ |
|
//JH 1.2: BREAK condition has an error flag and and an event flag. Not sure if both |
|
//are always raised, so if CE_BREAK is only error flag ignore it and set the EV_BREAK |
|
//flag for normal handling. Also made more robust by handling case were no recognised |
|
//error was present in the flags. (Thanks to Fred Pittroff for finding this problem!) |
|
int ec = 0; |
|
StringBuilder s = new StringBuilder("UART Error: ", 40); |
|
if ((errs & Win32Com.CE_FRAME) != 0) |
|
{ |
|
s = s.Append("Framing,"); ec++; |
|
} |
|
if ((errs & Win32Com.CE_IOE) != 0) |
|
{ |
|
s = s.Append("IO,"); ec++; |
|
} |
|
if ((errs & Win32Com.CE_OVERRUN) != 0) |
|
{ |
|
s = s.Append("Overrun,"); ec++; |
|
} |
|
if ((errs & Win32Com.CE_RXOVER) != 0) |
|
{ |
|
s = s.Append("Receive Cverflow,"); ec++; |
|
} |
|
if ((errs & Win32Com.CE_RXPARITY) != 0) |
|
{ |
|
s = s.Append("Parity,"); ec++; |
|
} |
|
if ((errs & Win32Com.CE_TXFULL) != 0) |
|
{ |
|
s = s.Append("Transmit Overflow,"); ec++; |
|
} |
|
if (ec > 0) |
|
{ |
|
s.Length = s.Length - 1; |
|
throw new CommPortException(s.ToString()); |
|
} |
|
else |
|
{ |
|
if (errs == Win32Com.CE_BREAK) |
|
{ |
|
eventMask |= Win32Com.EV_BREAK; |
|
} |
|
else |
|
{ |
|
throw new CommPortException("IO Error [003]"); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
throw new CommPortException("IO Error [003]"); |
|
} |
|
} |
|
if ((eventMask & Win32Com.EV_RXCHAR) != 0) |
|
{ |
|
do |
|
{ |
|
gotbytes = 0; |
|
if (!Win32Com.ReadFile(hPort, buf, 1, out gotbytes, unmanagedOv)) |
|
{ |
|
//JH 1.1: Removed ERROR_IO_PENDING handling as comm timeouts have now |
|
//been set so ReadFile returns immediately. This avoids use of CancelIo |
|
//which was causing loss of data. Thanks to Daniel Moth for suggesting this |
|
//might be a problem, and to many others for reporting that it was! |
|
|
|
int x = Marshal.GetLastWin32Error(); |
|
|
|
throw new CommPortException("IO Error [004]"); |
|
} |
|
if (gotbytes == 1) |
|
{ |
|
OnRxChar(buf[0]); |
|
Que.Enqueue(buf[0]); |
|
} |
|
} while (gotbytes > 0); |
|
OnBreak(); |
|
} |
|
if ((eventMask & Win32Com.EV_TXEMPTY) != 0) |
|
{ |
|
//JH1.3: |
|
lock(empty) empty[0] = true; |
|
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) |
|
{ |
|
//JH 1.3: Added for shutdown robustness (Thanks to Fred Pittroff, Mark Behner and Kevin Williamson!), . |
|
Win32Com.CancelIo(hPort); |
|
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) |
|
{ |
|
//JH 1.1: Avoid use of GetHandleInformation for W98 compatability. |
|
if (hPort != (System.IntPtr)Win32Com.INVALID_HANDLE_VALUE) return true; |
|
ThrowException("Offline"); |
|
return false; |
|
} |
|
else |
|
{ |
|
if (auto) |
|
{ |
|
if (Open()) return true; |
|
} |
|
ThrowException("Offline"); |
|
return false; |
|
} |
|
} |
|
|
|
} |
|
|
|
|
|
/// <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) {} |
|
} |
|
|
|
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; |
|
internal const UInt32 ERROR_IO_INCOMPLETE = 996; |
|
|
|
//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); |
|
|
|
/// <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); |
|
|
|
[StructLayout( LayoutKind.Sequential )] internal struct COMMTIMEOUTS |
|
{ |
|
//JH 1.1: Changed Int32 to UInt32 to allow setting to MAXDWORD |
|
internal UInt32 ReadIntervalTimeout; |
|
internal UInt32 ReadTotalTimeoutMultiplier; |
|
internal UInt32 ReadTotalTimeoutConstant; |
|
internal UInt32 WriteTotalTimeoutMultiplier; |
|
internal UInt32 WriteTotalTimeoutConstant; |
|
} |
|
//JH 1.1: Added to enable use of "return immediately" timeout. |
|
internal const UInt32 MAXDWORD = 0xffffffff; |
|
|
|
[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) |
|
{ |
|
//JH 1.3: Was 0x8001 ans so not setting fAbortOnError - Thanks Larry Delby! |
|
DCBlength = 28; PackedValues = 0x4001; |
|
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); |
|
|
|
} |
|
} |
|
|
|
/// <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); |
|
|
|
[StructLayout( LayoutKind.Sequential )] internal struct OVERLAPPED |
|
{ |
|
internal UIntPtr Internal; |
|
internal UIntPtr InternalHigh; |
|
internal UInt32 Offset; |
|
internal UInt32 OffsetHigh; |
|
internal IntPtr hEvent; |
|
} |
|
|
|
[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; |
|
|
|
[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; |
|
} |
|
[DllImport("kernel32.dll")] |
|
internal static extern Boolean GetCommProperties(IntPtr hFile, out COMMPROP cp); |
|
|
|
[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; |
|
} |
|
|
|
} |
|
|
|
}
|
|
|