Exemplo n.º 1
0
 /// <summary>
 /// Gets the status of bytes received by the serial provider that haven't been read yet. If there is
 /// a failure in obtaining the information, zero is returned.
 /// </summary>
 /// <param name="bytesInRecvQueue">Output indicating number of bytes in queue but not read by ReadFile.</param>
 /// <param name="eofReceived">Output indicating whether an EOF character was received.</param>
 /// <returns>true if the stats were received, otherwise false.</returns>
 /// <remarks>
 /// Getting this information has the side effect of processing and clearing any serial port
 /// errors and firing CommErrorEvent.
 /// </remarks>
 private bool GetReceiveStats(out uint bytesInRecvQueue, out bool eofReceived)
 {
     bytesInRecvQueue = 0;
     eofReceived      = false;
     lock (m_Buffer.ReadLock) {
         NativeMethods.ComStatErrors cErr;
         NativeMethods.COMSTAT       comStat = new NativeMethods.COMSTAT();
         bool result = UnsafeNativeMethods.ClearCommError(m_ComPortHandle, out cErr, out comStat);
         if (!result)
         {
             int w32err = Marshal.GetLastWin32Error();
             int hr     = Marshal.GetHRForLastWin32Error();
             SerialTrace.TraceSer.TraceEvent(System.Diagnostics.TraceEventType.Error, 0,
                                             "{0}: SerialThread: BytesInSerialQueue: ClearCommError() error {1}", m_Name, w32err);
             Marshal.ThrowExceptionForHR(hr);
             return(false);
         }
         if (cErr != 0)
         {
             OnCommErrorEvent(new CommErrorEventArgs(cErr));
         }
         bytesInRecvQueue = comStat.cbInQue;
         eofReceived      = ((comStat.Flags & NativeMethods.ComStatFlags.Eof) == NativeMethods.ComStatFlags.Eof);
         return(true);
     }
 }
 public static extern bool ClearCommError(
     [In] SafeFileHandle hFile,
     [MarshalAs(UnmanagedType.U4)] out NativeMethods.ComStatErrors lpErrors,
     [In, Out] ref NativeMethods.COMSTAT lpStat
     );
Exemplo n.º 3
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
            }
        }
Exemplo n.º 4
0
 public static extern bool ClearCommError(
     [In] SafeFileHandle hFile,
     out NativeMethods.ComStatErrors lpErrors,
     [Out] out NativeMethods.COMSTAT lpStat
     );