«Не удается получить доступ к удаленному объекту» во время отображения данных последовательного порта в поле Rich Text Box - PullRequest
0 голосов
/ 01 ноября 2018

Я пытаюсь создать Serial Data Plotter, используя C #. У меня есть Arduino, выступающий в качестве устройства последовательной передачи данных, и он постоянно отправляет данные на ПК. Мое приложение должно получать эти данные и отображать их в текстовом поле. Дальнейшее применение намного сложнее, но это основной строительный блок.

Теперь данные принимаются правильно, если скорость передачи данных низкая. Я могу подключить и отключить последовательный порт с помощью кнопки «Предоставлено».

Проблема возникает при высокой скорости передачи данных. Во время получения данных, если я пытаюсь отключиться, выдается ошибка «Не удается получить доступ к удаленному объекту». Я отправляю код для того же:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO.Ports;
using System.Windows.Forms;

namespace Serial_Port_reference
{
    public partial class Form1 : Form
    {
        bool cnctBtn_state = false;

        public SerialPort sp = new SerialPort();
        string[] portNames;

        delegate void SetTextCallback(string text);
        delegate void SetIntCallback(int value);

        bool spClosesd = false;

        private void SetoutputRtb(string text)
        {
            if (outputRtb.InvokeRequired)
            {

                SetTextCallback d = new SetTextCallback(SetoutputRtb);
                Invoke(d, new object[] { text });
            }
            else
            {
                outputRtb.AppendText(text);
                outputRtb.ScrollToCaret();
            }
        }

        public Form1()
        {
            InitializeComponent();
        }

        //Serial Port Receive Interrupt
        public void serialReceived(object sender, SerialDataReceivedEventArgs e)
        {
            string temp = null;
            try
            {
                temp = sp.ReadTo("#");
                SetoutputRtb(temp);
            }
            catch (Exception err)
            {
                MessageBox.Show(err.ToString());
            }


        }

        private void Form1_Load(object sender, EventArgs e)
        {
            cnctBtn.BackColor = Color.ForestGreen;
            cnctBtn.ForeColor = Color.White;

            sp.DataReceived += new SerialDataReceivedEventHandler(serialReceived);
            portNames = SerialPort.GetPortNames();

            foreach (string ports in portNames)
            {
                portCombo.Items.Add(ports);
            }

            portCombo.SelectedIndex = 0;



        }

        private void cnctBtn_Click(object sender, EventArgs e)
        {
            if (cnctBtn_state == false)
            {
                sp.PortName = portCombo.SelectedItem.ToString();
                sp.BaudRate = int.Parse(baudCombo.SelectedItem.ToString());
                sp.DataBits = 8;
                sp.StopBits = StopBits.One;
                sp.Parity = Parity.None;

                try
                {
                    sp.Open();
                }
                catch (Exception err)
                {
                    MessageBox.Show(err.ToString());
                    return;
                }

                cnctBtn.Text = "Disconnect";
                cnctBtn.BackColor = Color.PaleVioletRed;

                cnctBtn_state = true;
            }
            else
            {
                try
                {
                    //Thread.Sleep(1000);
                    sp.DiscardInBuffer();

                    //sp.Dispose();
                }
                catch (Exception err)
                {
                    MessageBox.Show(err.ToString());
                    return;
                }

                cnctBtn.Text = "Connect";
                cnctBtn.BackColor = Color.LightCoral;

                cnctBtn_state = false;
            }
        }

        private void sendTxtButton_Click(object sender, EventArgs e)
        {
            try
            {
                sp.Write(sendTxtBox.Text);
            }
            catch (Exception err)
            {
                MessageBox.Show(err.ToString());
            }

            sendTxtBox.Clear();
        }
    }
}

Я использовал метод Invoke для выполнения межпотоковых вызовов для записи в RTB, и там программа останавливается.

Я пробовал много разных вещей в соответствии с моим пониманием, а также предложения, данные на разных форумах, но ни один из них не имеет конкретного отношения к последовательному порту, поэтому я не могу их реализовать. Я понимаю природу проблемы, но не знаю, как реализовать безопасный метод для перехвата ObjectDisposeExceptions или главным образом для их предотвращения. Я немного новичок в программировании на C #, поэтому, возможно, потребуется немного больше объяснений по части программирования. Заранее спасибо!

1 Ответ

0 голосов
/ 05 ноября 2018

Я предполагаю, что вы закрываете соединение с помощью sp.Dispose ()? В настоящее время это закомментировано в вашем коде. Обычно вы хотите использовать Close () - он отбрасывает как входной, так и выходной буферы.

Но я думаю, что вы, возможно, получаете данные, и событие DataReceived запускается, но не обязательно доставляется, когда вы выполняете Dispose. Когда он доставлен, ваш последовательный порт sp уже удален, поэтому я подозреваю, что вы получаете ошибку sp.ReadTo () в вашей функции serialReceived (). На самом деле, при ближайшем рассмотрении, когда (если) вы делаете Close (), вы получите еще одно событие - указание «конец файла» вызовет событие DataReceived.

Вы можете установить sp = null после удаления, а затем проверить это в функции serialReceived () перед чтением. В любом случае, это хорошая практика. Как вы, вероятно, знаете, событие DataReceived будет запускать serialReceived в другом потоке. Я не уверен, может ли это быть запланировано ранее между вызовом Dispose () и sp = null.

Итак, я думаю, что лучше всего отключить событие перед вызовом Close ():

sp.DataReceived += serialReceived.

Другое дело - я раньше не использовал ReadTo (), но похоже, что он будет блокироваться, пока этот символ не будет получен. Это то, что вы ожидаете? Если персонаж входит, ваше событие будет запущено, но этот поток будет блокироваться до тех пор, пока не будет получено «#». Я думаю, что более типичный обработчик событий DataReceived будет читать и обрабатывать все доступные байты и анализировать его.

...