修正了若干Bug 的 .NET 下的 串口通信的基类
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.IO;
using System.Xml.Serialization;
namespace XST.XSTComm.Base
{
///
/// 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.
///
public abstract 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;
///
/// Parity settings
///
public enum Parity
{
///
/// Characters do not have a parity bit.
///
none = 0,
///
/// If there are an odd number of 1s in the data bits, the parity bit is 1.
///
odd = 1,
///
/// If there are an even number of 1s in the data bits, the parity bit is 1.
///
even = 2,
///
/// The parity bit is always 1.
///
mark = 3,
///
/// The parity bit is always 0.
///
space = 4
};
///
/// Stop bit settings
///
public enum StopBits
{
///
/// Line is asserted for 1 bit duration at end of each character
///
one = 0,
///
/// Line is asserted for 1.5 bit duration at end of each character
///
onePointFive = 1,
///
/// Line is asserted for 2 bit duration at end of each character
///
two = 2
};
///
/// Uses for RTS or DTR pins
///
public enum HSOutput
{
///
/// Pin is asserted when this station is able to receive data.
///
handshake = 2,
///
/// Pin is asserted when this station is transmitting data (RTS on NT, 2000 or XP only).
///
gate = 3,
///
/// Pin is asserted when this station is online (port is open).
///
online = 1,
///
/// Pin is never asserted.
///
none = 0
};
///
/// Standard handshake methods
///
public enum Handshake
{
///
/// No handshaking
///
none,
///
/// Software handshaking using Xon / Xoff
///
XonXoff,
///
/// Hardware handshaking using CTS / RTS
///
CtsRts,
///
/// Hardware handshaking using DSR / DTR
///
DsrDtr
}
///
/// Set the public fields to supply settings to CommBase.
///
public class CommBaseSettings
{
///
/// Port Name (default: "COM1:")
///
public string port = "COM1";
///
/// Baud Rate (default: 2400) unsupported rates will throw "Bad settings"
///
public int baudRate = 19200;
///
/// The parity checking scheme (default: none)
///
public Parity parity = Parity.none;
///
/// Number of databits 1..8 (default: 8) unsupported values will throw "Bad settings"
///
public int dataBits = 8;
///
/// Number of stop bits (default: one)
///
public StopBits stopBits = StopBits.one;
///
/// If true, transmission is halted unless CTS is asserted by the remote station (default: false)
///
public bool txFlowCTS = false;
///
/// If true, transmission is halted unless DSR is asserted by the remote station (default: false)
///
public bool txFlowDSR = false;
///
/// If true, transmission is halted when Xoff is received and restarted when Xon is received (default: false)
///
public bool txFlowX = false;
///
/// If false, transmission is suspended when this station has sent Xoff to the remote station (default: true)
/// Set false if the remote station treats any character as an Xon.
///
public bool txWhenRxXoff = true;
///
/// If true, received characters are ignored unless DSR is asserted by the remote station (default: false)
///
public bool rxGateDSR = false;
///
/// If true, Xon and Xoff characters are sent to control the data flow from the remote station (default: false)
///
public bool rxFlowX = false;
///
/// Specifies the use to which the RTS output is put (default: none)
///
public HSOutput useRTS = HSOutput.none;
///
/// Specidies the use to which the DTR output is put (default: none)
///
public HSOutput useDTR = HSOutput.none;
///
/// The character used to signal Xon for X flow control (default: DC1)
///
public ASCII XonChar = ASCII.NULL;//ASCII.DC1;
///
/// The character used to signal Xoff for X flow control (default: DC3)
///
public ASCII XoffChar = ASCII.NULL;//ASCII.DC3;
//JH 1.2: Next two defaults changed to 0 to use new defaulting mechanism dependant on queue size.
///
/// The number of free bytes in the reception queue at which flow is disabled
/// (Default: 0 = Set to 1/10th of actual rxQueue size)
///
public int rxHighWater = 0;
///
/// The number of bytes in the reception queue at which flow is re-enabled
/// (Default: 0 = Set to 1/10th of actual rxQueue size)
///
public int rxLowWater = 0;
///
/// Multiplier. Max time for Send in ms = (Multiplier * Characters) + Constant
/// (default: 0 = No timeout)
///
public uint sendTimeoutMultiplier = 0;
///
/// Constant. Max time for Send in ms = (Multiplier * Characters) + Constant (default: 0)
///
public uint sendTimeoutConstant = 0;
///
/// Requested size for receive queue (default: 0 = use operating system default)
///
public int rxQueue = 0;
///
/// Requested size for transmit queue (default: 0 = use operating system default)
///
public int txQueue = 0;
///
/// If true, the port will automatically re-open on next send if it was previously closed due
/// to an error (default: false)
///
public bool autoReopen = false;
///
/// If true, subsequent Send commands wait for completion of earlier ones enabling the results
/// to be checked. If false, errors, including timeouts, may not be detected, but performance
/// may be better.
///
public bool checkAllSends = true;
///
/// 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.
///
/// The port to use (i.e. "COM1:")
/// The baud rate
/// The handshake protocol
public 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;
}
}
///
/// Save the object in XML format to a stream
///
/// Stream to save the object to
public void SaveAsXML(Stream s)
{
XmlSerializer sr = new XmlSerializer(this.GetType());
sr.Serialize(s, this);
}
///
/// Create a new CommBaseSettings object initialised from XML data
///
/// Stream to load the XML from
/// CommBaseSettings object
public static CommBaseSettings LoadFromXML(Stream s)
{
return LoadFromXML(s, typeof(CommBaseSettings));
}
///
/// Create a new object loading members from the stream in XML format.
/// Derived class should call this from a static method i.e.:
/// return (ComDerivedSettings)LoadFromXML(s, typeof(ComDerivedSettings));
///
/// Stream to load the object from
/// Type of the derived object
///
protected static CommBaseSettings LoadFromXML(Stream s, Type t)
{
XmlSerializer sr = new XmlSerializer(t);
try
{
return (CommBaseSettings)sr.Deserialize(s);
}
catch
{
return null;
}
}
}
#region "ASCII 定 义"
//JH 1.3: Corrected STH -> STX (Thanks - Johan Thelin!)
///
/// Byte type with enumeration constants for ASCII control codes.
///
public enum ASCII : byte
{
///
/// NULL
///
NULL = 0x00,
///
/// SOH
///
SOH = 0x01,
///
/// STX
///
STX = 0x02,
///
/// ETX
///
ETX = 0x03,
///
/// EOT
///
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
}
#endregion
//JH 1.3: Added AltName function, PortStatus enum and IsPortAvailable function.
///
/// 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.
///
/// Name in form COM1 or COM1:
/// Name in form //./COM1 < a>>
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;
}
///
/// Availability status of a port
///
public enum PortStatus
{
///
/// Port exists but is unavailable (may be open to another program)
///
unavailable = 0,
///
/// Available for use
///
available = 1,
///
/// Port does not exist
///
absent = -1
}
///
/// Tests the availability of a named comm port.
///
/// Name of port
/// Availability of port
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;
}
///
/// Opens the com port and configures it with the required settings
///
/// false if the port could not be opened
public bool Open()
{
Win32Com.DCB PortDCB = new Win32Com.DCB();
Win32Com.COMMTIMEOUTS CommTimeouts = new Win32Com.COMMTIMEOUTS();
CommBaseSettings cs;
Win32Com.OVERLAPPED wo = new Win32Com.OVERLAPPED();
Win32Com.COMMPROP cp;
if (online) return false;
cs = CommSettings();
hPort = Win32Com.CreateFile(cs.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(cs.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 (cs.sendTimeoutMultiplier == 0)
{
if (System.Environment.OSVersion.Platform == System.PlatformID.Win32NT)
{
CommTimeouts.WriteTotalTimeoutMultiplier = 0;
}
else
{
CommTimeouts.WriteTotalTimeoutMultiplier = 10000;
}
}
else
{
CommTimeouts.WriteTotalTimeoutMultiplier = cs.sendTimeoutMultiplier;
}
CommTimeouts.WriteTotalTimeoutConstant = cs.sendTimeoutConstant;
PortDCB.init(((cs.parity == Parity.odd) || (cs.parity == Parity.even)), cs.txFlowCTS, cs.txFlowDSR,
(int)cs.useDTR, cs.rxGateDSR, !cs.txWhenRxXoff, cs.txFlowX, cs.rxFlowX, (int)cs.useRTS);
PortDCB.BaudRate = cs.baudRate;
PortDCB.ByteSize = (byte)cs.dataBits;
PortDCB.Parity = (byte)cs.parity;
PortDCB.StopBits = (byte)cs.stopBits;
PortDCB.XoffChar = (byte)cs.XoffChar;
PortDCB.XonChar = (byte)cs.XonChar;
if ((cs.rxQueue != 0) || (cs.txQueue != 0))
if (!Win32Com.SetupComm(hPort, (uint)cs.rxQueue, (uint)cs.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 ((cs.rxLowWater == 0) || (cs.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)cs.rxHighWater;
PortDCB.XonLim = (short)cs.rxLowWater;
}
if (!Win32Com.SetCommState(hPort, ref PortDCB)) ThrowException("Bad com settings");
if (!Win32Com.SetCommTimeouts(hPort, ref CommTimeouts)) ThrowException("Bad timeout settings");
stateBRK = 0;
if (cs.useDTR == HSOutput.none) stateDTR = 0;
if (cs.useDTR == HSOutput.online) stateDTR = 1;
if (cs.useRTS == HSOutput.none) stateRTS = 0;
if (cs.useRTS == HSOutput.online) stateRTS = 1;
checkSends = cs.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 = cs.autoReopen;
return true;
}
else
{
Close();
return false;
}
}
///
/// 添加者:syma 日期:2004-06-07
/// 按照设置的端口和波特率打开串口
///
///
///
///
public bool Open(string Port,int Baud)
{
Win32Com.DCB PortDCB = new Win32Com.DCB();
Win32Com.COMMTIMEOUTS CommTimeouts = new Win32Com.COMMTIMEOUTS();
CommBaseSettings cs;
Win32Com.OVERLAPPED wo = new Win32Com.OVERLAPPED();
Win32Com.COMMPROP cp;
if (online) return false;
cs = CommSettings();
cs.port = Port;
cs.baudRate = Baud;
hPort = Win32Com.CreateFile(cs.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(cs.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 (cs.sendTimeoutMultiplier == 0)
{
if (System.Environment.OSVersion.Platform == System.PlatformID.Win32NT)
{
CommTimeouts.WriteTotalTimeoutMultiplier = 0;
}
else
{
CommTimeouts.WriteTotalTimeoutMultiplier = 10000;
}
}
else
{
CommTimeouts.WriteTotalTimeoutMultiplier = cs.sendTimeoutMultiplier;
}
CommTimeouts.WriteTotalTimeoutConstant = cs.sendTimeoutConstant;
PortDCB.init(((cs.parity == Parity.odd) || (cs.parity == Parity.even)), cs.txFlowCTS, cs.txFlowDSR,
(int)cs.useDTR, cs.rxGateDSR, !cs.txWhenRxXoff, cs.txFlowX, cs.rxFlowX, (int)cs.useRTS);
PortDCB.BaudRate = cs.baudRate;
PortDCB.ByteSize = (byte)cs.dataBits;
PortDCB.Parity = (byte)cs.parity;
PortDCB.StopBits = (byte)cs.stopBits;
PortDCB.XoffChar = (byte)cs.XoffChar;
PortDCB.XonChar = (byte)cs.XonChar;
if ((cs.rxQueue != 0) || (cs.txQueue != 0))
if (!Win32Com.SetupComm(hPort, (uint)cs.rxQueue, (uint)cs.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 ((cs.rxLowWater == 0) || (cs.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)cs.rxHighWater;
PortDCB.XonLim = (short)cs.rxLowWater;
}
if (!Win32Com.SetCommState(hPort, ref PortDCB)) ThrowException("Bad com settings");
if (!Win32Com.SetCommTimeouts(hPort, ref CommTimeouts)) ThrowException("Bad timeout settings");
stateBRK = 0;
if (cs.useDTR == HSOutput.none) stateDTR = 0;
if (cs.useDTR == HSOutput.online) stateDTR = 1;
if (cs.useRTS == HSOutput.none) stateRTS = 0;
if (cs.useRTS == HSOutput.online) stateRTS = 1;
checkSends = cs.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 = cs.autoReopen;
return true;
}
else
{
Close();
return false;
}
}
///
/// Closes the com port.
///
public void Close()
{
if (online)
{
auto = false;
BeforeClose(false);
InternalClose();
rxException = null;
}
}
private void InternalClose()
{
Win32Com.CancelIo(hPort);
if (rxThread != null)
{
rxThread.Abort();
//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;
}
///
/// For IDisposable
///
public void Dispose() {Close();}
///
/// Destructor (just in case)
///
~CommBase() {Close();}
///
/// True if online.
///
public bool Online {get {if (!online) return false; else return CheckOnline();}}
///
/// Block until all bytes in the queue have been transmitted.
///
public void Flush()
{
CheckOnline();
CheckResult();
}
///
/// 设置波特率
///
///
public void ResetBaudRate(int bdr)
{
Win32Com.DCB dcb = new XST.XSTComm.Base.Win32Com.DCB();
Win32Com.GetCommState(hPort,ref dcb);
dcb.BaudRate = bdr;
Win32Com.SetCommState(hPort,ref dcb);
PurgeIn();
PurgeOut();
}
///
/// 清除读取缓冲区内容
///
private void PurgeIn()
{
//Win32Com.PurgeComm(hPort,Win32Com.PURGE_RXABORT | Win32Com.PURGE_RXCLEAR);
CancelRead();
PurgeRead();
}
///
/// 清除写缓冲区内容
///
private void PurgeOut()
{
//Win32Com.PurgeComm(hPort,Win32Com.PURGE_TXABORT | Win32Com.PURGE_TXCLEAR);
CancelWrite();
PurgeWrite();
}
private bool PurgeRead() { return(Win32Com.PurgeComm(hPort, Win32Com.PURGE_RXCLEAR)); }
private bool PurgeWrite() { return(Win32Com.PurgeComm(hPort, Win32Com.PURGE_TXCLEAR)); }
private bool CancelRead() { return(Win32Com.PurgeComm(hPort, Win32Com.PURGE_RXABORT)); }
private bool CancelWrite() { return(Win32Com.PurgeComm(hPort, Win32Com.PURGE_TXABORT)); }
///
/// Use this to throw exceptions in derived classes. Correctly handles threading issues
/// and closes the port if necessary.
///
/// Description of fault
protected void ThrowException(string reason)
{
if (Thread.CurrentThread == rxThread)
{
throw new CommPortException(reason);
}
else
{
if (online)
{
BeforeClose(true);
InternalClose();
}
if (rxException == null)
{
throw new CommPortException(reason);
}
else
{
throw new CommPortException(rxException);
}
}
}
///
/// Queues bytes for transmission.
///
/// Array of bytes to be sent
protected 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;
}
else
{
if (Marshal.GetLastWin32Error() != Win32Com.ERROR_IO_PENDING) ThrowException("Send failed");
//JH1.3:
dataQueued = true;
}
}
///
/// Queues a single byte for transmission.
///
/// Byte to be sent
protected 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");
}
}
}
///
/// Sends a protocol byte immediately ahead of any queued bytes.
///
/// Byte to send
protected void SendImmediate(byte tosend)
{
CheckOnline();
if (!Win32Com.TransmitCommChar(hPort, tosend)) ThrowException("Transmission failure");
}
///
/// Delay processing.
///
/// Milliseconds to delay by
protected void Sleep(int milliseconds)
{
Thread.Sleep(milliseconds);
}
///
/// Represents the status of the modem control input signals.
///
public struct ModemStatus
{
private uint status;
internal ModemStatus(uint val) {status = val;}
///
/// Condition of the Clear To Send signal.
///
public bool cts {get{return ((status & Win32Com.MS_CTS_ON) != 0);}}
///
/// Condition of the Data Set Ready signal.
///
public bool dsr {get{return ((status & Win32Com.MS_DSR_ON) != 0);}}
///
/// Condition of the Receive Line Status Detection signal.
///
public bool rlsd {get{return ((status & Win32Com.MS_RLSD_ON) != 0);}}
///
/// Condition of the Ring Detection signal.
///
public bool ring {get{return ((status & Win32Com.MS_RING_ON) != 0);}}
}
///
/// Gets the status of the modem control input signals.
///
/// Modem status object
protected ModemStatus GetModemStatus()
{
uint f;
CheckOnline();
if (!Win32Com.GetCommModemStatus(hPort, out f)) ThrowException("Unexpected failure");
return new ModemStatus(f);
}
///
/// Represents the current condition of the port queues.
///
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;}
///
/// Output is blocked by CTS handshaking.
///
public bool ctsHold {get{return ((status & Win32Com.COMSTAT.fCtsHold) != 0);}}
///
/// Output is blocked by DRS handshaking.
///
public bool dsrHold {get{return ((status & Win32Com.COMSTAT.fDsrHold) != 0);}}
///
/// Output is blocked by RLSD handshaking.
///
public bool rlsdHold {get{return ((status & Win32Com.COMSTAT.fRlsdHold) != 0);}}
///
/// Output is blocked because software handshaking is enabled and XOFF was received.
///
public bool xoffHold {get{return ((status & Win32Com.COMSTAT.fXoffHold) != 0);}}
///
/// Output was blocked because XOFF was sent and this station is not yet ready to receive.
///
public bool xoffSent {get{return ((status & Win32Com.COMSTAT.fXoffSent) != 0);}}
///
/// There is a character waiting for transmission in the immediate buffer.
///
public bool immediateWaiting {get{return ((status & Win32Com.COMSTAT.fTxim) != 0);}}
///
/// Number of bytes waiting in the input queue.
///
public long InQueue {get{return (long)inQueue;}}
///
/// Number of bytes waiting for transmission.
///
public long OutQueue {get{return (long)outQueue;}}
///
/// Total size of input queue (0 means information unavailable)
///
public long InQueueSize {get{return (long)inQueueSize;}}
///
/// Total size of output queue (0 means information unavailable)
///
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();
}
}
///
/// Get the status of the queues
///
/// Queue status object
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.
///
/// 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.
///
/// True if congested
protected bool IsCongested()
{
bool e;
if (!dataQueued) return false;
lock(empty) {e = empty[0]; empty[0] = false;}
dataQueued = false;
return !e;
}
///
/// True if the RTS pin is controllable via the RTS property
///
protected bool RTSavailable { get { return (stateRTS < 2);}}
///
/// Set the state of the RTS modem control output
///
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);
}
}
///
/// True if the DTR pin is controllable via the DTR property
///
protected bool DTRavailable { get { return (stateDTR < 2);}}
///
/// The state of the DTR modem control output
///
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);
}
}
///
/// Assert or remove a break condition from the transmission line
///
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);
}
}
///
/// Override this to provide settings. (NB this is called during Open method)
///
/// CommBaseSettings, or derived object with required settings initialised
protected virtual CommBaseSettings CommSettings() {return new CommBaseSettings();}
///
/// Override this to provide processing after the port is openned (i.e. to configure remote
/// device or just check presence).
///
/// false to close the port again
protected virtual bool AfterOpen() {return true;}
///
/// Override this to provide processing prior to port closure.
///
/// True if closing due to an error
protected virtual void BeforeClose(bool error) {}
///
/// Override this to process received bytes.
///
/// The byte that was received
protected virtual void OnRxChar(byte ch) {}
///
/// Override this to take action when transmission is complete (i.e. all bytes have actually
/// been sent, not just queued).
///
protected virtual void OnTxDone() {}
///
/// Override this to take action when a break condition is detected on the input line.
///
protected virtual void OnBreak() {}
//JH 1.3: Deleted OnRing() which was never called: use OnStatusChange instead (Thanks Jim Foster)
///
/// Override this to take action when one or more modem status inputs change state
///
/// The status inputs that have changed state
/// The state of the status inputs
protected virtual void OnStatusChange(ModemStatus mask, ModemStatus state) {}
///
/// Override this to take action when the reception thread closes due to an exception being thrown.
///
/// The exception which was thrown
protected virtual void OnRxException(Exception e) {}
private void ReceiveThread()
{
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]);
} while (gotbytes > 0);
}
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;
}
}
}
///
/// Overlays CommBase to provide line or packet oriented communications to derived classes. Strings
/// are sent and received and the Transact method is added which transmits a string and then blocks until
/// a reply string has been received (subject to a timeout).
///
public abstract class CommLine : CommBase
{
private byte[] RxBuffer;
private uint RxBufferP = 0;
private ASCII RxTerm;
private ASCII[] TxTerm;
private ASCII[] RxFilter;
private string RxString = "";
private ManualResetEvent TransFlag = new ManualResetEvent(true);
private uint TransTimeout;
///
/// Extends CommBaseSettings to add the settings used by CommLine.
///
public class CommLineSettings : CommBase.CommBaseSettings
{
///
/// Maximum size of received string (default: 256)
///
public int rxStringBufferSize = 256;
///
/// ASCII code that terminates a received string (default: CR)
///
public ASCII rxTerminator = ASCII.CR;
///
/// ASCII codes that will be ignored in received string (default: null)
///
public ASCII[] rxFilter;
///
/// Maximum time (ms) for the Transact method to complete (default: 500)
///
public int transactTimeout = 500;
///
/// ASCII codes transmitted after each Send string (default: null)
///
public ASCII[] txTerminator;
///
/// 从XML文件中取得设置信息
///
///
///
public static new CommLineSettings LoadFromXML(Stream s)
{
return (CommLineSettings)LoadFromXML(s, typeof(CommLineSettings));
}
}
///
/// Queue the ASCII representation of a string and then the set terminator bytes for sending.
///
/// String to be sent.
protected void Send(string toSend)
{
//JH 1.1: Use static encoder for efficiency. Thanks to Prof. Dr. Peter Jesorsky!
uint l = (uint)Encoding.ASCII.GetByteCount(toSend);
if (TxTerm != null) l += (uint)TxTerm.GetLength(0);
byte[] b = new byte[l];
byte[] s = Encoding.ASCII.GetBytes(toSend);
int i;
for (i = 0; (i <= s.GetUpperBound(0)); i++) b[i] = s[i];
if (TxTerm != null) for (int j = 0; (j <= TxTerm.GetUpperBound(0)); j++, i++) b[i] = (byte)TxTerm[j];
Send(b);
}
///
/// Transmits the ASCII representation of a string followed by the set terminator bytes and then
/// awaits a response string.
///
/// The string to be sent.
/// The response string.
protected string Transact(string toSend)
{
Send(toSend);
TransFlag.Reset();
if (!TransFlag.WaitOne((int)TransTimeout, false)) ThrowException("Timeout");
string s;
lock(RxString) {s = RxString;}
return s;
}
///
/// If a derived class overrides ComSettings(), it must call this prior to returning the settings to
/// the base class.
///
/// Class containing the appropriate settings.
protected void Setup(CommLineSettings s)
{
RxBuffer = new byte[s.rxStringBufferSize];
RxTerm = s.rxTerminator;
RxFilter = s.rxFilter;
TransTimeout = (uint)s.transactTimeout;
TxTerm = s.txTerminator;
}
///
/// Override this to process unsolicited input lines (not a result of Transact).
///
/// String containing the received ASCII text.
protected virtual void OnRxLine(string s) {}
///
/// 按个字节接受数据
///
///
protected override void OnRxChar(byte ch)
{
ASCII ca = (ASCII)ch;
if ((ca == RxTerm) || (RxBufferP > RxBuffer.GetUpperBound(0)))
{
//JH 1.1: Use static encoder for efficiency. Thanks to Prof. Dr. Peter Jesorsky!
lock(RxString) {RxString = Encoding.ASCII.GetString(RxBuffer, 0, (int)RxBufferP);}
RxBufferP = 0;
if (TransFlag.WaitOne(0,false))
{
OnRxLine(RxString);
}
else
{
TransFlag.Set();
}
}
else
{
bool wr = true;
if (RxFilter != null)
{
for (int i=0; i <= RxFilter.GetUpperBound(0); i++) if (RxFilter[i] == ca) wr = false;
}
if (wr)
{
RxBuffer[RxBufferP] = ch;
RxBufferP++;
}
}
}
}
///
/// Exception used for all errors.
///
public class CommPortException : ApplicationException
{
///
/// Constructor for raising direct exceptions
///
/// Description of error
public CommPortException(string desc) : base(desc) {}
///
/// Constructor for re-raising exceptions from receive thread
///
/// Inner exception raised on receive thread
public CommPortException(Exception e) : base("Receive Thread Exception", e) {}
}
internal class Win32Com
{
///
/// Opening Testing and Closing the Port Handle.
///
[DllImport("kernel32.dll", SetLastError=true)]
internal static extern IntPtr CreateFile(String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode,
IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile);
//Constants for errors:
internal const UInt32 ERROR_FILE_NOT_FOUND = 2;
internal const UInt32 ERROR_INVALID_NAME = 123;
internal const UInt32 ERROR_ACCESS_DENIED = 5;
internal const UInt32 ERROR_IO_PENDING = 997;
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);
///
/// Manipulating the communications settings.
///
[DllImport("kernel32.dll")]
internal static extern Boolean GetCommState(IntPtr hFile, ref DCB lpDCB);
[DllImport("kernel32.dll")]
internal static extern Boolean GetCommTimeouts(IntPtr hFile, out COMMTIMEOUTS lpCommTimeouts);
[DllImport("kernel32.dll")]
internal static extern Boolean BuildCommDCBAndTimeouts(String lpDef, ref DCB lpDCB, ref COMMTIMEOUTS lpCommTimeouts);
[DllImport("kernel32.dll")]
internal static extern Boolean SetCommState(IntPtr hFile, [In] ref DCB lpDCB);
[DllImport("kernel32.dll")]
internal static extern Boolean SetCommTimeouts(IntPtr hFile, [In] ref COMMTIMEOUTS lpCommTimeouts);
[DllImport("kernel32.dll")]
internal static extern Boolean SetupComm(IntPtr hFile, UInt32 dwInQueue, UInt32 dwOutQueue);
[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);
}
}
///
/// Reading and writing.
///
[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);
///
/// Control port functions.
///
[DllImport("kernel32.dll")]
internal static extern Boolean EscapeCommFunction(IntPtr hFile, UInt32 dwFunc);
// Constants for dwFunc:
internal const UInt32 SETXOFF = 1;
internal const UInt32 SETXON = 2;
internal const UInt32 SETRTS = 3;
internal const UInt32 CLRRTS = 4;
internal const UInt32 SETDTR = 5;
internal const UInt32 CLRDTR = 6;
internal const UInt32 RESETDEV = 7;
internal const UInt32 SETBREAK = 8;
internal const UInt32 CLRBREAK = 9;
[DllImport("kernel32.dll")]
internal static extern Boolean GetCommModemStatus(IntPtr hFile, out UInt32 lpModemStat);
// Constants for lpModemStat:
internal const UInt32 MS_CTS_ON = 0x0010;
internal const UInt32 MS_DSR_ON = 0x0020;
internal const UInt32 MS_RING_ON = 0x0040;
internal const UInt32 MS_RLSD_ON = 0x0080;
///
/// Status Functions.
///
[DllImport("kernel32.dll", SetLastError=true)]
internal static extern Boolean GetOverlappedResult(IntPtr hFile, IntPtr lpOverlapped,
out UInt32 nNumberOfBytesTransferred, Boolean bWait);
[DllImport("kernel32.dll",SetLastError=true)]
internal static extern Boolean PurgeComm(IntPtr hFile, uint dwFlags);
internal const uint PURGE_TXABORT = 0x0001; // Kill the pending/current writes to the comm port.
internal const uint PURGE_RXABORT = 0x0002; // Kill the pending/current reads to the comm port.
internal const uint PURGE_TXCLEAR = 0x0004; // Kill the transmit queue if there.
internal const uint PURGE_RXCLEAR = 0x0008; // Kill the typeahead buffer if there.
[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;
}
}
}