📄 commbase.cs
字号:
return PortStatus.available;
}
/// <summary>
/// Opens the com port and configures it with the required settings
/// </summary>
/// <returns>false if the port could not be opened</returns>
public bool Open()
{
Win32Com.DCB PortDCB = new Win32Com.DCB();
Win32Com.COMMTIMEOUTS CommTimeouts = new Win32Com.COMMTIMEOUTS();
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;
}
}
/// <summary>
/// Closes the com port.
/// </summary>
public void Close()
{
if (online)
{
auto = false;
BeforeClose(false);
InternalClose();
rxException = null;
}
}
private void InternalClose()
{
Win32Com.CancelIo(hPort);
if (rxThread != null)
{
rxThread.Abort();
//JH 1.3: Improve robustness of Close in case were followed by Open:
rxThread.Join(100);
rxThread = null;
}
Win32Com.CloseHandle(hPort);
if (ptrUWO != IntPtr.Zero) Marshal.FreeHGlobal(ptrUWO);
stateRTS = 2;
stateDTR = 2;
stateBRK = 2;
online = false;
}
/// <summary>
/// For IDisposable
/// </summary>
public void Dispose() {Close();}
/// <summary>
/// Destructor (just in case)
/// </summary>
~CommBase() {Close();}
/// <summary>
/// True if online.
/// </summary>
public bool Online {get {if (!online) return false; else return CheckOnline();}}
/// <summary>
/// Block until all bytes in the queue have been transmitted.
/// </summary>
public void Flush() {
CheckOnline();
CheckResult();
}
/// <summary>
/// Use this to throw exceptions in derived classes. Correctly handles threading issues
/// and closes the port if necessary.
/// </summary>
/// <param name="reason">Description of fault</param>
protected void ThrowException(string reason)
{
if (Thread.CurrentThread == rxThread)
{
throw new CommPortException(reason);
}
else
{
if (online)
{
BeforeClose(true);
InternalClose();
}
if (rxException == null)
{
throw new CommPortException(reason);
}
else
{
throw new CommPortException(rxException);
}
}
}
/// <summary>
/// Queues bytes for transmission.
/// </summary>
/// <param name="tosend">Array of bytes to be sent</param>
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;
}
}
/// <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;
//JH 1.3: Fixed a number of problems working with checkSends == false. Byte counting was unreliable because
//occasionally GetOverlappedResult would return true with a completion having missed one or more previous
//completions. The test for ERROR_IO_INCOMPLETE was incorrectly for ERROR_IO_PENDING instead.
if (writeCount > 0)
{
if (Win32Com.GetOverlappedResult(hPort, ptrUWO, out sent, checkSends))
{
if (checkSends)
{
writeCount -= (int)sent;
if (writeCount != 0) ThrowException("Send Timeout");
writeCount = 0;
}
}
else
{
if (Marshal.GetLastWin32Error() != Win32Com.ERROR_IO_INCOMPLETE) ThrowException("Write Error");
}
}
}
/// <summary>
/// Sends a protocol byte immediately ahead of any queued bytes.
/// </summary>
/// <param name="tosend">Byte to send</param>
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>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -