📄 serialstream.cs
字号:
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*=============================================================================
**
** Class: SerialStream
**
** Purpose: Class for enabling low-level sync and async control over a serial
** : communications resource.
**
** Date: August, 2002
**
=============================================================================*/
using System;
using System.IO;
using System.Text;
using System.ComponentModel;
using System.Resources;
using System.Runtime;
using System.Security;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Collections;
using System.Data;
using Microsoft.Win32;
using System.Threading;
using System.Runtime.Remoting.Messaging;
using System.Runtime.CompilerServices;
// Notes about the SerialStream:
// * The stream is always opened via the SerialStream constructor.
// * The handleProtector guarantees ownership of the file handle, so that it may not be
// * unnaturally closed by another process or thread. Thus, since all properties are available
// * only when the object exists, the object's properties can be queried only when the SerialStream
// * object is instantiated (i.e. "open").
// * Handles to serial communications resources here always:
// * 1) own the handle
// * 2) are opened for asynchronous operation
// * 3) set access at the level of FileAccess.ReadWrite
// * 4) Allow for reading AND writing
// * 5) Disallow seeking, since they encapsulate a file of type FILE_TYPE_CHAR
namespace System.IO.Ports
{
public delegate void SerialEventHandler(object source, SerialEventArgs e);
internal delegate int WaitEventCallback();
internal class SerialStream : Stream
{
// members supporting properties exposed to SerialPort
private string portName;
private byte parityReplace = (byte) '?';
private bool dtrEnable;
private bool rtsEnable;
private bool inBreak = false; // port is initially in non-break state
private Handshake handshake;
// The internal C# representations of Win32 structures necessary for communication
// hold most of the internal "fields" maintaining information about the port.
private UnsafeNativeMethods.DCB dcb;
private UnsafeNativeMethods.COMMTIMEOUTS commTimeouts;
private UnsafeNativeMethods.COMSTAT comStat;
private UnsafeNativeMethods.COMMPROP commProp;
// internal-use members
private const long dsrTimeout = 0L;
private const int maxDataBits = 8;
private const int minDataBits = 5;
private HandleProtector _handleProtector; // See the HandleProtector class.
internal bool lastOpTimedOut = false; // Read returns without error on timeout, so this is internally required to determine timeout.
private byte[] tempBuf; // used to avoid multiple array allocations in ReadByte()
// callback-related members, following MSDN's Asyncrhonous Programming Design Pattern.
private WaitEventCallback myWaitCommCallback;
private AsyncCallback myAsyncCallback;
private Object state;
// called whenever any async i/o operation completes.
private unsafe static readonly IOCompletionCallback IOCallback = new IOCompletionCallback(SerialStream.AsyncFSCallback);
// three different events, also wrapped by SerialPort.
internal event SerialEventHandler ReceivedEvent; // called when one character is received.
internal event SerialEventHandler PinChangedEvent; // called when any of the pin/ring-related triggers occurs
internal event SerialEventHandler ErrorEvent; // called when any runtime error occurs on the port (frame, overrun, parity, etc.)
// ----SECTION: inherited properties from Stream class ------------*
// These six properites are required for SerialStream to inherit from the abstract Stream class.
// Note four of them are always true or false, and two of them throw exceptions, so these
// are not usefully queried by applications which know they have a SerialStream, etc...
public override bool CanRead
{
get { return (!_handleProtector.IsClosed); }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return (!_handleProtector.IsClosed); }
}
public override long Length
{
get { throw new NotSupportedException(InternalResources.GetResourceString("NotSupported_UnseekableStream")); }
}
public override long Position
{
get { throw new NotSupportedException(InternalResources.GetResourceString("NotSupported_UnseekableStream")); }
set { throw new NotSupportedException(InternalResources.GetResourceString("NotSupported_UnseekableStream")); }
}
// ----- new get-set properties -----------------*
// Standard port properties, also called from SerialPort
// BaudRate may not be settable to an arbitrary integer between dwMinBaud and dwMaxBaud,
// and is limited only by the serial driver. Typically about twelve values such
// as Winbase.h's CBR_110 through CBR_256000 are used.
internal int BaudRate
{
get { return (int) dcb.BaudRate; }
set
{
if (value <= 0 || (value > commProp.dwMaxBaud && commProp.dwMaxBaud > 0))
{
// if no upper bound on baud rate imposed by serial driver, note that argument must be positive
if (commProp.dwMaxBaud == 0)
{
throw new ArgumentOutOfRangeException("baudRate",
InternalResources.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
}
else
{
// otherwise, we can present the bounds on the baud rate for this driver
throw new ArgumentOutOfRangeException("baudRate",
InternalResources.GetResourceString("ArgumentOutOfRange_Bounds_Lower_Upper", 0, commProp.dwMaxBaud));
}
}
// Set only if it's different. Rollback to previous values if setting fails.
// This pattern occurs through most of the other properties in this class.
if(value != dcb.BaudRate)
{
int baudRateOld = (int) dcb.BaudRate;
dcb.BaudRate = (uint) value;
if (UnsafeNativeMethods.SetCommState(_handleProtector.Handle, ref dcb) == false)
{
dcb.BaudRate = (uint) baudRateOld;
InternalResources.WinIOError();
}
}
}
}
internal int DataBits
{
get { return (int) dcb.ByteSize; }
set
{
if(value < minDataBits || value > maxDataBits)
{
throw new ArgumentOutOfRangeException("dataBits",
InternalResources.GetResourceString("ArgumentOutOfRange_Bounds_Lower_Upper", minDataBits, maxDataBits));
}
if (value != dcb.ByteSize)
{
byte byteSizeOld = dcb.ByteSize;
dcb.ByteSize = (byte) value;
if (UnsafeNativeMethods.SetCommState(_handleProtector.Handle, ref dcb) == false)
{
dcb.ByteSize = byteSizeOld;
InternalResources.WinIOError();
}
}
}
}
internal bool DiscardNull
{
get { return (GetDcbFlag(NativeMethods.FNULL) == 1);}
set
{
int fNullFlag = GetDcbFlag(NativeMethods.FNULL);
if(value == true && fNullFlag == 0 || value == false && fNullFlag == 1)
{
int fNullOld = fNullFlag;
SetDcbFlag(NativeMethods.FNULL, value ? 1 : 0);
if (UnsafeNativeMethods.SetCommState(_handleProtector.Handle, ref dcb) == false)
{
SetDcbFlag(NativeMethods.FNULL, fNullOld);
InternalResources.WinIOError();
}
}
}
}
internal bool DtrEnable
{
get { return dtrEnable; }
set
{
if(value != dtrEnable)
{
bool dtrEnableOld = dtrEnable;
int fDtrControlOld = GetDcbFlag(NativeMethods.FDTRCONTROL);
dtrEnable = value;
SetDcbFlag(NativeMethods.FDTRCONTROL, dtrEnable ? 1 : 0);
if (UnsafeNativeMethods.SetCommState(_handleProtector.Handle, ref dcb) == false)
{
dtrEnable = dtrEnableOld;
SetDcbFlag(NativeMethods.FDTRCONTROL, fDtrControlOld);
InternalResources.WinIOError();
}
}
}
}
internal Handshake Handshake
{
get { return handshake; }
set
{
if (value < Handshake.None || value > Handshake.RequestToSendXOnXOff)
throw new ArgumentOutOfRangeException("handshake", InternalResources.GetResourceString("ArgumentOutOfRange_Enum"));
if(value != handshake)
{
// in the DCB, handshake affects the fRtsControl, fOutxCtsFlow, and fInX, fOutX fields,
// so we must save everything in that closure before making any changes.
Handshake handshakeOld = handshake;
int fInOutXOld = GetDcbFlag(NativeMethods.FINX);
int fOutxCtsFlowOld = GetDcbFlag(NativeMethods.FOUTXCTSFLOW);
int fRtsControlOld = GetDcbFlag(NativeMethods.FRTSCONTROL);
handshake = value;
int fInXOutXFlag = (handshake == Handshake.XOnXOff || handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0;
SetDcbFlag(NativeMethods.FINX, fInXOutXFlag);
SetDcbFlag(NativeMethods.FOUTX, fInXOutXFlag);
SetDcbFlag(NativeMethods.FOUTXCTSFLOW, (handshake == Handshake.RequestToSend ||
handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0);
// handshake and rtsEnable properties are necessary and sufficient to determining
// the fRtsControl field of the DCB.
if ((handshake == Handshake.RequestToSend ||
handshake == Handshake.RequestToSendXOnXOff))
{
SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_HANDSHAKE);
}
else if(rtsEnable)
{
SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_ENABLE);
}
else
{
SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_DISABLE);
}
if (UnsafeNativeMethods.SetCommState(_handleProtector.Handle, ref dcb) == false)
{
handshake = handshakeOld;
SetDcbFlag(NativeMethods.FINX, fInOutXOld);
SetDcbFlag(NativeMethods.FOUTX, fInOutXOld);
SetDcbFlag(NativeMethods.FOUTXCTSFLOW, fOutxCtsFlowOld);
SetDcbFlag(NativeMethods.FRTSCONTROL, fRtsControlOld);
InternalResources.WinIOError();
}
}
}
}
internal Parity Parity
{
get { return (Parity) dcb.Parity; }
set
{
if(value < Parity.None || value > Parity.Space)
throw new ArgumentOutOfRangeException("parity", InternalResources.GetResourceString("ArgumentOutOfRange_Enum"));;
if((byte) value != dcb.Parity)
{
byte parityOld = dcb.Parity;
// in the DCB structure, the parity setting also potentially effects:
// fParity, fErrorChar, ErrorChar
// so these must be saved as well.
int fParityOld = GetDcbFlag(NativeMethods.FPARITY);
byte ErrorCharOld = dcb.ErrorChar;
int fErrorCharOld = GetDcbFlag(NativeMethods.FPARITY);
dcb.Parity = (byte) value;
int parityFlag = (dcb.Parity == (byte) Parity.None) ? 1 : 0;
SetDcbFlag(NativeMethods.FPARITY, parityFlag);
if (parityFlag == 1)
{
SetDcbFlag(NativeMethods.FERRORCHAR, (parityReplace != '\0') ? 1 : 0);
dcb.ErrorChar = parityReplace;
}
else
{
SetDcbFlag(NativeMethods.FERRORCHAR, 0);
dcb.ErrorChar = (byte) '\0';
}
if (UnsafeNativeMethods.SetCommState(_handleProtector.Handle, ref dcb) == false)
{
dcb.Parity = parityOld;
SetDcbFlag(NativeMethods.FPARITY, fParityOld);
dcb.ErrorChar = ErrorCharOld;
SetDcbFlag(NativeMethods.FERRORCHAR, fErrorCharOld);
InternalResources.WinIOError();
}
}
}
}
// ParityReplace is the eight-bit character which replaces any bytes which
// ParityReplace affects the equivalent field in the DCB structure: ErrorChar, and
// the DCB flag fErrorChar.
internal byte ParityReplace
{
get { return parityReplace; }
set
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -