Jump to content

c# Disconnected Callback - Programm hängt sich an einer bestimmten Stelle auf...


Recommended Posts

Hallo Zusammen,

 

ich habe aktuell leider ein kleines Problem mit meinem Source-Code und weiß ehrlich gesagt nicht, wo ich mit dem Debuggen ansetzen soll :-(

Vielleicht kann mir ja einer von Euch hier weiter helfen?

 

Problembeschreibung:

 

Ich fülle im EnumerationCallback eine ListView mit den angeschlossenen Bricks.

Wenn ich nun die Verbindung trenne, dann soll diese Liste abgelöscht / geleert werden.

Dies funktioniert auch - wenn ich den Aufruf listView1.Items.Clear() direkt in der TFConnect Methode aufrufe (diese wird im Eventhandler Button_Click aufgerufen

Und es funktioniert auch, wenn ich die Liste im ConnectCallback leere.

 

Sobald ich den Aufruf aber im DisconnectCallback durchführe, egal an welcher Stelle, hängt sich das Programm auf und reagiert nicht mehr.

Nachfolgender Code wird nicht mehr ausgeführt.

 

Alles, was vor dem Aufruf von listView1.Items.Clear() im DisconnectCallback passiert (was derzeit noch nicht viel ist) funktioniert ohne Probleme.

 

Der Code läuft auch nicht in eine Exception hinein...

 

//=============================================================================
private TFConnect(string ConnectionCommand, string ConnectionString)
{
   string[] connections;
   connections = ConnectionString.Split(':');

   string HOST = connections[0];
   int PORT = Convert.ToInt16(connections[1]);

   switch (ConnectionCommand)
   {
      case "connect":
         try
         {                        
            if (ipcon.GetConnectionState() != IPConnection.CONNECTION_STATE_CONNECTED)
            {
               ipcon.Connect(HOST, PORT);    // TinkerForge IPConnection.Connect
               d_Logger.CreateLogFile("Connected to TinkerForge @ " + comboBox1.Text, "[3]");    // write LogOutput "Connected"
            }
         }
         catch (Exception ex)
         {
            d_Logger.CreateLogFile(ex.Message, "[8]");
         }
         break;
      
      case "disconnect":
         try
         {
            if (ipcon.GetConnectionState() == IPConnection.CONNECTION_STATE_CONNECTED)
            {
               ipcon.Disconnect();     // TinkerForge IPConnection.Disconnect
               try
               {
                  listView1.Invoke(new Action(() => listView1.Items.Clear()));
                  // An dieser Stelle leert das Programm auch wie gewünscht die Liste ab... 
               }
               catch (Exception ex)
               {
                  MessageBox.Show(ex.Message);
               }

               d_Logger.CreateLogFile("Disconnected from TinkerForge", "[3]");
            }
         }
         catch (Exception ex)
         {
            d_Logger.CreateLogFile(ex.Message, "[8]");
         }
         break;
   }
}
   //=============================================================================
   // Callback handles reconnection of IP Connection
private void ConnectedCB(IPConnection sender, short connectReason)
{
   switch (connectReason)
   {
      case IPConnection.CONNECT_REASON_REQUEST:
         MessageBox.Show("Connected by request");
         break;

      case IPConnection.CONNECT_REASON_AUTO_RECONNECT:
         MessageBox.Show("Auto-Reconnected");
            break;
   }

   try
   {
      listView1.Invoke(new Action(() => listView1.Items.Clear()));
      // Auch an dieser Stelle wird die ListView wie gewünscht abgelöscht
   }
   catch (Exception ex)
   {
      MessageBox.Show(ex.Message);
   }

   ipcon.Enumerate();
}
      //=============================================================================
private void DisconnectedCB(IPConnection sender, short disconnectReason)
{
   switch (disconnectReason)
   {
      case IPConnection.DISCONNECT_REASON_REQUEST:
         MessageBox.Show("Disconnected by request");
         break;
   
      case IPConnection.DISCONNECT_REASON_ERROR:
         MessageBox.Show("Disconnected by error");
         break;

      case IPConnection.DISCONNECT_REASON_SHUTDOWN:
         MessageBox.Show("Disconnected by shutdown");
         break;
   }

   try
   {
      listView1.Invoke(new Action(() => listView1.Items.Clear()));
      // sobald die listView1.Items.Clear() aber im DisconnectedCallback Event aufgerufen wird, hängt sich das Programm auf und reagiert nicht mehr.
     // Es wird auch keine Exception aufgeworfen.
     // Auch, wenn ich hier eine neue Methode aufrufe, in welcher dann die ListView abgelöscht werden soll, zeigt sich das gleiche Verhalten.
     // Code direkt vor dem Aufruf von listView1.Items.Clear() wird hier auch noch ausgeführt. (Testweise war eine MsgBox ausgabe enthalten.

   }
   catch (Exception ex)
   {
      MessageBox.Show(ex.Message);
   }
}

//=============================================================================
private void EnumerateCB(IPConnection sender, string UID, string connectedUID,
                    char position, short[] hardwareVersion,
                    short[] firmwareVersion, int deviceIdentifier,
                    short enumerationType)
{
   if (enumerationType == IPConnection.ENUMERATION_TYPE_CONNECTED ||
                enumerationType == IPConnection.ENUMERATION_TYPE_AVAILABLE)
   {
      //Baue ListenItems auf
      string[] myItems = new string[6];

      myItems[0] = deviceIdentifier.ToString();
      myItems[1] = UID;
      myItems[2] = connectedUID;
      myItems[3] = position.ToString().ToUpper();
      myItems[4] = firmwareVersion[0] + "." + firmwareVersion[1] + "." + firmwareVersion[2];
      myItems[5] = hardwareVersion[0] + "." + hardwareVersion[1] + "." + hardwareVersion[2];

      // Neues ListView Item initiieren
      ListViewItem LVItem = new ListViewItem(myItems);

      try
      {
         listView1.Invoke(new Action(() => listView1.Items.Add(LVItem)));
      }
      catch (Exception exceptionMsg)
      {
         MessageBox.Show(exceptionMsg.Message, "Fehler!", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error);
      }
   }
}

Link zu diesem Kommentar
Share on other sites

das ist es ja...

im Visual Studio habe ich nichts wirklich erkennen können... werde das aber heute Abend / morgen Früh noch einmal versuchen.

 

In den Tinkerforge Bindings ist ja ein mdb file für debug meldungen mit vorhanden... aber ehrlich gesagt, weiß ich nicht, wie ich damit umgehen sollte um ggf. die Bindings mit zu debuggen.

 

Das Verhalten ist aber definitiv reproduzierbar, sobald der Code im DisconnectCB aufgerufen wird...

 

Könnte es an den bindings liegen?

Wie kann ich diese ggf. mit ins debug bekommen?

 

(direkt die CS files einbinden?)

Link zu diesem Kommentar
Share on other sites

Hi CChris,

 

ich bin mir zwar nicht sicher, aber könnte es am UIThread liegen?

 

Ich weiß nicht wie gut du dich mit Mutli-Threading auskennst, aber wenn du eine WPF/UWP Anwendung schreibst musst du die Manipulation von den UI-Elementen auf dem UIThread machen, wenn jetzt ein anderer Thread auf irgendwelche Buttons, Grids, ListViews, etc. zugreifen möchte wird ihm das verweigert, weil nur der UIThread auf das Window zugreifen draf. Jetzt hast du nicht ein neuen Thread erzeugt, ich könnte mir aber Vorstellen, dass die C# Bindings im Hintergund in einem eigenen Thread laufen, da du im Callback auf das UI-Element zugreifst wird der Callback vermutlich auch von eben diesem Thread ausgeführt, der hat jedoch kein Zugriff auf die UI, woraufhin er eine Exception wirft, die wiederum siehst du in VS auch nicht, da sie in einem anderen Thread geworfen wird, das Programm hält damit an.

 

Abhilfe kannst du dir verschaffen indem du auf den Dispatcher der ListView zugreifst.(Über den Dispatcher kannst du dem UIThread einen Delegate übergeben, in dem du die ListView bearbeiten kannst)

 

listView1.Dispatcher.Invoke( () => listView1.Items.Clear() );

 

Müsste hoffentlich so passen.

Wie gesagt ich weiß nicht ob es daran liegt und ich kenne mit leider mit der Architektur von .Net nicht so tief aus, dass ich jetzt mit Sicherheit sagen kann es passt alles so wie ich es erkläre, aber auf mich wirkt es wie ein Thread Problem, kann aber natürlich auch an was anderem liegen;)

Link zu diesem Kommentar
Share on other sites

OK. Ich habe jetzt nochmal in die Debug-Ausgabe von Visual Studio geschaut.

Folgende Exceptions werden ausgegeben, sobald ipcon.Disconnect() aufgerufen wird...

 

Allerdings habe ich mein Ziel nun auf anderem Wege erreicht - und ich glaube, dieser ist sogar die bessere Wahl :)

 

Danke jedenfalls für deinen Input... vielleicht ist diese Info aber dennoch relevant und hilfreich für jmd. anderen ?

 

---

Da in der zwischenzeit noch die Antwort von Marvin dazu kam, werde ich den Vorschlag noch aus testen, um zu sehen, ob es daran lag oder nicht :)

 

Vielen Dank auch hier :)

Link zu diesem Kommentar
Share on other sites

Hi CChris,

 

listView1.Dispatcher.Invoke( () => listView1.Items.Clear() );

 

Müsste hoffentlich so passen.

 

 

Hi, Danke nochmal für deinen Vorschlag.

Leider steht "Dispatcher" nicht zur Auswahl für das ListView Element zur Verfügung.

 

Das "invoke" dient schon dem Thread-Übergreifenden Zugriff auf das Steuerelement ListView.

Sonst hätte er hier direkt eine Exception geworfen.

Link zu diesem Kommentar
Share on other sites

ja das hat mich sowieso schon gewundert, darf ich fragen was du verwendest(Wpf/UWP/Winforms), denn in der WPF Doku gibt es in der ListView Klasse keine Invoke Methode und IntelliSense zeigt mir da auch nichts an? Invoke kann ich doch nur über den Dispatcher aufrufen?

 

https://docs.microsoft.com/de-de/dotnet/api/system.windows.controls.listview?view=netframework-4.7.1

Link zu diesem Kommentar
Share on other sites

ja das hat mich sowieso schon gewundert, darf ich fragen was du verwendest(Wpf/UWP/Winforms), denn in der WPF Doku gibt es in der ListView Klasse keine Invoke Methode und IntelliSense zeigt mir da auch nichts an? Invoke kann ich doch nur über den Dispatcher aufrufen?

 

https://docs.microsoft.com/de-de/dotnet/api/system.windows.controls.listview?view=netframework-4.7.1

 

Ich hab gerade nochmal in der Doku nachgeschaut ich vermute mal du benutzt Winforms, denn dort gibt es die Invoke Methode :D

https://docs.microsoft.com/de-de/dotnet/api/system.windows.forms.listview?view=netframework-4.7.1

Dann war ich wohl auf der falschen Plattform ;D 

Link zu diesem Kommentar
Share on other sites

Sieht nach nem Deadlock aus. Eventuell liegts an dem Zusammenspiel von Invoke (der Threadwechsel von Thread X auf UI-Thread) und der Implementierung von Disconnect:

 

Aus den C# Bindings IpConnection.cs

/// <summary>
	///  Disconnects the TCP/IP connection from the Brick Daemon or the
	///  WIFI/Ethernet Extension.
	/// </summary>
	public void Disconnect()
	{
                        /* Der Übersicht halber rausgelöscht*/
		if (Thread.CurrentThread != localCallback.thread)
		{
			localCallback.thread.Join();
		}
	}

 

Meine Theorie wäre:

Durch den Callback sind wir im Moment des Aufrufs nicht auf dem UI-Thread. Das obere Snippet wartet auf die Beendigung dieses Threads. Gleichzeitig versuchen wir aber im Callback auf den UI-Thread zurückzuwechseln, was mMn nicht klappen kann, da dieser Thread durchs .Join() aus der Diconnect-Methode blockiert ist.

 

Kann sein, dass meine Theorie Müll ist, mit einem BeginInvoke()-Call statt des Invoke()-Calls sollte es aber funktionieren.

 

Mich würde noch interessieren, für was der Thread.Join() im Disconnect überhaupt drin ist.

 

Grüße,

 

oro

 

edit: Hier findest ne Erklärung zum Unterschied von BeginInvoke/Invoke:

 

https://stackoverflow.com/questions/229554/whats-the-difference-between-invoke-and-begininvoke

Link zu diesem Kommentar
Share on other sites

Sieht nach nem Deadlock aus. Eventuell liegts an dem Zusammenspiel von Invoke (der Threadwechsel von Thread X auf UI-Thread) und der Implementierung von Disconnect:

 

Aus den C# Bindings IpConnection.cs

/// <summary>
	///  Disconnects the TCP/IP connection from the Brick Daemon or the
	///  WIFI/Ethernet Extension.
	/// </summary>
	public void Disconnect()
	{
                        /* Der Übersicht halber rausgelöscht*/
		if (Thread.CurrentThread != localCallback.thread)
		{
			localCallback.thread.Join();
		}
	}

 

Danke - auch für den Ausschnitt aus dem IPConnection!

Werde mir deine Vorschläge - und auch die anderen Callbacks mal genauer anschauen...

 

Meine Theorie wäre:

Durch den Callback sind wir im Moment des Aufrufs nicht auf dem UI-Thread. Das obere Snippet wartet auf die Beendigung dieses Threads. Gleichzeitig versuchen wir aber im Callback auf den UI-Thread zurückzuwechseln, was mMn nicht klappen kann, da dieser Thread durchs .Join() aus der Diconnect-Methode blockiert ist.

 

Kann sein, dass meine Theorie Müll ist, mit einem BeginInvoke()-Call statt des Invoke()-Calls sollte es aber funktionieren.

Wie gesagt, werde es mal probieren, mit dem BeginInvoke() ...

 

Mich würde noch interessieren, für was der Thread.Join() im Disconnect überhaupt drin ist.

 

:)

 

 

Link zu diesem Kommentar
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gast
Reply to this topic...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

×
×
  • Neu erstellen...