C# serial port code for CF

T

Tom

Hello All,

I have ported John Hinds excellent MSDN article on .net
serial port programming using C# to the Compact Framework.
The article is at
http://msdn.microsoft.com/msdnmag/issues/02/10/NETSerialCom
m/

I would like to share my work but I am not sure where to
post it, so I will include it inline here. I have updated
John's base class CommBase.cs for use with ce.net 4.1. It
still works on .net. Just define CE_NET for use with the
CF.

cheers,
Tom.


using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.IO;
using System.Xml.Serialization;

//JH1.1: Version 1.1 changes labelled thus.
//JH1.2: Version 1.2 changes labelled thus.
// Tom, 7/03 - port to Compact Framework

namespace JH.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;
#if CE_NET
[DllImport("coredll")]
extern static IntPtr LocalAlloc( int
flags, int size );
[DllImport("coredll")]
extern static void LocalFree( IntPtr ptr );
private object pLock = new object();
private Boolean rxThreadCancelled = false;
private Boolean rxThreadStopped = true;
#endif
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;

/// <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;
//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 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 int rxLowWater = 0;
/// <summary>
/// Multiplier. Max time for Send
in ms = (Multiplier * Characters) + Constant
/// (default: 0 = No timeout)
/// </summary>
public uint sendTimeoutMultiplier
= 0;
/// <summary>
/// Constant. Max time for Send
in ms = (Multiplier * Characters) + Constant (default: 0)
/// </summary>
public uint 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)
{
#if !CE_NET
XmlSerializer sr = new
XmlSerializer(this.GetType());
sr.Serialize(s, this);
#endif
}

/// <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)
{
#if CE_NET
return null;
#else
XmlSerializer sr = new
XmlSerializer(t);
try
{
return
(CommBaseSettings)sr.Deserialize(s);
}
catch
{
return null;
}
#endif
}
}

/// <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();
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
{
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;
#if CE_NET
ptrUWO = LocalAlloc
(0,Marshal.SizeOf(wo));
Marshal.StructureToPtr(wo, ptrUWO,
false);
#else
ptrUWO = Marshal.AllocHGlobal
(Marshal.SizeOf(wo));
Marshal.StructureToPtr(wo, ptrUWO,
true);
#endif
writeCount = 0;

rxException = null;
rxExceptionReported = false;
rxThread = new Thread(new
ThreadStart(this.ReceiveThread));
#if !CE_NET
rxThread.Name = "CommBaseRx";
#endif
rxThread.Priority =
ThreadPriority.AboveNormal;
rxThread.Start();
#if CE_NET
rxThreadStopped = false;
#endif


//JH1.2: More robust thread start-
up wait.
#if CE_NET
Thread.Sleep(500); //Give rx
thread time to start. By documentation, 0 should work, but
it does not!
#else
startEvent.WaitOne(500, false);
#endif
auto = false;
if (AfterOpen())
{
auto = cs.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)
{
#if CE_NET
lock(pLock)
{
rxThreadCancelled
= true;
}
while(rxThreadStopped ==
false)
Thread.Sleep(500);
#else
rxThread.Abort();
#endif
rxThread = null;
}
Win32Com.CloseHandle(hPort);
if (ptrUWO != IntPtr.Zero)
#if CE_NET
LocalFree(ptrUWO);
#else
Marshal.FreeHGlobal
(ptrUWO);
#endif
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>
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("Unexpected failure");
}
}

/// <summary>
/// Queues a single byte for transmission.
/// </summary>
/// <param name="tosend">Byte to be
sent</param>
protected void Send(byte tosend)
{
byte[] b = new byte[1];
b[0] = tosend;
Send(b);
}

private void CheckResult()
{
uint sent = 0;
if (writeCount > 0)
{
if
(Win32Com.GetOverlappedResult(hPort, ptrUWO, out sent,
checkSends))
{
writeCount -= (int)
sent;
if (writeCount !=
0) ThrowException("Send Timeout");
}
else
{
if
(Marshal.GetLastWin32Error() != Win32Com.ERROR_IO_PENDING)
ThrowException("Unexpected failure");
}
}
}

/// <summary>
/// Sends a protocol byte immediately
ahead of any queued bytes.
/// </summary>
/// <param name="tosend">Byte to
send</param>
/// <returns>False if an immediate byte is
already scheduled and not yet sent</returns>
protected void SendImmediate(byte tosend) {
CheckOnline();
if (!Win32Com.TransmitCommChar
(hPort, tosend)) ThrowException("Transmission failure");
}

/// <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);
}

/// <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))
stateRTS =
1;
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>CommBaseSettings, or derived
object with required settings initialised</returns>
protected virtual CommBaseSettings
CommSettings() {return new CommBaseSettings();}

/// <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>
protected virtual void OnRxChar(byte ch) {}

/// <summary>
/// Override this to take action when
transmission is complete (i.e. all bytes have actually
/// been sent, not just queued).
/// </summary>
protected virtual void OnTxDone() {}

/// <summary>
/// Override this to take action when a
break condition is detected on the input line.
/// </summary>
protected virtual void OnBreak() {}

/// <summary>
/// Override this to take action when a
ring condition is signalled by an attached modem.
/// </summary>
protected virtual void OnRing() {}

/// <summary>
/// Override this to take action when one
or more modem status inputs change state
/// </summary>
/// <param name="mask">The status inputs
that have changed state</param>
/// <param name="state">The state of the
status inputs</param>
protected virtual void OnStatusChange
(ModemStatus mask, ModemStatus state) {}

/// <summary>
/// Override this to take action when the
reception thread closes due to an exception being thrown.
/// </summary>
/// <param name="e">The exception which
was thrown</param>
protected virtual void OnRxException
(Exception e) {}

private void ReceiveThread() {
byte[] buf = new Byte[1];
uint gotbytes;
bool starting;

starting = true;
AutoResetEvent sg = new
AutoResetEvent(false);
Win32Com.OVERLAPPED ov = new
Win32Com.OVERLAPPED();
ov.Offset = 0; ov.OffsetHigh = 0;
ov.hEvent = sg.Handle;
#if CE_NET
IntPtr unmanagedOv = LocalAlloc
(0,Marshal.SizeOf(ov));
Marshal.StructureToPtr(ov,
unmanagedOv, false);
#else
IntPtr unmanagedOv =
Marshal.AllocHGlobal(Marshal.SizeOf(ov));
Marshal.StructureToPtr(ov,
unmanagedOv, true);
#endif
uint eventMask = 0;
#if CE_NET
IntPtr uMask = LocalAlloc
(0,Marshal.SizeOf(eventMask));
rxThreadStopped = false;
#else
IntPtr uMask = Marshal.AllocHGlobal
(Marshal.SizeOf(eventMask));
#endif
try
{
#if CE_NET
while(rxThreadCancelled ==
false)
#else
while(true)
#endif
{
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);
//JH1.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))
{

//JH1.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))
{

//JH1.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!

throw new CommPortException("IO Error [004]");
}
if
(gotbytes == 1) OnRxChar(buf[0]);
} while
(gotbytes > 0);
}
if ((eventMask &
Win32Com.EV_TXEMPTY) != 0)
{
OnTxDone();
}
if ((eventMask &
Win32Com.EV_BREAK) != 0) OnBreak();

uint i = 0;
if ((eventMask &
Win32Com.EV_CTS) != 0) i |= Win32Com.MS_CTS_ON;
if ((eventMask &
Win32Com.EV_DSR) != 0) i |= Win32Com.MS_DSR_ON;
if ((eventMask &
Win32Com.EV_RLSD) != 0) i |= Win32Com.MS_RLSD_ON;
if ((eventMask &
Win32Com.EV_RING) != 0) i |= Win32Com.MS_RING_ON;
if (i != 0)
{
uint f;
if (!
Win32Com.GetCommModemStatus(hPort, out f)) throw new
CommPortException("IO Error [005]");

OnStatusChange(new ModemStatus(i), new ModemStatus
(f));
}
}
}
catch(Exception e)
{
#if !CE_NET
if (uMask != IntPtr.Zero)
Marshal.FreeHGlobal(uMask);
if (unmanagedOv !=
IntPtr.Zero) Marshal.FreeHGlobal(unmanagedOv);
if (!(e is
ThreadAbortException))
{
rxException = e;
OnRxException(e);
}
#else
if (uMask != IntPtr.Zero)
{
LocalFree(uMask);
}
if (unmanagedOv !=
IntPtr.Zero)
{
LocalFree
(unmanagedOv);
}
Console.WriteLine
("ReceiveThread: Exception-" + e.ToString());
lock(pLock)
{
rxThreadStopped =
true;
}
#endif
}
}

private bool CheckOnline()
{
if ((rxException != null) && (!
rxExceptionReported))
{
rxExceptionReported = true;
ThrowException("rx");
}
if (online)
{
//JH1.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>
/// 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).
/// </summary>
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;

/// <summary>
/// Extends CommBaseSettings to add the
settings used by CommLine.
/// </summary>
public class CommLineSettings :
CommBase.CommBaseSettings
{
/// <summary>
/// Maximum size of received
string (default: 256)
/// </summary>
public int rxStringBufferSize =
256;
/// <summary>
/// ASCII code that terminates a
received string (default: CR)
/// </summary>
public ASCII rxTerminator =
ASCII.CR;
/// <summary>
/// ASCII codes that will be
ignored in received string (default: null)
/// </summary>
public ASCII[] rxFilter;
/// <summary>
/// Maximum time (ms) for the
Transact method to complete (default: 500)
/// </summary>
public int transactTimeout = 500;
/// <summary>
/// ASCII codes transmitted after
each Send string (default: null)
/// </summary>
public ASCII[] txTerminator;

public static new CommLineSettings
LoadFromXML(Stream s)
{
return (CommLineSettings)
LoadFromXML(s, typeof(CommLineSettings));
}
}

/// <summary>
/// Queue the ASCII representation of a
string and then the set terminator bytes for sending.
/// </summary>
/// <param name="toSend">String to be
sent.</param>
protected void Send(string toSend)
{
//JH1.1: Use static encoder for
efficiency. Thanks to Prof. Dr. Peter Jesorsky!
Encoding enc = Encoding.ASCII;
uint l = (uint)enc.GetByteCount
(toSend);
if (TxTerm != null) l += (uint)
TxTerm.GetLength(0);
byte[] b = new byte[l];
byte[] s = enc.GetBytes(toSend);
int i;
for (i = 0; (i <= s.GetUpperBound
(0)); i++) b = s;
if (TxTerm != null) for (int j =
0; (j <= TxTerm.GetUpperBound(0)); j++, i++) b = (byte)
TxTerm[j];
Send(b);
}

/// <summary>
/// Transmits the ASCII representation of
a string followed by the set terminator bytes and then
/// awaits a response string.
/// </summary>
/// <param name="toSend">The string to be
sent.</param>
/// <returns>The response string.</returns>
protected string Transact(string toSend) {
Send(toSend);
TransFlag.Reset();
#if CE_NET
if (!TransFlag.WaitOne())
ThrowException("Timeout");
#else
if (!TransFlag.WaitOne((int)
TransTimeout, false)) ThrowException("Timeout");
#endif
string s;
lock(RxString) {s = RxString;}
return s;
}

/// <summary>
/// If a derived class overrides
ComSettings(), it must call this prior to returning the
settings to
/// the base class.
/// </summary>
/// <param name="s">Class containing the
appropriate settings.</param>
protected void Setup(CommLineSettings s) {
RxBuffer = new byte
[s.rxStringBufferSize];
RxTerm = s.rxTerminator;
RxFilter = s.rxFilter;
TransTimeout = (uint)
s.transactTimeout;
TxTerm = s.txTerminator;
}

/// <summary>
/// Override this to process unsolicited
input lines (not a result of Transact).
/// </summary>
/// <param name="s">String containing the
received ASCII text.</param>
protected virtual void OnRxLine(string s)
{}

protected override void OnRxChar(byte ch) {
ASCII ca = (ASCII)ch;
if ((ca == RxTerm) || (RxBufferP >
RxBuffer.GetUpperBound(0))) {
//JH1.1: Use static
encoder for efficiency. Thanks to Prof. Dr. Peter Jesorsky!
Encoding enc =
Encoding.ASCII;
lock(RxString) {RxString =
enc.GetString(RxBuffer, 0, (int)RxBufferP);}
RxBufferP = 0;
#if CE_NET
if (TransFlag.WaitOne()) {
#else
if (TransFlag.WaitOne
(0,false)) {
#endif
OnRxLine(RxString);
} else {
TransFlag.Set();
}
} else {
bool wr = true;
if (RxFilter != null) {
for (int i=0; i <=
RxFilter.GetUpperBound(0); i++) if (RxFilter == ca) wr
= false;
}
if (wr) {
RxBuffer
[RxBufferP] = ch;
RxBufferP++;
}
}
}
}

/// <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>
#if CE_NET
[DllImport("coredll.dll",
SetLastError=true)]
#else
[DllImport("kernel32.dll",
SetLastError=true)]
#endif
internal static extern IntPtr CreateFile
(String lpFileName, UInt32 dwDesiredAccess, UInt32
dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile);

//Constants for errors:
internal const UInt32
ERROR_FILE_NOT_FOUND = 2;
internal const UInt32
ERROR_INVALID_NAME = 123;
internal const UInt32
ERROR_ACCESS_DENIED = 5;
internal const UInt32
ERROR_IO_PENDING = 997;

//Constants for return value:
internal const Int32
INVALID_HANDLE_VALUE = -1;

//Constants for dwFlagsAndAttributes:
internal const UInt32
FILE_FLAG_OVERLAPPED = 0x40000000;

//Constants for dwCreationDisposition:
internal const UInt32
OPEN_EXISTING = 3;

//Constants for dwDesiredAccess:
internal const UInt32 GENERIC_READ
= 0x80000000;
internal const UInt32
GENERIC_WRITE = 0x40000000;
#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
internal static extern Boolean CloseHandle
(IntPtr hObject);

/// <summary>
/// Manipulating the communications
settings.
/// </summary>

#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
internal static extern Boolean GetCommState
(IntPtr hFile, ref DCB lpDCB);

#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
internal static extern Boolean
GetCommTimeouts(IntPtr hFile, out COMMTIMEOUTS
lpCommTimeouts);

#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
internal static extern Boolean
BuildCommDCBAndTimeouts(String lpDef, ref DCB lpDCB, ref
COMMTIMEOUTS lpCommTimeouts);

#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
internal static extern Boolean SetCommState
(IntPtr hFile, [In] ref DCB lpDCB);

#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
internal static extern Boolean
SetCommTimeouts(IntPtr hFile, [In] ref COMMTIMEOUTS
lpCommTimeouts);

#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
internal static extern Boolean SetupComm
(IntPtr hFile, UInt32 dwInQueue, UInt32 dwOutQueue);

[StructLayout( LayoutKind.Sequential )]
internal struct COMMTIMEOUTS
{
//JH1.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;
}
//JH1.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)
{
DCBlength = 28;
PackedValues = 0x8001;
if (parity) PackedValues
|= 0x0002;
if (outCTS) PackedValues
|= 0x0004;
if (outDSR) PackedValues
|= 0x0008;
PackedValues |= ((dtr &
0x0003) << 4);
if (inDSR) PackedValues |=
0x0040;
if (txc) PackedValues |=
0x0080;
if (xOut) PackedValues |=
0x0100;
if (xIn) PackedValues |=
0x0200;
PackedValues |= ((rts &
0x0003) << 12);

}
}

/// <summary>
/// Reading and writing.
/// </summary>
#if CE_NET
[DllImport("coredll.dll",
SetLastError=true)]
#else
[DllImport("kernel32.dll",
SetLastError=true)]
#endif
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;
}

#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
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;

#if CE_NET
[DllImport("coredll.dll",
SetLastError=true)]
#else
[DllImport("kernel32.dll",
SetLastError=true)]
#endif
internal static extern Boolean
WaitCommEvent(IntPtr hFile, IntPtr lpEvtMask, IntPtr
lpOverlapped);

#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
internal static extern Boolean CancelIo
(IntPtr hFile);

#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
internal static extern Boolean ReadFile
(IntPtr hFile, [Out] Byte[] lpBuffer, UInt32
nNumberOfBytesToRead,
out UInt32 nNumberOfBytesRead,
IntPtr lpOverlapped);

#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
internal static extern Boolean
TransmitCommChar(IntPtr hFile, Byte cChar);

/// <summary>
/// Control port functions.
/// </summary>
#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
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;

#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
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>
#if CE_NET
[DllImport("coredll.dll",
SetLastError=true)]
#else
[DllImport("kernel32.dll",
SetLastError=true)]
#endif
internal static extern Boolean
GetOverlappedResult(IntPtr hFile, IntPtr lpOverlapped,
out UInt32
nNumberOfBytesTransferred, Boolean bWait);

#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
internal static extern Boolean
ClearCommError(IntPtr hFile, out UInt32 lpErrors, IntPtr
lpStat);
#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
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;
}
#if CE_NET
[DllImport("coredll.dll")]
#else
[DllImport("kernel32.dll")]
#endif
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;
}

}

}
 
B

Bert Hyman

In "Tom"
XmlSerializer sr = new XmlSerializer(this.GetType());

The Compact Framework doesn't seem to include the XmlSerializer (at least
not the copy I have).
 
Joined
Feb 10, 2006
Messages
1
Reaction score
0
Source Code

I was wondering if you could post a zip of the source code or email it to me directly? I copied the code you pasted in the window but the formatting is all off and the line breaks are wrong. I could go through and try and fix it all, but it is a long file and I have tried similar things in the past and I usually end up screwing up the file. It would be much easier to just get a copy of the original. Also, the original file from John Hind appears to rely heavily on overlapped communication that is not allowed in ce. How did you get around this? Do all the calls block?

Thanks
David
 
Joined
Jul 16, 2007
Messages
1
Reaction score
0
Windows CE Serial driver (1.3)

I have found Tom's revisions for the windows ce invaluable and wanted to give back.

Windows CE does support overlapped IO and this works fine (window 95/98 do not support Overlapped io), so yes the wait event and read calls do block.

I have also incorporated changes made by John Hind in 1.3 in the attached version and I believe I have incorporated and documented all John Hinds 1.3 changes correctly.

However calling CancelIO exepted (due to missing method exception and possible defined wrongly but I could not find out why) and when removed the method did seem to close the port but any attempt to write to it again would never work. Any one got any ideas?

The attachment contains the formatted code, I hope it is as useful to you as it was too me (or try http://www.jarman.org.uk/downloads/serial_1_3_ce.zip).

Cheers

DJ
 

Attachments

  • serial_1_3_ce.zip
    13.7 KB · Views: 6,665
Joined
Mar 13, 2008
Messages
1
Reaction score
0
rxStringBufferSize = 256

NMR said:
I spendt nearly to hours trying to make this working. It didn't happen:
FILE_FLAG_OVERLAPPED is NOT supported by windows ce 4.2 and older. The link below will explain :-----------------------------------((((((((((((((((((((((((


http://msdn2.microsoft.com/en-us/library/ms923949.aspx

Hello, I am using this program to receive data by the serial port of computers but I have a problem, rxStringBufferSize value is equal to 256 if this attempt to change the value SerialPort.CommBase function does not work, but because I need more characters from a receipt machine more than 256 characters. Anyone know how I can solve this problem. Thanks every body.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top