Laden...

SerialPort.Close friert Form ein

Erstellt von Marco_78 vor 10 Jahren Letzter Beitrag vor 10 Jahren 6.336 Views
M
Marco_78 Themenstarter:in
5 Beiträge seit 2014
vor 10 Jahren
SerialPort.Close friert Form ein

Hallo Comunity,

zu erst: Ich habe mich heut erst angemeldet lese aber seit einiger Zeit schon im Forum mit.
Da ich nur Freizeit Programierer bin, bitte ich um Verständnis für meinen schlechten Programierstil.

Ich Versuche gerade mit einer C# Windows Form Daten von meinem Arduino Uno zu
empfangen --> was auch schon Funktioniert 🙂

Das Problem ist das beim Beenden der Kommunikation "serial1.Close()" die Form Sporadisch einfriert. Die Form erscheint dann im Vordergrund reagiert aber nicht mehr. Dann muss ich die Form per Debugg Stop anhalten. Es werden keine Fehlermeldungen ausgegeben !

Da der Eventhandler ein eigener Thread ist, vermute ich das bei Stop ich nie weis ob der Eventhandler fertig ist mit seinem letzten Event 🤔 oder so ?

Mein Code:

 private void Form1_Load(object sender, EventArgs e)
        {
            portName = "COM3";
            getComPorts();           //hier werden die Portinformationen gelesen
            
            //erstellen von myDelegate
            this.myDelegate = new update_txtRecivedDelegate(update_txtRecived);
        }

Eventhandler registrieren & Port öffnen

 private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                serial1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
                //****
                serial1.BaudRate = 9600;
                serial1.Parity = Parity.None;
                serial1.DataBits = 8;
                serial1.StopBits = StopBits.One;

                serial1.Open();

                //****
                serial1.DiscardInBuffer();  // Vorhandene Daten im Empfangspuffer löschen
                btnStart.Enabled = false;
                btnStop.Enabled = true;

                tsLabel1.Text = portName + ": Verbunden";
            }
            catch (Exception ex)
            {
                MessageBox.Show("Fehler beim öffnen von: " + portName + Environment.NewLine
                    + ex);

                //****
                serial1.DiscardInBuffer();
                serial1.Close();        
                serial1.Dispose();

                btnStart.Enabled = true;
                btnStop.Enabled = false;

                tsLabel1.Text = portName + ": Getrennt";
            }
        }

Eventhandler abmelden & Port schliessen
--> bei serial1.Close(); friert die APP ein ????
--> dies geschieht nicht immer, Auffällig ist das es bei schnelleren Daten Empfang zu passiern scheint, aber auch so nach dem 3-5 Start Stop wechsel

 private void btnStop_Click(object sender, EventArgs e)
        {
            // Eventhandler Stoppen, Serielle Schnittstelle schliessen
                // Eventhandler abmelden
                serial1.DataReceived -= new SerialDataReceivedEventHandler(DataReceivedHandler);

            try
            {
                // Puffer leeren
                serial1.DiscardInBuffer();
                // Port schliessen
                serial1.Close();               //--> *** Friert die APP ein *****
                // Ressourcen freigeben
                serial1.Dispose();

                btnStart.Enabled = true;
                btnStop.Enabled = false;
                //Label aktualisieren
                tsLabel1.Text = portName + ": Getrennt";
            }
            catch (Exception ex)
            {
                btnStart.Enabled = true;
                btnStop.Enabled = false;

                MessageBox.Show("Fehler beim schliessen von: " + portName + Environment.NewLine
                    + ex);
            }
        }

Der Event Handler mit der Text Update Methode 🤔
(die Methode ist nicht mehr vom Typ static da ich da noch mehr Probleme mit dem Thread Übergreifenden Befehlen bekam beim auslösen von serial1.Close(); )

 private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;
            //string s = sp.ReadExisting();         //aus Beispiel nicht verwendet

            try
            {
                string s = sp.ReadLine();   //Lese bis "/n"
                txtRecived.Invoke(this.myDelegate, new Object[] { s });
            }
            catch (Exception ex)
            {
                txtRecived.Invoke(this.myDelegate, new Object[] { ex });
            }
        }

        private void update_txtRecived(String inData)
        {
            try
            {
                pbA0.Value = Convert.ToInt32(inData);
                //****
                txtRecived.AppendText(inData);
                txtRecived.AppendText("\r\n");
            }
            catch
            {
                txtRecived.AppendText("Daten Fehlerhaft ! ! ! : ");
                txtRecived.AppendText(inData);
                txtRecived.AppendText("\r\n");
            }
        }

Ich wäre Dankbar für ein paar Tips woran das liegen könnte.

Freizeit c# Progger in Lernphase

849 Beiträge seit 2006
vor 10 Jahren

Hallo,

also auf dem ersten Blick würde ich mich fragen warum hier überhaupt Start/Stop wechsel möglich sind. Bei BtnStop Disposed Du den Port.. erstellst aber beim Start keinen neuen. Da hätte ich schon eine ObjectDisposedException erwartet. Ist scheinbar in SerialPort nicht implementiert. Lass das Dispose weg oder erstell bei BtnStart jeweils eine neue Instanz.

Ohne Gewähr 😉

Gruß

16.807 Beiträge seit 2008
vor 10 Jahren

Die beiden anderen aktuellen Threads mit fast gleichen Symptomen helfen Dir nicht weiter?
Serial Port schließen hängt, wenn Gerät nicht mehr verfügbar
SerialPort Hotplug

M
Marco_78 Themenstarter:in
5 Beiträge seit 2014
vor 10 Jahren

Hallo,

@Abt --> ich habe die beiden Threads gelesen finde aber irgendwie nicht den gemeinsamen Nenner es werden zwar Vorschläge gemacht aber ich verstehe nicht wie ich die bei mir anwende...
auch der Workaround wird mit eigens erstellten Theads gemacht die ich ja nicht habe...

@unconnected --> der Port ist in der Klasse initalisiert (glaub so sagt man das)
oder meist Du den myDelegate das neu erstellt werden muss? 🤔

bsp:

 
myClass{

SerialPort serial1 = new SerialPort(); 

private void Form1_Load(object sender, EventArgs e)
        {
            portName = "COM3";
            getComPorts();           //hier werden die Portinformationen gelesen

            //erstellen von myDelegate
            this.myDelegate = new update_txtRecivedDelegate(update_txtRecived);
        }
}

Wenn ich den Port mit dem Start Button erstelle dann kann ich ja nicht mehr von anderen Methoden darauf zugreifen? (bsp: "my_get_PortInfo()" --> um alle Ports zu ermitteln oder my_set_PortInfo() --> um dem ausgewählten Port seine neuen Parameter zuzuweisen)


Die Frage: die sich mir stellt ist wie Unterbreche ich den Recived_Eventhandler (der läuft ja in seinem eigenen Thread ) wie fange den ab, so das er auch beendet ist bevor ich den Port schliesse?

Wenn ich mit Breakpoints mich durch das Programm hangele dann seh ich das es eine ganze weile dauert bis ich im Stop_Button Event lande davor werden noch fleisig Daten gelesen und aktualisiert.

Danke

Freizeit c# Progger in Lernphase

16.807 Beiträge seit 2008
vor 10 Jahren

ich habe die beiden Threads gelesen finde aber irgendwie nicht den gemeinsamen Nenner

Ich denke nicht, dass Du die Threads aufmerksam gelesen hast, dann a) verwendest Du Close UND Dispose obwohl ich in einem Thread erklärt habe, dass Close Dispose bereits aufruft und b) wird genau erklärt wie man auf Fehler reagiert, die in Close auftreten können.
Egal wo also nun der Fehler ist: wenn du dies korrekt anwendest wird zumindest des Hängen der Anwendung vermieden. Nichts desto trotz scheinst Du einen logischen Fehler zu haben, den Du mit Tracing/Debugging herausfinden könntest.
Aus dem Stück Code ist das nicht wirklich so leicht da irgendwas zu erahnen.... bzw. achte auf unconnecteds Einwand.

849 Beiträge seit 2006
vor 10 Jahren

Hallo Marco,

das eine Instanz vorhanden ist habe ich nicht bezweifelt. Nur das Du sobald du stop drückst den Port disposed -> ungültig machst. Danach brauchst Du einen neue Instanz vom SerialPort, die Du am besten im Start handler erstellst.

M
Marco_78 Themenstarter:in
5 Beiträge seit 2014
vor 10 Jahren

Hallo

Ich versuche euch beiden zu folgen. Mein Code ist im moment nicht Länger als das was ich schon gepostet habe. ⚠ Ich habe jetzt aber den Code noch mal so geändert das die 2 Methoden zur Porterkennung und Anzeigen entfallen sind.

Aktuell tu ich nichts anderes als :

  • die Instanz für den Port erstellen ( Global)
  • die delegant Funktion erstellen um meine Textbox (Empfangen) beschreiben zu können wenn der Data Recived Handler Daten empfängt.
    -mit Start Stop den Eventhandler unterbrechen und den Port schliessen [ ⚠ dies wird benötigt da ein anderes Program den Arduino Programieren muss auf dem selben Port (USB/RS232 intern im Arduino gemacht)

Das Dispose hab ich überall entfernt --> wird also nur vom Close Event ausgeführt.
-? Wird dann die Instanz die Global erstellt war auch zerstört?
--> aber dann müsste ich die Form ja jedesmal neu Starten um die ganze Initalisierung wieder korrekt zu erstellen ??

Ihr habt gesagt das ich den Serial Port Close per try/catch abfangen soll --> hab gemacht
[Wenn keine Daten ankommen funktioniert der Start Stop] -> bei einem dauerhaften Daten Strom auf den Serial Port & Stop.btn Friert die Form ein --> OHNE in den Catch zu kommen !

Wenn ich im StartButton_Click Event den Serial Port neu Instansiere dann kann ich ihn nicht mehr schliessen da mein StopButton_Click Event diesen Port dann ja nicht mehr kennt 🤔

Wie erstelle ich eine neue Instanz von einer Funktion aus die von Allen Methoden dann auch erkannt wird ? ?(


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

namespace Test_AduCom
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    
        public SerialPort sp1 = new SerialPort();   // Serial Port 1 Instanz öffnen

        public delegate void update_txtRecivedDelegate(String text);
        public update_txtRecivedDelegate myDelegate;

        private void Form1_Load(object sender, EventArgs e)
        {
            sp1.PortName = "COM7";  //COM3 = PC / COM7 = LAPPI
            sp1.BaudRate = 9600;
            sp1.Parity = Parity.None;
            sp1.DataBits = 8;
            sp1.StopBits = StopBits.One;

            //erstellen von myDelegate
            this.myDelegate = new update_txtRecivedDelegate(update_txtRecived);
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            try{
                sp1.Close();

                if (!sp1.IsOpen)
                { Application.Exit(); }
                else
                { return; }     // ist zwar doppelt bin mir aber nicht sicher ob der try/catch Ausgeführt wird
            }
            catch
            { return; }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            sp1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);

            try{
                sp1.Open();

                //****
                sp1.DiscardInBuffer();  // Vorhandene Daten im Empfangspuffer löschen

                btnStart.Enabled = false; btnStop.Enabled = true;
            }
            catch 
            { MessageBox.Show("Fehler beim öffnen !"); }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            sp1.DataReceived -= new SerialDataReceivedEventHandler(DataReceivedHandler);

            try{
                sp1.DiscardInBuffer();  // Vorhandene Daten im Empfangspuffer löschen

                try{                //************************
                    sp1.Close();    // --> hier bleibt das Programm hängen [catch wird nicht erreicht]
                }
                catch(Exception ex) 
                { MessageBox.Show("Fehler: " + ex); }
                
                btnStart.Enabled = true; btnStop.Enabled = false;
            }
            catch(Exception ex)
            {
                MessageBox.Show("Fehler beim schliessen ! --> " + ex);
                btnStart.Enabled = false; btnStop.Enabled = true;
            }
        }

        private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;
            
            try{
                string s = sp.ReadLine();   //Lese bis "/n"
                txtRecived.Invoke(this.myDelegate, new Object[] { s });
            }
            catch (Exception ex)
            { txtRecived.Invoke(this.myDelegate, new Object[] { ex }); }
        }

        private void update_txtRecived(String inData)
        {
            try{
                txtRecived.AppendText(inData);
                txtRecived.AppendText("\r\n");
            }
            catch
            {
                txtRecived.AppendText("Daten Fehlerhaft ! ! ! : "); //wird später gebraucht da der erste Stream
                txtRecived.AppendText(inData);                      //ab und zu falsche Einträge enthält
                txtRecived.AppendText("\r\n");
            }
        }
    }//end class
}//end namespace

Das ist der komplette Code X(

Sorry wie gesagt ich versuche zu Verstehen bin aber kein Programierer, also Danke für die Tips.

Gruß

Freizeit c# Progger in Lernphase

4.221 Beiträge seit 2005
vor 10 Jahren

Zieh dir mal einen Serialport im Designer auf das Form und verwende diesen.

Ich vermute, dass der Designer:

  1. einen anderen ctor aufruft
  2. den ctor erst aufruft nachdem das Handle des Forms erstellt ist

Gruss
Programmierhans

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

5.657 Beiträge seit 2006
vor 10 Jahren

Hi Marco_78,

ich seh hier zwei Probleme, das erste ist das mit dem SerialPort, das andere ist das Design deiner Software.

Daß das Schließen eines SerialPorts zum Aufhängen des Programms führen kann, ist ja schon in dem verlinkten Thread festgestellt worden, dort ist auch ein Verweis auf SerialPort.Close() hangs the application, wo diverse Workarounds beschrieben werden. Ich kann in deinem Code ehrlich gesagt nicht feststellen, daß du die Tips beherzigt hast, die dort gegeben werden.

Als erstes solltest du meiner Meinung nach mal die Workarounds ausprobieren, dazu brauchst du ja keine Benutzeroberfläche. Solange sich das Programm jedesmal aufhängt, wenn du den Port schließt, würde ich mir erstmal um die Benutzeroberfläche keine Gedanken machen.

Wenn der Zugriff auf den SerialPort dann soweit funktioniert würde ich das in eine eigene Klasse mit fest definierter Schnittstelle kapseln, dann kannst du die GUI unabhängig davon entwickeln, indem du die Schnittstelle verwendest. Dazu solltest du dir aber schon die wichtigsten Grundlagen aneignen, z.B. über WindowsForms im speziellen und Events im allgemeinen.

Christian

Weeks of programming can save you hours of planning

U
1.688 Beiträge seit 2007
vor 10 Jahren

Hallo,

am einfachsten ist, wenn Du BeginInvoke statt Invoke verwendest. Natürlich must Du in der entsprechend aufgerufenen Methode prüfen, ob Dein Fenster/Deine Controls noch gültig sind.

M
Marco_78 Themenstarter:in
5 Beiträge seit 2014
vor 10 Jahren

OK, Danke,

Aus dem Stück Code ist das nicht wirklich so leicht da irgendwas zu erahnen...

Im vorran gegangenen Thread wollte nur noch mal der gesamte Code wie er bis jetzt ist posten um zu zeigen das es nicht mehr gibt 😉 ohne alle Umsetzungen !

Ich werde das die Tage versuchen die Verschiedenen Tips umzusetzen. (Probieren)

Bei dem Workaround find ich nur irgendwie blöd das dafür noch ein eigener Thread gestartet werden muss, aber wenn es hilft 😉

Gruß

Freizeit c# Progger in Lernphase

M
Marco_78 Themenstarter:in
5 Beiträge seit 2014
vor 10 Jahren

Hallo

sorry hat etwas länger gedauert. Ich habe die Verschiedenen Möglichkeiten probiert.

BeginInvoke --> Verursacht bei mir einen Fehler ( 🤔 fällt mir gerade nicht ein) in der program.cs vor dem Aufruf

Application.Run(new Form1());

Den Serial Port über den Designer erstellen Verursacht trotzdem ein einfrieren der Form.

Also wie erwartet funktioniert der Weg über den Workaround Thread serialport-close-hangs-the-application

Freizeit c# Progger in Lernphase