using System; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace TestSerialDLL { public class SerialWrapper : IDisposable { #region Enum public enum StopBits { None, One, Two, OnePointFive, } public enum Parity { None, Odd, Even, Mark, Space, } #endregion #region Fields /// /// The baud rate at which the communications device operates. /// private readonly int iBaudRate; /// /// The number of bits in the bytes to be transmitted and received. /// private readonly byte byteSize; /// /// The system handle to the serial port connection ('file' handle). /// private IntPtr pHandle = IntPtr.Zero; /// /// The parity scheme to be used. /// private readonly Parity parity; /// /// The name of the serial port to connect to. /// private readonly string sPortName; /// /// The number of bits in the bytes to be transmitted and received. /// private readonly StopBits stopBits; #endregion #region Constructor /// /// Creates a new instance of SerialCom. /// /// The name of the serial port to connect to /// The baud rate at which the communications device operates /// The number of stop bits to be used /// The parity scheme to be used /// The number of bits in the bytes to be transmitted and received public SerialWrapper(string portName, int baudRate, StopBits stopBits, Parity parity, byte byteSize) { if (stopBits == StopBits.None) throw new ArgumentException("stopBits cannot be StopBits.None", "stopBits"); if (byteSize < 5 || byteSize > 8) throw new ArgumentOutOfRangeException("The number of data bits must be 5 to 8 bits.", "byteSize"); if (baudRate < 110 || baudRate > 256000) throw new ArgumentOutOfRangeException("Invalid baud rate specified.", "baudRate"); if ((byteSize == 5 && stopBits == StopBits.Two) || (stopBits == StopBits.OnePointFive && byteSize > 5)) throw new ArgumentException("The use of 5 data bits with 2 stop bits is an invalid combination, " + "as is 6, 7, or 8 data bits with 1.5 stop bits."); this.sPortName = portName; this.iBaudRate = baudRate; this.byteSize = byteSize; this.stopBits = stopBits; this.parity = parity; } /// /// Creates a new instance of SerialCom. /// /// The name of the serial port to connect to /// The baud rate at which the communications device operates /// The number of stop bits to be used /// The parity scheme to be used public SerialWrapper(string portName, int baudRate, StopBits stopBits, Parity parity) : this(portName, baudRate, stopBits, parity, 8) { } #endregion #region Open /// /// Opens and initializes the serial connection. /// /// Whether or not the operation succeeded public bool Open() { pHandle = CreateFile(this.sPortName, FileAccess.ReadWrite, FileShare.None, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero); if (pHandle == IntPtr.Zero) return false; if (ConfigureSerialPort()) return true; else { Dispose(); return false; } } #endregion #region Write /// /// Transmits the specified array of bytes. /// /// The bytes to write /// The number of bytes written (-1 if error) public int Write(byte[] data) { FailIfNotConnected(); if (data == null) return 0; int bytesWritten; if (WriteFile(pHandle, data, data.Length, out bytesWritten, 0)) return bytesWritten; return -1; } /// /// Transmits the specified string. /// /// The string to write /// The number of bytes written (-1 if error) public int Write(string data) { FailIfNotConnected(); // convert the string to bytes byte[] bytes; if (data == null) { bytes = null; } else { bytes = Encoding.UTF8.GetBytes(data); } return Write(bytes); } /// /// Transmits the specified string and appends the carriage return to the end /// if it does not exist. /// /// /// Note that the string must end in '\r\n' before any serial device will interpret the data /// sent. For ease of programmability, this method should be used instead of Write() when you /// want to automatically execute the specified command string. /// /// The string to write /// The number of bytes written (-1 if error) public int WriteLine(string data) { if (data != null && !data.EndsWith("\r\n")) data += "\r\n"; return Write(data); } #endregion #region Read /// /// Reads any bytes that have been received and writes them to the specified array. /// /// The array to write the read data to /// The number of bytes read (-1 if error) public int Read(byte[] data) { FailIfNotConnected(); if (data == null) return 0; int bytesRead; if (ReadFile(pHandle, data, data.Length, out bytesRead, 0)) return bytesRead; return -1; } /// /// Reads any data that has been received as a string. /// /// The maximum number of bytes to read /// The data received (null if no data) public string ReadString(int maxBytesToRead) { if (maxBytesToRead < 1) throw new ArgumentOutOfRangeException("maxBytesToRead"); byte[] bytes = new byte[maxBytesToRead]; int numBytes = Read(bytes); //string data = ASCIIEncoding.ASCII.GetString(bytes, 0, numBytes); string data = Encoding.UTF8.GetString(bytes, 0, numBytes); return data; } #endregion #region Dispose Utils /// /// Disconnects and disposes of the SerialCom instance. /// public void Dispose() { if (pHandle != IntPtr.Zero) { CloseHandle(pHandle); pHandle = IntPtr.Zero; } } /// /// Flushes the serial I/O buffers. /// /// Whether or not the operation succeeded public bool Flush() { FailIfNotConnected(); const int PURGE_RXCLEAR = 0x0008; // input buffer const int PURGE_TXCLEAR = 0x0004; // output buffer return PurgeComm(pHandle, PURGE_RXCLEAR | PURGE_TXCLEAR); } #endregion #region Private Helpers /// /// Configures the serial device based on the connection parameters pased in by the user. /// /// Whether or not the operation succeeded private bool ConfigureSerialPort() { DCB serialConfig = new DCB(); if (GetCommState(pHandle, ref serialConfig)) { // setup the DCB struct with the serial settings we need serialConfig.BaudRate = (uint)this.iBaudRate; serialConfig.ByteSize = this.byteSize; serialConfig.fBinary = 1; // must be true serialConfig.fDtrControl = 1; // DTR_CONTROL_ENABLE "Enables the DTR line when the device is opened and leaves it on." serialConfig.fAbortOnError = 0; // false serialConfig.fTXContinueOnXoff = 0; // false serialConfig.fParity = 1; // true so that the Parity member is looked at switch (this.parity) { case Parity.Even: serialConfig.Parity = 2; break; case Parity.Mark: serialConfig.Parity = 3; break; case Parity.Odd: serialConfig.Parity = 1; break; case Parity.Space: serialConfig.Parity = 4; break; case Parity.None: default: serialConfig.Parity = 0; break; } switch (this.stopBits) { case StopBits.One: serialConfig.StopBits = 0; break; case StopBits.OnePointFive: serialConfig.StopBits = 1; break; case StopBits.Two: serialConfig.StopBits = 2; break; case StopBits.None: default: throw new ArgumentException("stopBits cannot be StopBits.None"); } if (SetCommState(pHandle, ref serialConfig)) { // set the serial connection timeouts COMMTIMEOUTS timeouts = new COMMTIMEOUTS(); timeouts.ReadIntervalTimeout = 1; timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.ReadTotalTimeoutConstant = 0; timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = 0; if (SetCommTimeouts(pHandle, ref timeouts)) { return true; } else { return false; } } else { return false; } } else { return false; } } /// /// Helper that throws a InvalidOperationException if we don't have a serial connection. /// private void FailIfNotConnected() { if (pHandle == IntPtr.Zero) throw new InvalidOperationException("You must be connected to the serial port before performing this operation."); } #endregion #region Native Helpers #region Native structures /// /// Contains the time-out parameters for a communications device. /// [StructLayout(LayoutKind.Sequential)] struct COMMTIMEOUTS { public uint ReadIntervalTimeout; public uint ReadTotalTimeoutMultiplier; public uint ReadTotalTimeoutConstant; public uint WriteTotalTimeoutMultiplier; public uint WriteTotalTimeoutConstant; } /// /// Defines the control setting for a serial communications device. /// [StructLayout(LayoutKind.Sequential)] struct DCB { public int DCBlength; public uint BaudRate; public uint Flags; public ushort wReserved; public ushort XonLim; public ushort XoffLim; public byte ByteSize; public byte Parity; public byte StopBits; public sbyte XonChar; public sbyte XoffChar; public sbyte ErrorChar; public sbyte EofChar; public sbyte EvtChar; public ushort wReserved1; public uint fBinary; public uint fParity; public uint fOutxCtsFlow; public uint fOutxDsrFlow; public uint fDtrControl; public uint fDsrSensitivity; public uint fTXContinueOnXoff; public uint fOutX; public uint fInX; public uint fErrorChar; public uint fNull; public uint fRtsControl; public uint fAbortOnError; } #endregion #region Native Methods // Used to get a handle to the serial port so that we can read/write to it. [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern IntPtr CreateFile(string fileName, [MarshalAs(UnmanagedType.U4)] FileAccess fileAccess, [MarshalAs(UnmanagedType.U4)] FileShare fileShare, IntPtr securityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, int flags, IntPtr template); // Used to close the handle to the serial port. [DllImport("kernel32.dll", SetLastError = true)] static extern bool CloseHandle(IntPtr hObject); // Used to get the state of the serial port so that we can configure it. [DllImport("kernel32.dll")] static extern bool GetCommState(IntPtr hFile, ref DCB lpDCB); // Used to configure the serial port. [DllImport("kernel32.dll")] static extern bool SetCommState(IntPtr hFile, [In] ref DCB lpDCB); // Used to set the connection timeouts on our serial connection. [DllImport("kernel32.dll", SetLastError = true)] static extern bool SetCommTimeouts(IntPtr hFile, ref COMMTIMEOUTS lpCommTimeouts); // Used to read bytes from the serial connection. [DllImport("kernel32.dll")] static extern bool ReadFile(IntPtr hFile, byte[] lpBuffer, int nNumberOfBytesToRead, out int lpNumberOfBytesRead, int lpOverlapped); // Used to write bytes to the serial connection. [DllImport("kernel32.dll", SetLastError = true)] static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, int nNumberOfBytesToWrite, out int lpNumberOfBytesWritten, int lpOverlapped); // Used to flush the I/O buffers. [DllImport("kernel32.dll", SetLastError = true)] static extern bool PurgeComm(IntPtr hFile, int dwFlags); #endregion #endregion } } // using System; // using System.Collections.Generic; // using System.Linq; // using System.Runtime.InteropServices; // using System.Text; // using System; // using System.IO.Ports; // using System.Threading; // // namespace Scale // { // // Use this code inside a project created with the Visual C# > Windows Desktop > Console Application template. // // Replace the code in Program.cs with this code. // // // public class PortChat // { // static bool _continue; // static SerialPort _serialPort; // // public static void Main() // { // string name; // string message; // StringComparer stringComparer = StringComparer.OrdinalIgnoreCase; // Thread readThread = new Thread(Read); // // // Create a new SerialPort object with default settings. // _serialPort = new SerialPort(); // // // Allow the user to set the appropriate properties. // _serialPort.PortName = SetPortName(_serialPort.PortName); // _serialPort.BaudRate = SetPortBaudRate(_serialPort.BaudRate); // _serialPort.Parity = SetPortParity(_serialPort.Parity); // _serialPort.DataBits = SetPortDataBits(_serialPort.DataBits); // _serialPort.StopBits = SetPortStopBits(_serialPort.StopBits); // _serialPort.Handshake = SetPortHandshake(_serialPort.Handshake); // // // Set the read/write timeouts // _serialPort.ReadTimeout = 500; // _serialPort.WriteTimeout = 500; // // _serialPort.Open(); // _continue = true; // readThread.Start(); // // Console.Write("Name: "); // name = Console.ReadLine(); // // Console.WriteLine("Type QUIT to exit"); // // while (_continue) // { // message = Console.ReadLine(); // // if (stringComparer.Equals("quit", message)) // { // _continue = false; // } // else // { // _serialPort.WriteLine( // String.Format("<{0}>: {1}", name, message)); // } // } // // readThread.Join(); // _serialPort.Close(); // } // // public static void Read() // { // while (_continue) // { // try // { // string message = _serialPort.ReadLine(); // Console.WriteLine(message); // } // catch (TimeoutException) { } // } // } // // // Display Port values and prompt user to enter a port. // public static string SetPortName(string defaultPortName) // { // string portName; // // Console.WriteLine("Available Ports:"); // foreach (string s in SerialPort.GetPortNames()) // { // Console.WriteLine(" {0}", s); // } // // Console.Write("Enter COM port value (Default: {0}): ", defaultPortName); // portName = Console.ReadLine(); // // if (portName == "" || !(portName.ToLower()).StartsWith("com")) // { // portName = defaultPortName; // } // return portName; // } // // Display BaudRate values and prompt user to enter a value. // public static int SetPortBaudRate(int defaultPortBaudRate) // { // string baudRate; // // Console.Write("Baud Rate(default:{0}): ", defaultPortBaudRate); // baudRate = Console.ReadLine(); // // if (baudRate == "") // { // baudRate = defaultPortBaudRate.ToString(); // } // // return int.Parse(baudRate); // } // // // Display PortParity values and prompt user to enter a value. // public static Parity SetPortParity(Parity defaultPortParity) // { // string parity; // // Console.WriteLine("Available Parity options:"); // foreach (string s in Enum.GetNames(typeof(Parity))) // { // Console.WriteLine(" {0}", s); // } // // Console.Write("Enter Parity value (Default: {0}):", defaultPortParity.ToString(), true); // parity = Console.ReadLine(); // // if (parity == "") // { // parity = defaultPortParity.ToString(); // } // // return (Parity)Enum.Parse(typeof(Parity), parity, true); // } // // Display DataBits values and prompt user to enter a value. // public static int SetPortDataBits(int defaultPortDataBits) // { // string dataBits; // // Console.Write("Enter DataBits value (Default: {0}): ", defaultPortDataBits); // dataBits = Console.ReadLine(); // // if (dataBits == "") // { // dataBits = defaultPortDataBits.ToString(); // } // // return int.Parse(dataBits.ToUpperInvariant()); // } // // // Display StopBits values and prompt user to enter a value. // public static StopBits SetPortStopBits(StopBits defaultPortStopBits) // { // string stopBits; // // Console.WriteLine("Available StopBits options:"); // foreach (string s in Enum.GetNames(typeof(StopBits))) // { // Console.WriteLine(" {0}", s); // } // // Console.Write("Enter StopBits value (None is not supported and \n" + // "raises an ArgumentOutOfRangeException. \n (Default: {0}):", defaultPortStopBits.ToString()); // stopBits = Console.ReadLine(); // // if (stopBits == "") // { // stopBits = defaultPortStopBits.ToString(); // } // // return (StopBits)Enum.Parse(typeof(StopBits), stopBits, true); // } // public static Handshake SetPortHandshake(Handshake defaultPortHandshake) // { // string handshake; // // Console.WriteLine("Available Handshake options:"); // foreach (string s in Enum.GetNames(typeof(Handshake))) // { // Console.WriteLine(" {0}", s); // } // // Console.Write("Enter Handshake value (Default: {0}):", defaultPortHandshake.ToString()); // handshake = Console.ReadLine(); // // if (handshake == "") // { // handshake = defaultPortHandshake.ToString(); // } // // return (Handshake)Enum.Parse(typeof(Handshake), handshake, true); // } // } // } //