📄 serialstream.cs
字号:
}
public override void SetLength(long value)
{
throw new NotSupportedException(InternalResources.GetResourceString("NotSupported_UnseekableStream"));
}
public override void Write(byte[] array, int offset, int count)
{
Write(array, offset, count, WriteTimeout);
}
internal void Write(byte[] array, int offset, int count, int timeout)
{
if (inBreak)
throw new InvalidOperationException("Write in break");
if (array==null)
throw new ArgumentNullException("write buffer", InternalResources.GetResourceString("ArgumentNull_Array"));
if (offset < 0)
throw new ArgumentOutOfRangeException("write offset", InternalResources.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
if (count < 0)
throw new ArgumentOutOfRangeException("write count", InternalResources.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
if (count == 0) return; // no need to expend overhead in creating asyncResult, etc.
if (array.Length - offset < count)
throw new ArgumentException("write count",InternalResources.GetResourceString("ArgumentOutOfRange_OffsetOut"));
Debug.Assert(timeout == SerialPort.InfiniteTimeout || timeout >= 0, "Serial Stream Write - write timeout is " + timeout);
// check for open handle, though the port is always supposed to be open
if (_handleProtector.IsClosed) InternalResources.FileNotOpen();
IAsyncResult result = BeginWriteCore(array, offset, count, null, null, timeout);
EndWrite(result);
return;
}
// use default timeout as argument to WriteByte override with timeout arg
public override void WriteByte(byte value)
{
WriteByte(value, WriteTimeout);
}
internal void WriteByte(byte value, int timeout)
{
if (inBreak)
throw new InvalidOperationException("WriteByte in break");
if (_handleProtector.IsClosed) InternalResources.FileNotOpen();
tempBuf[0] = value;
IAsyncResult ar = BeginWriteCore(tempBuf, 0, 1, null, null, timeout);
EndWrite(ar);
return;
}
// --------SUBSECTION: internal-use methods ----------------------*
// ------ internal DCB-supporting methods ------- *
// Initializes unmananged DCB struct, to be called after opening communications resource.
// assumes we have already: baudRate, parity, dataBits, stopBits
// should only be called in SerialStream(...)
private void InitializeDCB(int baudRate, Parity parity, int dataBits, StopBits stopBits, bool discardNull)
{
// first get the current dcb structure setup
if (UnsafeNativeMethods.GetCommState(_handleProtector.Handle, ref dcb) == false)
{
InternalResources.WinIOError();
}
dcb.DCBlength = (uint) System.Runtime.InteropServices.Marshal.SizeOf(dcb);
// set parameterized properties
dcb.BaudRate = (uint) baudRate;
dcb.ByteSize = (byte) dataBits;
switch (stopBits)
{
case StopBits.One:
dcb.StopBits = NativeMethods.ONESTOPBIT;
break;
case StopBits.OnePointFive:
dcb.StopBits = NativeMethods.ONE5STOPBITS;
break;
case StopBits.Two:
dcb.StopBits = NativeMethods.TWOSTOPBITS;
break;
default:
Debug.Assert(true, "TBD");
break;
}
dcb.Parity = (byte) parity;
// SetDcbFlag, GetDcbFlag expose access to each of the relevant bits of the 32-bit integer
// storing all flags of the DCB. C# provides no direct means of manipulating bit fields, so
// this is the solution.
SetDcbFlag(NativeMethods.FPARITY, ((parity == Parity.None) ? 0 : 1));
SetDcbFlag(NativeMethods.FBINARY, 1); // always true for communications resources
// set DCB fields implied by default and the arguments given.
// Boolean fields in C# must become 1, 0 to properly set the bit flags in the unmanaged DCB struct
SetDcbFlag(NativeMethods.FOUTXCTSFLOW, ((handshake == Handshake.RequestToSend ||
handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0));
SetDcbFlag(NativeMethods.FOUTXDSRFLOW, (dsrTimeout != 0L) ? 1 : 0);
SetDcbFlag(NativeMethods.FDTRCONTROL, (dtrEnable) ? NativeMethods.DTR_CONTROL_ENABLE : NativeMethods.DTR_CONTROL_DISABLE);
SetDcbFlag(NativeMethods.FDSRSENSITIVITY, 0); // this should remain off
SetDcbFlag(NativeMethods.FINX, (handshake == Handshake.XOnXOff || handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0);
SetDcbFlag(NativeMethods.FOUTX,(handshake == Handshake.XOnXOff || handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0);
// if no parity, we have no error character (i.e. ErrorChar = '\0' or null character)
if (parity != Parity.None)
{
SetDcbFlag(NativeMethods.FERRORCHAR, (parityReplace != '\0') ? 1 : 0);
dcb.ErrorChar = parityReplace;
}
else
{
SetDcbFlag(NativeMethods.FERRORCHAR, 0);
dcb.ErrorChar = (byte) '\0';
}
// this method only runs once in the constructor, so we only have the default value to use.
// Later the user may change this via the NullDiscard property.
SetDcbFlag(NativeMethods.FNULL, discardNull ? 1 : 0);
// Setting RTS control, which is RTS_CONTROL_HANDSHAKE if RTS / RTS-XOnXOff handshaking
// used, RTS_ENABLE (RTS pin used during operation) if rtsEnable true but XOnXoff / No handshaking
// used, and disabled otherwise.
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);
}
dcb.XonChar = NativeMethods.DEFAULTXONCHAR; // may be exposed later but for now, constant
dcb.XoffChar = NativeMethods.DEFAULTXOFFCHAR;
// minimum number of bytes allowed in each buffer before flow control activated
// heuristically, this has been set at 1/4 of the buffer size
dcb.XonLim = dcb.XoffLim = (ushort) (commProp.dwCurrentRxQueue / 4);
dcb.EofChar = NativeMethods.EOFCHAR;
//OLD MSCOMM: dcb.EvtChar = (byte) 0;
// now changed to make use of RxFlag WaitCommEvent event => EofReceived WaitForCommEvent event
dcb.EvtChar = NativeMethods.EOFCHAR;
// set DCB structure
if (UnsafeNativeMethods.SetCommState(_handleProtector.Handle, ref dcb) == false)
{
InternalResources.WinIOError();
}
}
// Here we provide a method for getting the flags of the Device Control Block structure dcb
// associated with each instance of SerialStream, i.e. this method gets myStream.dcb.Flags
// Flags are any of the constants in NativeMethods such as FBINARY, FDTRCONTROL, etc.
internal int GetDcbFlag(int whichFlag)
{
uint mask;
Debug.Assert(whichFlag >= NativeMethods.FBINARY && whichFlag <= NativeMethods.FDUMMY2, "GetDcbFlag needs to fit into enum!");
if (whichFlag == NativeMethods.FDTRCONTROL || whichFlag == NativeMethods.FRTSCONTROL)
{
mask = 0x3;
}
else if (whichFlag == NativeMethods.FDUMMY2)
{
mask = 0x1FFFF;
}
else
{
mask = 0x1;
}
uint result = dcb.Flags & (mask << whichFlag);
return (int) (result >> whichFlag);
}
// Since C# applications have to provide a workaround for accessing and setting bitfields in unmanaged code,
// here we provide methods for getting and setting the Flags field of the Device Control Block structure dcb
// associated with each instance of SerialStream, i.e. this method sets myStream.dcb.Flags
// Flags are any of the constants in NativeMethods such as FBINARY, FDTRCONTROL, etc.
internal void SetDcbFlag(int whichFlag, int setting)
{
uint mask;
setting = setting << whichFlag;
Debug.Assert(whichFlag >= NativeMethods.FBINARY && whichFlag <= NativeMethods.FDUMMY2, "SetDcbFlag needs to fit into enum!");
if (whichFlag == NativeMethods.FDTRCONTROL || whichFlag == NativeMethods.FRTSCONTROL)
{
mask = 0x3;
}
else if (whichFlag == NativeMethods.FDUMMY2)
{
mask = 0x1FFFF;
}
else
{
mask = 0x1;
}
// clear the region
dcb.Flags &= ~(mask << whichFlag);
// set the region
dcb.Flags |= ((uint) setting);
}
// ----SUBSECTION: internal methods supporting public read/write methods-------*
unsafe private AsyncSerialStream_AsyncResult BeginReadCore(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject, int numBufferedBytesRead, int timeout)
{
// Create and store async stream class library specific data in the
// async result
AsyncSerialStream_AsyncResult asyncResult = new AsyncSerialStream_AsyncResult();
asyncResult._userCallback = userCallback;
asyncResult._userStateObject = stateObject;
asyncResult._isWrite = false;
asyncResult._oldTimeout = ReadTimeout; // store "old" timeout
ReadTimeout = timeout; // enforce timeouts in COMMTIMEOUTS structure
// Must set this here to ensure all the state on the IAsyncResult
// object is set before we call ReadFile, which gives the OS an
// opportunity to run our callback (including the user callback &
// the call to EndRead) before ReadFile has returned.
asyncResult._numBufferedBytes = numBufferedBytesRead;
// For Synchronous IO, I could go with either a callback and using
// the managed Monitor class, or I could create a handle and wait on it.
ManualResetEvent waitHandle = new ManualResetEvent(false);
asyncResult._waitHandle = waitHandle;
// Create a managed overlapped class
// We will set the file offsets later
Overlapped overlapped = new Overlapped(0, 0, 0, asyncResult);
// Pack the Overlapped class, and store it in the async result
NativeOverlapped* intOverlapped = overlapped.Pack(IOCallback);
asyncResult._overlapped = intOverlapped;
// Keep the array in one location in memory until the OS writes the
// relevant data into the array. Free GCHandle later.
asyncResult.PinBuffer(array);
// queue an async ReadFile operation and pass in a packed overlapped
//int r = ReadFile(_handle, array, numBytes, null, intOverlapped);
int hr = 0;
int r = ReadFileNative(_handleProtector, array, offset, numBytes,
intOverlapped, out hr);
// ReadFile, the OS version, will return 0 on failure. But
// my ReadFileNative wrapper returns -1. My wrapper will return
// the following:
// On error, r==-1.
// On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING
// on async requests that completed sequentially, r==0
// Note that you will NEVER RELIABLY be able to get the number of bytes
// read back from this call when using overlapped structures! You must
// not pass in a non-null lpNumBytesRead to ReadFile when using
// overlapped structures!
if (r==-1)
{
if (hr != NativeMethods.ERROR_IO_PENDING)
{
if (hr == NativeMethods.ERROR_HANDLE_EOF)
InternalResources.EndOfFile();
else
InternalResources.WinIOError(hr, String.Empty);
}
}
return asyncResult;
}
unsafe private AsyncSerialStream_AsyncResult BeginWriteCore(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject, int timeout)
{
// Create and store async stream class library specific data in the
// async result
AsyncSerialStream_AsyncResult asyncResult = new AsyncSerialStream_AsyncResult();
asyncResult._userCallback = userCallback;
asyncResult._userStateObject = stateObject;
asyncResult._isWrite = true;
asyncResult._oldTimeout = WriteTimeout; // set "old" timeout
WriteTimeout = timeout;
// For Synchronous IO, I could go with either a callback and using
// the managed Monitor class, or I could create a handle and wait on it.
ManualResetEvent waitHandle = new ManualResetEvent(false);
asyncResult._waitHandle = waitHandle;
// Create a managed overlapped class
// We will set the file offsets later
Overlapped overlapped = new Overlapped(0, 0, 0, asyncResult);
// Pack the Overlapped class, and store it in the async result
NativeOverlapped* intOverlapped = overlapped.Pack(IOCallback);
asyncResult._overlapped = intOverlapped;
// Keep the array in one location in memory until the OS reads the
// relevant data from the array. Free GCHandle later.
asyncResult.PinBuffer(array);
int hr = 0;
// queue an async WriteFile operation and pass in a packed overlapped
int r = WriteFileNative(_handleProtector, array, offset, numBytes, intOverlapped, out hr);
// WriteFile, the OS version, will return 0 on failure. But
// my WriteFileNative wrapper returns -1. My wrapper will return
// the following:
// On error, r==-1.
// On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING
// On async requests that completed sequentially, r==0
// Note that you will NEVER RELIABLY be able to get the number of bytes
// written back from this call when using overlapped IO! You must
// not pass in a non-null lpNumBytesWritten to WriteFile when using
// overlapped structures!
if (r==-1)
{
if (hr != NativeMethods.ERROR_IO_PENDING)
{
if (hr == NativeMethods.ERROR_HANDLE_EOF)
InternalResources.EndOfFile();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -