Как правильно закрыть порт, чтобы избежать возникновения ошибки "System.OperationCanceledException" в библиотеке System.IO.Ports.dll?

Добрый день! <br/> Я разрабатываю приложение на .NET C# WPF, которое принимает и обрабатывает данные через COM-порт, используя класс SerialPorts из библиотеки System.IO.Ports.dll. На скриншотах я выделил красным полупрозрачным маркером те элементы, которые, по моему мнению, не влияют на возникшую ошибку. Это касается элементов WPF и других визуальных аспектов приложения.
  • 20 апреля 2025 г. 5:05
Ответы на вопрос 2
Добрый день!

Ошибка `System.OperationCanceledException` может возникать в различных ситуациях, связанных с работой с COM-портом. Чтобы правильно закрыть порт и избежать этой ошибки, можно следовать нескольким рекомендациям:

1. **Проверка состояния порта перед его закрытием**: Перед тем как закрыть COM-порт, убедитесь, что он открыт. Попытка закрыть закрытый или уже освобождённый порт может вызвать исключения.

   ```csharp
   if (serialPort.IsOpen)
   {
       serialPort.Close();
   }
   ```

2. **Использование `try-catch` для обработки исключений**: Обработайте возможные исключения при закрытии порта. Это поможет избежать сбоев в работе приложения.

   ```csharp
   try
   {
       if (serialPort.IsOpen)
       {
           serialPort.Close();
       }
   }
   catch (Exception ex)
   {
       // Обработка ошибок (например, логирование)
       Console.WriteLine($"Ошибка при закрытии порта: {ex.Message}");
   }
   ```

3. **Освобождение ресурсов**: Убедитесь, что порты и потоки, связанные с ними, правильно освобождаются. Например, если вы используете `DataReceived` или читающие потоки, убедитесь, что обработчики событий не остаются активными.

   ```csharp
   void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
   {
       // Обработка данных здесь
   }
   
   // Перед закрытием порта
   serialPort.DataReceived -= serialPort_DataReceived;
   ```

4. **Корректное завершение потоков**: Если вы используете дополнительные потоки или задачи для обработки данных из порта, убедитесь, что они корректно завершены перед закрытием порта.

   ```csharp
   // Завершение потоков
   if (dataProcessingThread.IsAlive)
   {
       dataProcessingThread.Join(); // Завершаем поток, если он работает
   }
   ```

5. **Использование метода `Dispose`**: После закрытия порта рекомендуется вызвать `Dispose`, чтобы освободить ресурсы, связанные с `SerialPort`.

   ```csharp
   serialPort.Close();
   serialPort.Dispose();
   ```

6. **Установка таймаутов**: Убедитесь, что таймауты для чтения и записи заданы корректно. Иногда долгождущие операции могут вызвать исключения, если вы не обрабатываете их должным образом.

7. **Мониторинг событий закрытия**: Может быть полезно отслеживать события закрытия порта и обрабатывать их, чтобы избежать неожиданных исключений.

Следуя этим рекомендациям, вы сможете снизить вероятность возникновения ошибки `System.OperationCanceledException` при работе с COM-портом в вашем приложении.
А не проще ловить событие порта DataReceived? 

...

_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.DataReceived += SerialPort_DataReceived;
_serialPort.ErrorReceived += SerialPort_ErrorReceived;

...

  public static void Disconnect()
        {
            try
            {
                if (_serialPort is not null && _serialPort.IsOpen)
                {
                    _serialPort.DataReceived -= SerialPort_DataReceived;
                    _serialPort.ErrorReceived -= SerialPort_ErrorReceived;
                    _serialPort.Close();
                }
            } // try
            catch(Exception ex)
            {
                Log.Error(ex, "Error closing the serial port.");
            } // 
        } // Disconnect
...


 private static void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (_serialPort?.IsOpen != true) return;

            try
            {
                var encoding = _serialPort.Encoding;
                var bytesRead = _serialPort.Read(_buffer, 0, _serialPort.BytesToRead);
                var rawBytes = new ReadOnlyMemory<byte>(_buffer, 0, bytesRead);

                var maxCharCount = encoding.GetMaxCharCount(bytesRead);
                var charArray = ArrayPool<char>.Shared.Rent(maxCharCount);

                var charsProduced = encoding.GetChars(
                    rawBytes.Span,
                    charArray.AsSpan()
                );

                var scannedData = new ScannedData(
                    rawBytes,
                    new ReadOnlyMemory<char>(charArray, 0, charsProduced)
                );

                IncomingData.Enqueue(scannedData);
                DataReceived?.Invoke(null, new DataReceivedEventArgs(scannedData));

                ArrayPool<char>.Shared.Return(charArray);
            } // try
            catch (Exception ex)
            {
                Log.Error(ex, "Error processing barcode data");
            } // catch
        } // _serialPort_DataReceived
Похожие вопросы