📄 serialstream.cs
字号:
InternalResources.WinIOError();
}
if (!ThreadPool.BindHandle(_handleProtector.Handle))
{
throw new IOException(InternalResources.GetResourceString("IO.IO_BindHandleFailed"));
}
// prep. for starting event cycle.
myWaitCommCallback = new WaitEventCallback(WaitForCommEvent);
myAsyncCallback = new AsyncCallback(EndWaitForCommEvent);
state = null; // no need for new object, since we never use it.
IAsyncResult ar = myWaitCommCallback.BeginInvoke(myAsyncCallback, state);
}
~SerialStream()
{
if (_handleProtector != null)
{
Dispose(false);
}
}
protected virtual void Dispose(bool disposing)
{
// Nothing will be done differently based on whether we are
// disposing vs. finalizing.
// Signal the other side that we're closing. Should do regardless of whether we've called
// Close() or not Dispose()
if (_handleProtector != null)
{
if (!_handleProtector.IsClosed)
{
if(!UnsafeNativeMethods.EscapeCommFunction(_handleProtector.Handle, NativeMethods.CLRDTR))
{
// should not happen
InternalResources.WinIOError();
}
Flush();
_handleProtector.Close();
}
}
}
// -----SECTION: all public methods ------------------*
// User-accessible async read method. Returns AsyncSerialStream_AsyncResult : IAsyncResult
public override IAsyncResult BeginRead(byte[] array, int offset,int numBytes, AsyncCallback userCallback, object stateObject)
{
return BeginRead(array, offset, numBytes, userCallback, stateObject, ReadTimeout);
}
internal IAsyncResult BeginRead(byte[] array, int offset,int numBytes,
AsyncCallback userCallback, object stateObject, int timeout)
{
if (array==null)
throw new ArgumentNullException("array");
if (offset < 0)
throw new ArgumentOutOfRangeException("offset", InternalResources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (numBytes < 0)
throw new ArgumentOutOfRangeException("numBytes", InternalResources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (array.Length - offset < numBytes)
throw new ArgumentException(InternalResources.GetResourceString("Argument_InvalidOffLen"));
if (_handleProtector.IsClosed) InternalResources.FileNotOpen();
return BeginReadCore(array, offset, numBytes, userCallback, stateObject, 0, timeout);
}
// User-accessible async write method. Returns AsyncSerialStream_AsyncResult : IAsyncResult
// Throws an exception if port is in break state.
public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes,
AsyncCallback userCallback, object stateObject)
{
return BeginWrite(array, offset, numBytes, userCallback, stateObject, WriteTimeout);
}
internal IAsyncResult BeginWrite(byte[] array, int offset, int numBytes,
AsyncCallback userCallback, object stateObject, int timeout)
{
if (inBreak)
throw new InvalidOperationException("BeginWrite in break");
if (array==null)
throw new ArgumentNullException("array");
if (offset < 0)
throw new ArgumentOutOfRangeException("offset", InternalResources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (numBytes < 0)
throw new ArgumentOutOfRangeException("numBytes", InternalResources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (array.Length - offset < numBytes)
throw new ArgumentException(InternalResources.GetResourceString("Argument_InvalidOffLen"));
if (_handleProtector.IsClosed) InternalResources.FileNotOpen();
return BeginWriteCore(array, offset, numBytes, userCallback, stateObject, timeout);
}
// Equivalent to MSComm's Break = false
internal void ClearBreak()
{
if (UnsafeNativeMethods.ClearCommBreak(_handleProtector.Handle) == false)
InternalResources.WinIOError();
inBreak = false;
}
// handle protector itself is closed in the Dispose() method, since we can Dispose() without calling
// Close() in some cases.
public override void Close()
{
if (_handleProtector.IsClosed) InternalResources.FileNotOpen();
Dispose(true);
GC.SuppressFinalize(this);
}
// Uses Win32 method to dump out the receive buffer; analagous to MSComm's "InBufferCount = 0"
internal void DiscardInBuffer()
{
if (UnsafeNativeMethods.PurgeComm(_handleProtector.Handle, NativeMethods.PURGE_RXCLEAR) == false)
InternalResources.WinIOError();
}
// Uses Win32 method to dump out the xmit buffer; analagous to MSComm's "OutBufferCount = 0"
internal void DiscardOutBuffer()
{
if (UnsafeNativeMethods.PurgeComm(_handleProtector.Handle, NativeMethods.PURGE_TXCLEAR) == false)
InternalResources.WinIOError();
}
// Async companion to BeginRead.
// Note, assumed IAsyncResult argument is of derived type AsyncSerialStream_AsyncResult,
// and throws an exception if untrue.
public unsafe override int EndRead(IAsyncResult asyncResult)
{
if (_handleProtector.IsClosed) InternalResources.FileNotOpen();
if (asyncResult==null)
throw new ArgumentNullException("asyncResult");
AsyncSerialStream_AsyncResult afsar = asyncResult as AsyncSerialStream_AsyncResult;
if (afsar==null || afsar._isWrite)
InternalResources.WrongAsyncResult();
// This sidesteps race conditions, avoids memory corruption after freeing the
// NativeOverlapped class or GCHandle twice.
if (1 == Interlocked.CompareExchange(ref afsar._EndXxxCalled, 1, 0))
InternalResources.EndReadCalledTwice();
WaitHandle wh = afsar.AsyncWaitHandle;
if (wh != null)
{
if (!afsar.IsCompleted)
{
do {
// Since ReadFile() drops after a timeout (set above with SetCommTimeouts())
// without a guarantee of setting the error property to indicate this, calculate
// actual time elapsed here.
// The granularity of the system is such that border cases may go either way,
// but Windows makes no guarantees anyway.
int beginTicks = SafeNativeMethods.GetTickCount();
wh.WaitOne();
// There's a subtle race condition here. In AsyncFSCallback,
// I must signal the WaitHandle then set _isComplete to be true,
// to avoid closing the WaitHandle before AsyncFSCallback has
// signalled it. But with that behavior and the optimization
// to call WaitOne only when IsCompleted is false, it's possible
// to return from this method before IsCompleted is set to true.
// This is currently completely harmless, so the most efficient
// solution of just setting the field seems like the right thing
// to do.
int currentTimeout = ReadTimeout;
int endTicks = SafeNativeMethods.GetTickCount();
if (endTicks - beginTicks >= currentTimeout && currentTimeout != SerialPort.InfiniteTimeout)
lastOpTimedOut = true;
else
lastOpTimedOut = false;
} while (afsar._numBytes == 0 && ReadTimeout == SerialPort.InfiniteTimeout);
afsar._isComplete = true;
}
wh.Close();
}
// Free memory, GC handles.
NativeOverlapped* overlappedPtr = afsar._overlapped;
if (overlappedPtr != null)
Overlapped.Free(overlappedPtr);
afsar.UnpinBuffer();
// Check for non-timeout errors during the read.
if (afsar._errorCode != 0)
InternalResources.WinIOError(afsar._errorCode, portName);
// return to old timeout. Note that this class is not thread-safe, and the timeout of read operation A
// is actually the minimum timeout of any read operation plus the time from A's invocation that B set a minimum timeout.
// NOT THREAD SAFE.
ReadTimeout = afsar._oldTimeout;
return afsar._numBytes + afsar._numBufferedBytes;
}
// Async companion to BeginWrite.
// Note, assumed IAsyncResult argument is of derived type AsyncSerialStream_AsyncResult,
// and throws an exception if untrue.
// Also fails if called in port's break state.
public unsafe override void EndWrite(IAsyncResult asyncResult)
{
if (_handleProtector.IsClosed) InternalResources.FileNotOpen();
if (inBreak)
throw new InvalidOperationException("EndWrite in break");
if (asyncResult==null)
throw new ArgumentNullException("asyncResult");
AsyncSerialStream_AsyncResult afsar = asyncResult as AsyncSerialStream_AsyncResult;
if (afsar==null || !afsar._isWrite)
InternalResources.WrongAsyncResult();
// This sidesteps race conditions, avoids memory corruption after freeing the
// NativeOverlapped class or GCHandle twice.
if (1 == Interlocked.CompareExchange(ref afsar._EndXxxCalled, 1, 0))
InternalResources.EndWriteCalledTwice();
WaitHandle wh = afsar.AsyncWaitHandle;
if (wh != null)
{
if (!afsar.IsCompleted)
{
int beginTicks = SafeNativeMethods.GetTickCount();
// Since WriteFile() drops after a timeout (set above with SetCommTimeouts())
// without a guarantee of setting the error property to indicate this, calculate
// actual time elapsed here.
// The granularity of the system is such that border cases may go either way,
// but Windows makes no guarantees anyway. Plus, write timeouts should be extremely rare.
wh.WaitOne();
// There's a subtle race condition here. In AsyncFSCallback,
// I must signal the WaitHandle then set _isComplete to be true,
// to avoid closing the WaitHandle before AsyncFSCallback has
// signalled it. But with that behavior and the optimization
// to call WaitOne only when IsCompleted is false, it's possible
// to return from this method before IsCompleted is set to true.
// This is currently completely harmless, so the most efficient
// solution of just setting the field seems like the right thing
// to do.
int currentTimeout = WriteTimeout;
int endTicks = SafeNativeMethods.GetTickCount();
if (endTicks - beginTicks >= currentTimeout && currentTimeout != SerialPort.InfiniteTimeout)
throw new TimeoutException("Write Timed Out: " + (endTicks - beginTicks) + " > " + currentTimeout);
afsar._isComplete = true;
}
wh.Close();
}
// Free memory, GC handles.
NativeOverlapped* overlappedPtr = afsar._overlapped;
if (overlappedPtr != null)
Overlapped.Free(overlappedPtr);
afsar.UnpinBuffer();
// Now check for any error during the write.
if (afsar._errorCode != 0)
InternalResources.WinIOError(afsar._errorCode, portName);
// return to old timeout. See read timeout commentary on race conditions.
WriteTimeout = afsar._oldTimeout;
// Number of bytes written is afsar._numBytes + afsar._numBufferedBytes.
return;
}
// Flush dumps the contents of the serial driver's internal read and write buffers.
// We actually expose the functionality for each, but fulfilling Stream's contract
// requires a Flush() method. Fails if handle closed.
// Note: Serial driver's write buffer is *already* attempting to write it, so we can only wait until it finishes.
public override void Flush()
{
if (_handleProtector.Handle == NativeMethods.NULL) throw new InvalidOperationException("Flush - Stream not open!");
DiscardInBuffer();
DiscardOutBuffer();
return;
}
// Blocking read operation, returning the number of bytes read from the stream.
public override int Read([In, Out] byte[] array, int offset, int count)
{
return Read(array, offset, count, ReadTimeout);
}
internal int Read([In, Out] byte[] array, int offset, int count, int timeout)
{
if (array==null)
throw new ArgumentNullException("array", InternalResources.GetResourceString("ArgumentNull_Buffer"));
if (offset < 0)
throw new ArgumentOutOfRangeException("offset", InternalResources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (count < 0)
throw new ArgumentOutOfRangeException("count", InternalResources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (array.Length - offset < count)
throw new ArgumentException(InternalResources.GetResourceString("Argument_InvalidOffLen"));
if (count == 0) return 0; // return immediately if no bytes requested; no need for overhead.
Debug.Assert(timeout == SerialPort.InfiniteTimeout || timeout >= 0, "Serial Stream Read - called with timeout " + timeout);
// Check to see we have no handle-related error, since the port's always supposed to be open.
if (_handleProtector.IsClosed) InternalResources.FileNotOpen();
IAsyncResult result = BeginReadCore(array, offset, count, null, null, 0, timeout);
return EndRead(result);
}
public override int ReadByte()
{
return ReadByte(ReadTimeout);
}
internal int ReadByte(int timeout)
{
if (_handleProtector.IsClosed) InternalResources.FileNotOpen();
IAsyncResult result = BeginReadCore(tempBuf, 0, 1, null, null, 0, timeout);
int res = EndRead(result);
if (lastOpTimedOut)
return -1;
else
return tempBuf[0];
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException(InternalResources.GetResourceString("NotSupported_UnseekableStream"));
}
// Equivalent to MSComm's Break = true
internal void SetBreak()
{
if (UnsafeNativeMethods.SetCommBreak(_handleProtector.Handle) == false)
InternalResources.WinIOError();
inBreak = true;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -