Beispiel #1
0
        /// <summary>
        /// Do work based on the mask event that has occurred.
        /// </summary>
        /// <param name="mask">The mask that was provided.</param>
        private void ProcessWaitCommEvent(NativeMethods.SerialEventMask mask)
        {
            if (mask != (int)0)
            {
                SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "{0}: SerialThread: ProcessWaitCommEvent: {1}", m_Name, mask);
            }

            // Reading a character
            if ((mask & NativeMethods.SerialEventMask.EV_RXCHAR) != 0)
            {
                m_ReadByteAvailable = true;
            }
            if ((mask & NativeMethods.SerialEventMask.EV_RXFLAG) != 0)
            {
                m_ReadByteAvailable = true;
                m_ReadByteEof       = true;
            }

            // We don't raise an event for characters immediately, but only after the read operation
            // is complete.
            OnCommEvent(new CommEventArgs(mask & ~(NativeMethods.SerialEventMask.EV_RXCHAR | NativeMethods.SerialEventMask.EV_RXFLAG)));

            if ((mask & NativeMethods.SerialEventMask.EV_TXEMPTY) != 0)
            {
                lock (m_Buffer.WriteLock) {
                    if (m_Buffer.Serial.TxEmptyEvent())
                    {
                        SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "{0}: SerialThread: ProcessWaitCommEvent: TX-BUFFER empty", m_Name);
                    }
                }
            }

            if ((mask & (NativeMethods.SerialEventMask.EV_RXCHAR | NativeMethods.SerialEventMask.EV_ERR)) != 0)
            {
                NativeMethods.ComStatErrors comErr;
                bool result = UnsafeNativeMethods.ClearCommError(m_ComPortHandle, out comErr, IntPtr.Zero);
                if (!result)
                {
                    int w32err = Marshal.GetLastWin32Error();
                    int hr     = Marshal.GetHRForLastWin32Error();
                    SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0,
                                                    "{0}: SerialThread: ClearCommError: WINERROR {1}", m_Name, w32err);
                    Marshal.ThrowExceptionForHR(hr);
                }
                else
                {
                    comErr = (NativeMethods.ComStatErrors)((int)comErr & 0x10F);
                    if (comErr != 0)
                    {
                        OnCommErrorEvent(new CommErrorEventArgs(comErr));
                    }
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Check if we should execute WaitCommEvent() and get the result if immediately available.
        /// </summary>
        /// <remarks>
        /// This function abstracts the Win32 API WaitCommEvent(). It assumes overlapped I/O.
        /// Therefore, when calling this function, you should ensure that the parameter <c>mask</c>
        /// and <c>overlap</c> are pinned for the duration of the overlapped I/O. Any easy way to
        /// do this is to allocate the variables on the stack and then pass them by reference to
        /// this function.
        /// <para>You should not call this function if a pending I/O operation for WaitCommEvent()
        /// is still open. It is an error otherwise.</para>
        /// </remarks>
        /// <param name="mask">The mask value if information is available immediately.</param>
        /// <param name="overlap">The overlap structure to use.</param>
        /// <returns>If the operation is pending or not.</returns>
        private bool DoWaitCommEvent(out NativeMethods.SerialEventMask mask, ref NativeOverlapped overlap)
        {
            bool result = UnsafeNativeMethods.WaitCommEvent(m_ComPortHandle, out mask, ref overlap);

            if (!result)
            {
                int w32err = Marshal.GetLastWin32Error();
                int hr     = Marshal.GetHRForLastWin32Error();
                if (w32err != WinError.ERROR_IO_PENDING)
                {
                    SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Error, 0,
                                                    "{0}: SerialThread: DoWaitCommEvent: Result: {1}", m_Name, w32err);
                    throw new System.IO.IOException("WaitCommEvent overlapped exception", hr);
                }
            }
            else
            {
                ProcessWaitCommEvent(mask);
            }
            return(!result);
        }
 public static extern bool WaitCommEvent(SafeFileHandle hFile,
                                         [MarshalAs(UnmanagedType.U4)] out NativeMethods.SerialEventMask lpEvtMask,
                                         ref System.Threading.NativeOverlapped lpOverlapped);
 public static extern bool SetCommMask(SafeFileHandle hFile,
                                       [MarshalAs(UnmanagedType.U4)] NativeMethods.SerialEventMask dwEvtMask);
Beispiel #5
0
        private void OverlappedIoThreadMainLoop()
        {
            // WaitCommEvent
            bool serialCommPending = false;
            bool serialCommError   = false;

            m_SerialCommEvent.Reset();
            NativeOverlapped serialCommOverlapped = new NativeOverlapped();

#if NETSTANDARD15
            serialCommOverlapped.EventHandle = m_SerialCommEvent.GetSafeWaitHandle().DangerousGetHandle();
#else
            serialCommOverlapped.EventHandle = m_SerialCommEvent.SafeWaitHandle.DangerousGetHandle();
#endif
            // ReadFile
            bool readPending = false;
            m_ReadEvent.Reset();
            NativeOverlapped readOverlapped = new NativeOverlapped();
#if NETSTANDARD15
            readOverlapped.EventHandle = m_ReadEvent.GetSafeWaitHandle().DangerousGetHandle();
#else
            readOverlapped.EventHandle = m_ReadEvent.SafeWaitHandle.DangerousGetHandle();
#endif
            // WriteFile
            bool writePending = false;
            m_WriteEvent.Reset();
            NativeOverlapped writeOverlapped = new NativeOverlapped();
            m_ReadByteAvailable = false;
#if NETSTANDARD15
            writeOverlapped.EventHandle = m_WriteEvent.GetSafeWaitHandle().DangerousGetHandle();
#else
            writeOverlapped.EventHandle = m_WriteEvent.SafeWaitHandle.DangerousGetHandle();
#endif
            // SEt up the types of serial events we want to see.
            UnsafeNativeMethods.SetCommMask(m_ComPortHandle, maskRead);

            bool result;
            NativeMethods.SerialEventMask commEventMask = 0;

            bool running = true;
            uint bytes;
            List <WaitHandle> handles = new List <WaitHandle>(10);

            while (running)
            {
                handles.Clear();
                handles.Add(m_StopRunning);
                handles.Add(m_WriteClearEvent);

#if PL2303_WORKAROUNDS
                // - - - - - - - - - - - - - - - - - - - - - - - - -
                // PROLIFIC PL23030 WORKAROUND
                // - - - - - - - - - - - - - - - - - - - - - - - - -
                // If we have a read pending, we don't request events
                // for reading data. To do so will result in errors.
                // Have no idea why.
                if (readPending)
                {
                    UnsafeNativeMethods.SetCommMask(m_ComPortHandle, maskReadPending);
                }
                else
                {
                    UnsafeNativeMethods.SetCommMask(m_ComPortHandle, maskRead);

                    // While the comm event mask was set to ignore read events, data could have been written
                    // to the input queue. Check for that and if there are bytes waiting or EOF was received,
                    // set the appropriate flags.
                    uint bytesInQueue;
                    bool eofReceived;
                    if (GetReceiveStats(out bytesInQueue, out eofReceived) && (bytesInQueue > 0 || eofReceived))
                    {
                        // Tell DoReadEvent that there is data pending
                        m_ReadByteAvailable = true;
                        m_ReadByteEof      |= eofReceived;
                    }
                }
#else
                UnsafeNativeMethods.SetCommMask(m_ComPortHandle, maskRead);
#endif

                // commEventMask is on the stack, and is therefore fixed
                if (!serialCommError)
                {
                    try {
                        if (!serialCommPending)
                        {
                            serialCommPending = DoWaitCommEvent(out commEventMask, ref serialCommOverlapped);
                        }
                        if (serialCommPending)
                        {
                            handles.Add(m_SerialCommEvent);
                        }
                    } catch (System.IO.IOException) {
                        // Some devices, such as the Arduino Uno with a CH340 on board don't support an overlapped
                        // WaitCommEvent. So if that occurs, we remember it and don't use it again. The Windows error
                        // returned was 87 (ERROR_INVALID_PARAMETER) was returned in that case. GetReceiveStats() did
                        // work, so we can still know of data pending by polling. But we won't get any other events,
                        // such as TX_EMPTY.
                        SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Warning, 0,
                                                        "{0}: SerialThread: Not processing WaitCommEvent events", m_Name);
                        serialCommError = true;
                    }
                }

                if (!readPending)
                {
                    if (!m_Buffer.Serial.ReadBufferNotFull.WaitOne(0))
                    {
                        SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "{0}: SerialThread: Read Buffer Full", m_Name);
                        handles.Add(m_Buffer.Serial.ReadBufferNotFull);
                    }
                    else
                    {
                        readPending = DoReadEvent(ref readOverlapped);
                    }
                }
                if (readPending)
                {
                    handles.Add(m_ReadEvent);
                }

                if (!writePending)
                {
                    if (!m_Buffer.Serial.WriteBufferNotEmpty.WaitOne(0))
                    {
                        handles.Add(m_Buffer.Serial.WriteBufferNotEmpty);
                    }
                    else
                    {
                        writePending = DoWriteEvent(ref writeOverlapped);
                    }
                }
                if (writePending)
                {
                    handles.Add(m_WriteEvent);
                }

                // We wait up to 100ms, in case we're not actually pending on anything. Normally, we should always be
                // pending on a Comm event. Just in case this is not so (and is a theoretical possibility), we will
                // slip out of this WaitAny() after 100ms and then restart the loop, effectively polling every 100ms in
                // worst case.
                WaitHandle[] whandles = handles.ToArray();
                int          ev       = WaitHandle.WaitAny(whandles, 100);

                if (ev != WaitHandle.WaitTimeout)
                {
                    if (whandles[ev] == m_StopRunning)
                    {
                        SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "{0}: SerialThread: Thread closing", m_Name);
                        result = UnsafeNativeMethods.CancelIo(m_ComPortHandle);
                        if (!result)
                        {
                            int win32Error = Marshal.GetLastWin32Error();
                            int hr         = Marshal.GetHRForLastWin32Error();
                            SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Warning, 0,
                                                            "{0}: SerialThread: CancelIo error {1}", m_Name, win32Error);
                            Marshal.ThrowExceptionForHR(hr);
                        }
                        running = false;
                    }
                    else if (whandles[ev] == m_SerialCommEvent)
                    {
                        result = UnsafeNativeMethods.GetOverlappedResult(m_ComPortHandle, ref serialCommOverlapped, out bytes, true);
                        if (!result)
                        {
                            int win32Error = Marshal.GetLastWin32Error();
                            int hr         = Marshal.GetHRForLastWin32Error();
                            SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Error, 0,
                                                            "{0}: SerialThread: Overlapped WaitCommEvent() error {1}", m_Name, win32Error);
                            Marshal.ThrowExceptionForHR(hr);
                        }
                        ProcessWaitCommEvent(commEventMask);
                        serialCommPending = false;
                    }
                    else if (whandles[ev] == m_ReadEvent)
                    {
                        result = UnsafeNativeMethods.GetOverlappedResult(m_ComPortHandle, ref readOverlapped, out bytes, true);
                        if (!result)
                        {
                            int win32Error = Marshal.GetLastWin32Error();
                            int hr         = Marshal.GetHRForLastWin32Error();
                            // Should never get ERROR_IO_PENDING, as this method is only called when the event is triggered.
                            if (win32Error != WinError.ERROR_OPERATION_ABORTED || bytes > 0)
                            {
                                SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Error, 0,
                                                                "{0}: SerialThread: Overlapped ReadFile() error {1} bytes {2}", m_Name, win32Error, bytes);
                                Marshal.ThrowExceptionForHR(hr);
                            }
                            else
                            {
                                // ERROR_OPERATION_ABORTED may be caused by CancelIo or PurgeComm
                                if (SerialTrace.TraceSer.Switch.ShouldTrace(System.Diagnostics.TraceEventType.Verbose))
                                {
                                    SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0,
                                                                    "{0}: SerialThread: Overlapped ReadFile() error {1} bytes {2}", m_Name, win32Error, bytes);
                                }
                            }
                        }
                        else
                        {
                            ProcessReadEvent(bytes);
                        }
                        readPending = false;
                    }
                    else if (whandles[ev] == m_Buffer.Serial.ReadBufferNotFull)
                    {
                        // The read buffer is no longer full. We just loop back to the beginning to test if we
                        // should read or not.
                    }
                    else if (whandles[ev] == m_WriteEvent)
                    {
                        result = UnsafeNativeMethods.GetOverlappedResult(m_ComPortHandle, ref writeOverlapped, out bytes, true);
                        if (!result)
                        {
                            int win32Error = Marshal.GetLastWin32Error();
                            int hr         = Marshal.GetHRForLastWin32Error();
                            // Should never get ERROR_IO_PENDING, as this method is only called when the event is triggered.
                            if (win32Error != WinError.ERROR_OPERATION_ABORTED || bytes > 0)
                            {
                                SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Error, 0,
                                                                "{0}: SerialThread: Overlapped WriteFile() error {1} bytes {2}", m_Name, win32Error, bytes);
                                Marshal.ThrowExceptionForHR(hr);
                            }
                            else
                            {
                                // ERROR_OPERATION_ABORTED may be caused by CancelIo or PurgeComm
                                if (SerialTrace.TraceSer.Switch.ShouldTrace(System.Diagnostics.TraceEventType.Verbose))
                                {
                                    SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0,
                                                                    "{0}: SerialThread: Overlapped WriteFile() error {1} bytes {2}", m_Name, win32Error, bytes);
                                }
                            }
                        }
                        else
                        {
                            ProcessWriteEvent(bytes);
                        }
                        writePending = false;
                    }
                    else if (whandles[ev] == m_Buffer.Serial.WriteBufferNotEmpty)
                    {
                        // The write buffer is no longer empty. We just loop back to the beginning to test if we
                        // should write or not.
                    }
                    else if (whandles[ev] == m_WriteClearEvent)
                    {
                        if (writePending)
                        {
                            SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "{0}: SerialThread: PurgeComm() write pending", m_Name);
                            m_PurgePending = true;
                            result         = UnsafeNativeMethods.PurgeComm(m_ComPortHandle,
                                                                           NativeMethods.PurgeFlags.PURGE_TXABORT | NativeMethods.PurgeFlags.PURGE_TXCLEAR);
                            if (!result)
                            {
                                int win32Error = Marshal.GetLastWin32Error();
                                int hr         = Marshal.GetHRForLastWin32Error();
                                if (win32Error != WinError.ERROR_OPERATION_ABORTED)
                                {
                                    SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Error, 0,
                                                                    "{0}: SerialThread: PurgeComm() error {1}", m_Name, win32Error);
                                    Marshal.ThrowExceptionForHR(hr);
                                }
                                else
                                {
                                    if (SerialTrace.TraceSer.Switch.ShouldTrace(System.Diagnostics.TraceEventType.Verbose))
                                    {
                                        SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0,
                                                                        "{0}: SerialThread: PurgeComm() error {1}", m_Name, win32Error);
                                    }
                                }
                            }
                        }
                        else
                        {
                            lock (m_Buffer.WriteLock) {
                                SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "{0}: SerialThread: Purged", m_Name);
                                m_Buffer.Serial.Purge();
                                m_WriteClearDoneEvent.Set();
                            }
                        }
                    }
                }

#if STRESSTEST
                SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "{0}: STRESSTEST SerialThread: Stress Test Delay of 1000ms", m_Name);
                System.Threading.Thread.Sleep(1000);
                NativeMethods.ComStatErrors commStateErrors = new NativeMethods.ComStatErrors();
                NativeMethods.COMSTAT       commStat        = new NativeMethods.COMSTAT();
                result = UnsafeNativeMethods.ClearCommError(m_ComPortHandle, out commStateErrors, out commStat);
                if (result)
                {
                    SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Information, 0,
                                                    "{0}: STRESSTEST SerialThread: ClearCommError errors={1}", m_Name, commStateErrors);
                    SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Information, 0,
                                                    "{0}: STRESSTEST SerialThread: ClearCommError stats flags={1}, InQueue={2}, OutQueue={3}", m_Name, commStat.Flags, commStat.cbInQue, commStat.cbOutQue);
                }
                else
                {
                    SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Warning, 0,
                                                    "{0}: STRESSTEST SerialThread: ClearCommError error: {1}", m_Name, Marshal.GetLastWin32Error());
                }
#endif
            }
        }
 private void SerialPortIo_CommEvent(object sender, NativeSerialPort.CommOverlappedIo.CommEventArgs e)
 {
     if (IsDisposed || DataReceived == null && PinChanged == null) {
         m_CommEvent = 0;
         return;
     } else {
         lock (m_EventCheck) {
             m_CommEvent |= e.EventType;
         }
         CallEvent();
     }
 }
        /// <summary>
        /// Raise events to the calling application of this class
        /// </summary>
        /// <remarks>
        /// This function should be executed from a ThreadPool thread. This method will check
        /// flags, call user events. It will repeat until no events remain pending.
        /// </remarks>
        /// <param name="state">Not used</param>
        private void HandleEvent(object state)
        {
            NativeMethods.SerialEventMask commEvent;
            NativeMethods.ComStatErrors commErrorEvent;

            bool handleEvent;
            lock (m_EventCheck) {
                handleEvent = (m_CommEvent != 0) || (m_CommErrorEvent != 0);
                commEvent = m_CommEvent;
                m_CommEvent = 0;
                commErrorEvent = m_CommErrorEvent;
                m_CommErrorEvent = 0;
            }

            while (handleEvent) {
                // Received Data
                if ((commEvent & NativeMethods.SerialEventMask.EV_RXFLAG) != 0) {
                    lock (m_EventCheck) { if (IsDisposed) { handleEvent = false; break; } }
                    OnDataReceived(new SerialDataReceivedEventArgs(SerialData.Eof));
                } else if ((commEvent & NativeMethods.SerialEventMask.EV_RXCHAR) != 0) {
                    lock (m_EventCheck) { if (IsDisposed) { handleEvent = false; break; } }
                    if (m_SerialPort.SerialPortIo.BytesToRead >= m_RxThreshold) {
                        OnDataReceived(new SerialDataReceivedEventArgs(SerialData.Chars));
                    }
                }

                // Modem Pin States
                if ((commEvent & NativeMethods.SerialEventMask.EV_CTS) != 0) {
                    lock (m_EventCheck) { if (IsDisposed) { handleEvent = false; break; } }
                    OnPinChanged(new SerialPinChangedEventArgs(SerialPinChange.CtsChanged));
                }
                if ((commEvent & NativeMethods.SerialEventMask.EV_RING) != 0) {
                    lock (m_EventCheck) { if (IsDisposed) { handleEvent = false; break; } }
                    OnPinChanged(new SerialPinChangedEventArgs(SerialPinChange.Ring));
                }
                if ((commEvent & NativeMethods.SerialEventMask.EV_RLSD) != 0) {
                    lock (m_EventCheck) { if (IsDisposed) { handleEvent = false; break; } }
                    OnPinChanged(new SerialPinChangedEventArgs(SerialPinChange.CDChanged));
                }
                if ((commEvent & NativeMethods.SerialEventMask.EV_DSR) != 0) {
                    lock (m_EventCheck) { if (IsDisposed) { handleEvent = false; break; } }
                    OnPinChanged(new SerialPinChangedEventArgs(SerialPinChange.DsrChanged));
                }
                if ((commEvent & NativeMethods.SerialEventMask.EV_BREAK) != 0) {
                    lock (m_EventCheck) { if (IsDisposed) { handleEvent = false; break; } }
                    OnPinChanged(new SerialPinChangedEventArgs(SerialPinChange.Break));
                }

                // Error States
                if ((commErrorEvent & NativeMethods.ComStatErrors.CE_TXFULL) != 0) {
                    lock (m_EventCheck) { if (IsDisposed) { handleEvent = false; break; } }
                    OnCommError(new SerialErrorReceivedEventArgs(SerialError.TXFull));
                }
                if ((commErrorEvent & NativeMethods.ComStatErrors.CE_FRAME) != 0) {
                    lock (m_EventCheck) { if (IsDisposed) { handleEvent = false; break; } }
                    OnCommError(new SerialErrorReceivedEventArgs(SerialError.Frame));
                }
                if ((commErrorEvent & NativeMethods.ComStatErrors.CE_RXPARITY) != 0) {
                    lock (m_EventCheck) { if (IsDisposed) { handleEvent = false; break; } }
                    OnCommError(new SerialErrorReceivedEventArgs(SerialError.RXParity));
                }
                if ((commErrorEvent & NativeMethods.ComStatErrors.CE_OVERRUN) != 0) {
                    lock (m_EventCheck) { if (IsDisposed) { handleEvent = false; break; } }
                    OnCommError(new SerialErrorReceivedEventArgs(SerialError.Overrun));
                }
                if ((commErrorEvent & NativeMethods.ComStatErrors.CE_RXOVER) != 0) {
                    lock (m_EventCheck) { if (IsDisposed) { handleEvent = false; break; } }
                    OnCommError(new SerialErrorReceivedEventArgs(SerialError.RXOver));
                }

                lock (m_EventCheck) {
                    handleEvent = (m_CommEvent != 0) || (m_CommErrorEvent != 0);
                    commEvent = m_CommEvent;
                    m_CommEvent = 0;
                    commErrorEvent = m_CommErrorEvent;
                    m_CommErrorEvent = 0;
                }
            }

            m_EventProcessing.Reset();
        }
Beispiel #8
0
 public static extern bool WaitCommEvent(SafeFileHandle hFile,
                                         out NativeMethods.SerialEventMask lpEvtMask,
                                         ref System.Threading.NativeOverlapped lpOverlapped);
Beispiel #9
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="eventType">The event results.</param>
 public CommEventArgs(NativeMethods.SerialEventMask eventType)
 {
     m_EventType = eventType;
 }
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="eventType">The event results.</param>
 public CommEventArgs(NativeMethods.SerialEventMask eventType)
 {
     m_EventType = eventType;
 }