unsafe private SerialStreamAsyncResult BeginWriteCore(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) { // Create and store async stream class library specific data in the // async result SerialStreamAsyncResult asyncResult = new SerialStreamAsyncResult(); asyncResult._userCallback = userCallback; asyncResult._userStateObject = stateObject; asyncResult._isWrite = true; // 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, IntPtr.Zero, asyncResult); // Pack the Overlapped class, and store it in the async result NativeOverlapped* intOverlapped = overlapped.Pack(IOCallback, array); asyncResult._overlapped = intOverlapped; int hr = 0; // queue an async WriteFile operation and pass in a packed overlapped int r = WriteFileNative(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(); else InternalResources.WinIOError(hr, String.Empty); } } return asyncResult; }
internal unsafe void WaitForCommEvent() { int unused = 0; bool doCleanup = false; NativeOverlapped* intOverlapped = null; while (!ShutdownLoop) { SerialStreamAsyncResult asyncResult = null; if (isAsync) { asyncResult = new SerialStreamAsyncResult(); asyncResult._userCallback = null; asyncResult._userStateObject = null; asyncResult._isWrite = false; // we're going to use _numBytes for something different in this loop. In this case, both // freeNativeOverlappedCallback and this thread will decrement that value. Whichever one decrements it // to zero will be the one to free the native overlapped. This guarantees the overlapped gets freed // after both the callback and GetOverlappedResult have had a chance to use it. asyncResult._numBytes = 2; asyncResult._waitHandle = waitCommEventWaitHandle; waitCommEventWaitHandle.Reset(); Overlapped overlapped = new Overlapped(0, 0, waitCommEventWaitHandle.SafeWaitHandle.DangerousGetHandle(), asyncResult); // Pack the Overlapped class, and store it in the async result intOverlapped = overlapped.Pack(freeNativeOverlappedCallback, null); } fixed (int* eventsOccurredPtr = &eventsOccurred) { if (UnsafeNativeMethods.WaitCommEvent(handle, eventsOccurredPtr, intOverlapped) == false) { int hr = Marshal.GetLastWin32Error(); // When a device is disconnected unexpectedly from a serial port, there appear to be // at least two error codes Windows or drivers may return. if (hr == NativeMethods.ERROR_ACCESS_DENIED || hr == NativeMethods.ERROR_BAD_COMMAND) { doCleanup = true; break; } if (hr == NativeMethods.ERROR_IO_PENDING) { Debug.Assert(isAsync, "The port is not open for async, so we should not get ERROR_IO_PENDING from WaitCommEvent"); int error; // if we get IO pending, MSDN says we should wait on the WaitHandle, then call GetOverlappedResult // to get the results of WaitCommEvent. bool success = waitCommEventWaitHandle.WaitOne(); Debug.Assert(success, "waitCommEventWaitHandle.WaitOne() returned error " + Marshal.GetLastWin32Error()); do { // NOTE: GetOverlappedResult will modify the original pointer passed into WaitCommEvent. success = UnsafeNativeMethods.GetOverlappedResult(handle, intOverlapped, ref unused, false); error = Marshal.GetLastWin32Error(); } while (error == NativeMethods.ERROR_IO_INCOMPLETE && !ShutdownLoop && !success); if (!success) { // Ignore ERROR_IO_INCOMPLETE and ERROR_INVALID_PARAMETER, because there's a chance we'll get // one of those while shutting down if (! ( (error == NativeMethods.ERROR_IO_INCOMPLETE || error == NativeMethods.ERROR_INVALID_PARAMETER) && ShutdownLoop)) Debug.Assert(false, "GetOverlappedResult returned error, we might leak intOverlapped memory" + error.ToString(CultureInfo.InvariantCulture)); } } else if (hr != NativeMethods.ERROR_INVALID_PARAMETER) { // ignore ERROR_INVALID_PARAMETER errors. WaitCommError seems to return this // when SetCommMask is changed while it's blocking (like we do in Dispose()) Debug.Assert(false, "WaitCommEvent returned error " + hr); } } } if (!ShutdownLoop) CallEvents(eventsOccurred); if (isAsync) { if (Interlocked.Decrement(ref asyncResult._numBytes) == 0) Overlapped.Free(intOverlapped); } } // while (!ShutdownLoop) if (doCleanup) { // the rest will be handled in Dispose() endEventLoop = true; Overlapped.Free(intOverlapped); } eventLoopEndedSignal.Set(); }
private unsafe SerialStreamAsyncResult BeginWriteCore(byte[] array, int offset, int numBytes, AsyncCallback userCallback, object stateObject) { SerialStreamAsyncResult ar = new SerialStreamAsyncResult { _userCallback = userCallback, _userStateObject = stateObject, _isWrite = true }; ManualResetEvent event2 = new ManualResetEvent(false); ar._waitHandle = event2; NativeOverlapped* overlapped = new Overlapped(0, 0, IntPtr.Zero, ar).Pack(IOCallback, array); ar._overlapped = overlapped; int hr = 0; if ((this.WriteFileNative(array, offset, numBytes, overlapped, out hr) == -1) && (hr != 0x3e5)) { if (hr == 0x26) { InternalResources.EndOfFile(); return ar; } InternalResources.WinIOError(hr, string.Empty); } return ar; }