⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 commbase.cs

📁 封装COM的操作的Windows API
💻 CS
📖 第 1 页 / 共 4 页
字号:
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.IO;
using System.Xml.Serialization;

namespace BOCO.APP.CommBase
{

	/// <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 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);
		private int stateRTS = 2;
		private int stateDTR = 2;
		private int stateBRK = 2;
		
		/// <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>
		public class CommBaseSettings 
		{
			/// <summary>
			/// Port Name (default: "COM1:")
			/// </summary>
			public string port = "COM1:";
			/// <summary>
			/// Baud Rate (default: 2400) unsupported rates will throw "Bad settings"
			/// </summary>
			public int baudRate = 2400;
			/// <summary>
			/// The parity checking scheme (default: none)
			/// </summary>
			public Parity parity = Parity.none;
			/// <summary>
			/// Number of databits 1..8 (default: 8) unsupported values will throw "Bad settings"
			/// </summary>
			public int dataBits = 8;
			/// <summary>
			/// Number of stop bits (default: one)
			/// </summary>
			public StopBits stopBits = StopBits.one;
			/// <summary>
			/// If true, transmission is halted unless CTS is asserted by the remote station (default: false)
			/// </summary>
			public bool txFlowCTS = false;
			/// <summary>
			/// If true, transmission is halted unless DSR is asserted by the remote station (default: false)
			/// </summary>
			public bool txFlowDSR = false;
			/// <summary>
			/// If true, transmission is halted when Xoff is received and restarted when Xon is received (default: false)
			/// </summary>
			public 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 bool txWhenRxXoff = true;
			/// <summary>
			/// If true, received characters are ignored unless DSR is asserted by the remote station (default: false)
			/// </summary>
			public 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 bool rxFlowX = false;
			/// <summary>
			/// Specifies the use to which the RTS output is put (default: none)
			/// </summary>
			public HSOutput useRTS = HSOutput.none;
			/// <summary>
			/// Specidies the use to which the DTR output is put (default: none)
			/// </summary>
			public HSOutput useDTR = HSOutput.none;
			/// <summary>
			/// The character used to signal Xon for X flow control (default: DC1)
			/// </summary>
			public ASCII XonChar = ASCII.DC1;
			/// <summary>
			/// The character used to signal Xoff for X flow control (default: DC3)
			/// </summary>
			public ASCII XoffChar = ASCII.DC3;
			/// <summary>
			/// The number of free bytes in the reception queue at which flow is disabled (default: 2048)
			/// </summary>
			public int rxHighWater = 2048;
			/// <summary>
			/// The number of bytes in the reception queue at which flow is re-enabled (default: 512)
			/// </summary>
			public int rxLowWater = 512;
			/// <summary>
			/// Multiplier. Max time for Send in ms = (Multiplier * Characters) + Constant
			/// (default: 0 = No timeout)
			/// </summary>
			public int sendTimeoutMultiplier = 0;
			/// <summary>
			/// Constant.  Max time for Send in ms = (Multiplier * Characters) + Constant (default: 0)
			/// </summary>
			public int sendTimeoutConstant = 0;
			/// <summary>
			/// Requested size for receive queue (default: 0 = use operating system default)
			/// </summary>
			public int rxQueue = 0;
			/// <summary>
			/// Requested size for transmit queue (default: 0 = use operating system default)
			/// </summary>
			public 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 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 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 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;
				}
			}

			/// <summary>
			/// Save the object in XML format to a stream
			/// </summary>
			/// <param name="s">Stream to save the object to</param>
			public void SaveAsXML(Stream s)
			{
				XmlSerializer sr = new XmlSerializer(this.GetType());
				sr.Serialize(s, this);
			}

			/// <summary>
			/// Create a new CommBaseSettings object initialised from XML data
			/// </summary>
			/// <param name="s">Stream to load the XML from</param>
			/// <returns>CommBaseSettings object</returns>
			public static CommBaseSettings LoadFromXML(Stream s)
			{
				return LoadFromXML(s, typeof(CommBaseSettings));
			}

			/// <summary>
			/// 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));
			/// </summary>
			/// <param name="s">Stream to load the object from</param>
			/// <param name="t">Type of the derived object</param>
			/// <returns></returns>
			protected static CommBaseSettings LoadFromXML(Stream s, Type t)
			{
				XmlSerializer sr = new XmlSerializer(t);
				try
				{
					return (CommBaseSettings)sr.Deserialize(s);
				}
				catch
				{
					return null;
				}
			}
		}

		/// <summary>
		/// Byte type with enumeration constants for ASCII control codes.
		/// </summary>
		public enum ASCII : byte
		{
			NULL = 0x00,SOH = 0x01,STH = 0x02,ETX = 0x03,EOT = 0x04,ENQ = 0x05,ACK = 0x06,BELL = 0x07,
			BS = 0x08,HT = 0x09,LF = 0x0A,VT = 0x0B,FF = 0x0C,CR = 0x0D,SO = 0x0E,SI = 0x0F,DC1 = 0x11,
			DC2 = 0x12,DC3 = 0x13,DC4 = 0x14,NAK = 0x15,SYN = 0x16,ETB = 0x17,CAN = 0x18,EM = 0x19,
			SUB = 0x1A,ESC = 0x1B,FS = 0x1C,GS = 0x1D,RS = 0x1E,US = 0x1F,SP = 0x20,DEL = 0x7F
		}
	
		/// <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();
			CommBaseSettings cs;
			Win32Com.OVERLAPPED wo = new Win32Com.OVERLAPPED();

			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
				{
					throw new CommPortException("Port Open Failure");
				}
			}

			online = true;

			CommTimeouts.ReadIntervalTimeout = 0;
			CommTimeouts.ReadTotalTimeoutConstant = 0;
			CommTimeouts.ReadTotalTimeoutMultiplier = 0;
			CommTimeouts.WriteTotalTimeoutConstant = cs.sendTimeoutConstant;
			CommTimeouts.WriteTotalTimeoutMultiplier = cs.sendTimeoutMultiplier;
			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;
			PortDCB.XoffLim = (short)cs.rxHighWater;
			PortDCB.XonLim = (short)cs.rxLowWater;
			if ((cs.rxQueue != 0) || (cs.txQueue != 0))
				if (!Win32Com.SetupComm(hPort, (uint)cs.rxQueue, (uint)cs.txQueue)) ThrowException("Bad queue settings");
			if (!Win32Com.SetCommState(hPort, ref PortDCB)) ThrowException("Bad com settings");
			if (!Win32Com.SetCommTimeouts(hPort, ref CommTimeouts)) ThrowException("Bad timeout settings");

			stateBRK = 0;
			if (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;
						
			rxException = null;
			rxExceptionReported = false;
			rxThread = new Thread(new ThreadStart(this.ReceiveThread));
			rxThread.Name = "CommBaseRx";
			rxThread.Priority = ThreadPriority.AboveNormal;
			rxThread.Start();
			Thread.Sleep(1); //Give rx thread time to start. By documentation, 0 should work, but it does not!

			auto = false;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -