📄 serialstream.cs
字号:
else
InternalResources.WinIOError(hr, String.Empty);
}
}
return asyncResult;
}
// Internal method, wrapping the PInvoke to ReadFile().
internal unsafe int ReadFileNative(HandleProtector hp, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr)
{
// Don't corrupt memory when multiple threads are erroneously writing
// to this stream simultaneously.
if (bytes.Length - offset < count)
throw new IndexOutOfRangeException(InternalResources.GetResourceString("IndexOutOfRange_IORaceCondition"));
// You can't use the fixed statement on an array of length 0.
if (bytes.Length==0)
{
hr = 0;
return 0;
}
int r = 0;
int numBytesRead = 0;
bool incremented = false;
try
{
if (hp.TryAddRef(ref incremented))
{
fixed(byte* p = bytes)
{
r = UnsafeNativeMethods.ReadFile(hp.Handle, p + offset, count, NativeMethods.NULL, overlapped);
}
}
else
hr = NativeMethods.ERROR_INVALID_HANDLE; // Handle was closed.
}
finally
{
if (incremented) hp.Release();
}
if (r==0)
{
hr = Marshal.GetLastWin32Error();
// Note: we should never silently swallow an error here without some
// extra work. We must make sure that BeginReadCore won't return an
// IAsyncResult that will cause EndRead to block, since the OS won't
// call AsyncFSCallback for us.
// For invalid handles, detect the error and mark our handle
// as closed to give slightly better error messages. Also
// help ensure we avoid handle recycling bugs.
if (hr == NativeMethods.ERROR_INVALID_HANDLE)
_handleProtector.ForciblyMarkAsClosed();
return -1;
}
else
hr = 0;
return numBytesRead;
}
internal unsafe int WriteFileNative(HandleProtector hp, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr)
{
// Don't corrupt memory when multiple threads are erroneously writing
// to this stream simultaneously. (Note that the OS is reading from
// the array we pass to WriteFile, but if we read beyond the end and
// that memory isn't allocated, we could get an AV.)
if (bytes.Length - offset < count)
throw new IndexOutOfRangeException(InternalResources.GetResourceString("IndexOutOfRange_IORaceCondition"));
// You can't use the fixed statement on an array of length 0.
if (bytes.Length==0)
{
hr = 0;
return 0;
}
int numBytesWritten = 0;
int r = 0;
bool incremented = false;
try
{
if (hp.TryAddRef(ref incremented))
{
fixed(byte* p = bytes)
{
r = UnsafeNativeMethods.WriteFile(hp.Handle, p + offset, count, NativeMethods.NULL, overlapped);
}
}
else
hr = NativeMethods.ERROR_INVALID_HANDLE; // Handle was closed.
}
finally
{
if (incremented) hp.Release();
}
if (r==0)
{
hr = Marshal.GetLastWin32Error();
// Note: we should never silently swallow an error here without some
// extra work. We must make sure that BeginWriteCore won't return an
// IAsyncResult that will cause EndWrite to block, since the OS won't
// call AsyncFSCallback for us.
// For invalid handles, detect the error and mark our handle
// as closed to give slightly better error messages. Also
// help ensure we avoid handle recycling bugs.
if (hr == NativeMethods.ERROR_INVALID_HANDLE)
_handleProtector.ForciblyMarkAsClosed();
return -1;
}
else
hr = 0;
return numBytesWritten;
}
// ----SUBSECTION: internal methods supporting events/async operation------*
// This is the blocking method that waits for an event to occur. It wraps the SDK's WaitCommEvent function.
private unsafe int WaitForCommEvent()
{
int eventsOccurred = 0;
// monitor all events except TXEMPTY
UnsafeNativeMethods.SetCommMask(_handleProtector.Handle, NativeMethods.ALL_EVENTS);
AsyncSerialStream_AsyncResult asyncResult = new AsyncSerialStream_AsyncResult();
asyncResult._userCallback = null;
asyncResult._userStateObject = null;
asyncResult._isWrite = false;
asyncResult._oldTimeout = -1;
asyncResult._numBufferedBytes = 0;
ManualResetEvent waitHandle = new ManualResetEvent(false);
asyncResult._waitHandle = waitHandle;
Overlapped overlapped = new Overlapped(0, 0, 0, asyncResult);
// Pack the Overlapped class, and store it in the async result
NativeOverlapped* intOverlapped = overlapped.Pack(IOCallback);
if (UnsafeNativeMethods.WaitCommEvent(_handleProtector.Handle, ref eventsOccurred, intOverlapped) == false)
{
int hr = Marshal.GetLastWin32Error();
if (hr == NativeMethods.ERROR_IO_PENDING)
{
int temp = UnsafeNativeMethods.WaitForSingleObject(waitHandle.Handle, -1);
if(temp == 0) // no error
return eventsOccurred;
else
InternalResources.WinIOError();
}
else
InternalResources.WinIOError();
}
return eventsOccurred;
}
// This is the cleanup method called when WaitCommEvent() has returned.
[OneWayAttribute()]
private void EndWaitForCommEvent(IAsyncResult ar)
{
int errorEvents = (int) (SerialEvents.Frame | SerialEvents.Overrun
| SerialEvents.RxOver | SerialEvents.RxParity | SerialEvents.TxFull);
int receivedEvents = (int) (SerialEvents.ReceivedChars | SerialEvents.EofReceived);
int pinChangedEvents = (int) (SerialEvents.Break | SerialEvents.CDChanged | SerialEvents.CtsChanged |
SerialEvents.Ring | SerialEvents.DsrChanged);
// Extract the delegate from the AsynchResult.
WaitEventCallback myWaitCommCallback =
(WaitEventCallback) ((AsyncResult)ar).AsyncDelegate;
// Obtain the result.
// The EV_CTS, EV_RLSD, EV_DSR, EV_BREAK, EV_RING, EV_RXCHAR match up with SerialEvents constants
int nativeEvents = myWaitCommCallback.EndInvoke(ar);
int errors = 0;
int events = 0;
if ((nativeEvents & NativeMethods.EV_ERR) != 0)
{
if (UnsafeNativeMethods.ClearCommError(_handleProtector.Handle, ref errors, ref comStat) == false)
{
InternalResources.WinIOError();
}
if ((errors & NativeMethods.CE_RXOVER) != 0) events |= (int) SerialEvents.RxOver;
if ((errors & NativeMethods.CE_OVERRUN) != 0) events |= (int) SerialEvents.Overrun;
if ((errors & NativeMethods.CE_PARITY) != 0) events |= (int) SerialEvents.RxParity;
if ((errors & NativeMethods.CE_FRAME) != 0) events|= (int) SerialEvents.Frame;
if ((errors & NativeMethods.CE_TXFULL) != 0) events |= (int) SerialEvents.TxFull;
if ((errors & NativeMethods.CE_BREAK) != 0) events |= (int) SerialEvents.Break;
}
if ((nativeEvents & NativeMethods.EV_RXCHAR) != 0) events |= (int) SerialEvents.ReceivedChars;
if ((nativeEvents & NativeMethods.EV_RXFLAG) != 0) events |= (int) SerialEvents.EofReceived;
if ((nativeEvents & NativeMethods.EV_CTS) != 0) events |= (int) SerialEvents.CtsChanged;
if ((nativeEvents & NativeMethods.EV_DSR) != 0) events |= (int) SerialEvents.DsrChanged;
if ((nativeEvents & NativeMethods.EV_RLSD) != 0) events |= (int) SerialEvents.CDChanged;
if ((nativeEvents & NativeMethods.EV_RING) != 0) events |= (int) SerialEvents.Ring;
if ((nativeEvents & NativeMethods.EV_BREAK) != 0) events |= (int) SerialEvents.Break;
// errors are corresponding SerialEvents shifted right 8 bits
if ((events & errorEvents) != 0)
ErrorEvent(this, new SerialEventArgs((SerialEvents) (events & errorEvents)));
if ((events & pinChangedEvents) != 0)
PinChangedEvent(this, new SerialEventArgs((SerialEvents) (events & pinChangedEvents)));
if ((events & receivedEvents) != 0)
ReceivedEvent(this, new SerialEventArgs((SerialEvents) (events & receivedEvents)));
myWaitCommCallback.BeginInvoke(myAsyncCallback, state); // start it over again.
}
// This is a the callback prompted when a thread completes any async I/O operation.
unsafe private static void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped)
{
// Unpack overlapped
Overlapped overlapped = Overlapped.Unpack(pOverlapped);
// Extract async the result from overlapped structure
AsyncSerialStream_AsyncResult asyncResult =
(AsyncSerialStream_AsyncResult)overlapped.AsyncResult;
asyncResult._numBytes = (int)numBytes;
asyncResult._errorCode = (int)errorCode;
// Pull waitHandle (here a ManualResetEvent) out of the asyncResult
WaitHandle wh = asyncResult._waitHandle;
if (wh != null)
{
bool r = NativeMethods.SetEvent(wh.Handle);
if (!r) InternalResources.WinIOError();
}
// Set IsCompleted after WaitHandle signaled in case SetEvent fails.
asyncResult._isComplete = true;
// Call the user-provided callback. Note that it can and often should
// call EndRead or EndWrite. There's no reason to use an async
// delegate here - we're already on a threadpool thread.
// Note the IAsyncResult's completedSynchronously property must return
// false here, saying the user callback was called on another thread.
asyncResult._completedSynchronously = false;
AsyncCallback userCallback = asyncResult._userCallback;
if (userCallback!=null)
userCallback(asyncResult);
}
// ----SECTION: internal classes --------*
// This is an internal object implementing IAsyncResult with fields
// for all of the relevant data necessary to complete the IO operation.
// This is used by AsyncFSCallback and all async methods.
unsafe internal class AsyncSerialStream_AsyncResult : IAsyncResult
{
// User code callback
internal AsyncCallback _userCallback;
internal Object _userStateObject;
internal WaitHandle _waitHandle;
internal GCHandle _bufferHandle; // GCHandle to pin byte[].
internal bool _isWrite; // Whether this is a read or a write
internal bool _isComplete;
internal bool _completedSynchronously; // Which thread called callback
internal bool _bufferIsPinned; // Whether our _bufferHandle is valid.
internal int _EndXxxCalled; // Whether we've called EndXxx already.
internal int _numBytes; // number of bytes read OR written
internal int _numBufferedBytes;
internal int _errorCode;
internal NativeOverlapped* _overlapped;
internal int _oldTimeout; // added to preserve default timeouts on calls to specific timeouts
/// <include file='doc\ModFileStream.uex' path='docs/doc[@for="ModFileStream.AsyncSerialStream_AsyncResult.AsyncState"]/*' />
public virtual Object AsyncState
{
get { return _userStateObject; }
}
/// <include file='doc\ModFileStream.uex' path='docs/doc[@for="ModFileStream.AsyncSerialStream_AsyncResult.IsCompleted"]/*' />
public bool IsCompleted
{
get { return _isComplete; }
set { _isComplete = value; }
}
/// <include file='doc\ModFileStream.uex' path='docs/doc[@for="ModFileStream.AsyncSerialStream_AsyncResult.AsyncWaitHandle"]/*' />
public WaitHandle AsyncWaitHandle
{
get { return _waitHandle; }
}
// Returns true iff the user callback was called by the thread that
// called BeginRead or BeginWrite. If we use an async delegate or
// threadpool thread internally, this will be false. This is used
// by code to determine whether a successive call to BeginRead needs
// to be done on their main thread or in their callback to avoid a
// stack overflow on many reads or writes.
public bool CompletedSynchronously
{
get { return _completedSynchronously; }
}
internal static AsyncSerialStream_AsyncResult CreateBufferedReadResult(int numBufferedBytes, AsyncCallback userCallback, Object userStateObject)
{
AsyncSerialStream_AsyncResult asyncResult = new AsyncSerialStream_AsyncResult();
asyncResult._userCallback = userCallback;
asyncResult._userStateObject = userStateObject;
asyncResult._isComplete = true;
asyncResult._isWrite = false;
asyncResult._numBufferedBytes = numBufferedBytes;
return asyncResult;
}
// An internal convenience method
internal void CallUserCallback()
{
if (_userCallback != null)
{
// Call user's callback on a threadpool thread.
// we must set this to false, since this happens on another thread.
_completedSynchronously = false;
_userCallback.BeginInvoke(this, null, null);
// note that we needn't call EndInvoke, since the IAsyncResult finalizer does cleanup.
}
}
// Methods to ensure that if we somehow return from Win32 ReadFile() or WriteFile()
// and fill the buffer
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -