CChris Geschrieben March 13, 2018 at 22:31 Geschrieben March 13, 2018 at 22:31 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); } } } Zitieren
photron Geschrieben March 14, 2018 at 18:56 Geschrieben March 14, 2018 at 18:56 Hast du mir mal im Debugger angeschaut wo das Programm dann genau hängt? Zitieren
CChris Geschrieben March 14, 2018 at 19:00 Autor Geschrieben March 14, 2018 at 19:00 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?) Zitieren
Marvin Schöller Geschrieben March 14, 2018 at 20:33 Geschrieben March 14, 2018 at 20:33 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;) Zitieren
CChris Geschrieben March 14, 2018 at 21:00 Autor Geschrieben March 14, 2018 at 21:00 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 Zitieren
CChris Geschrieben March 14, 2018 at 21:05 Autor Geschrieben March 14, 2018 at 21:05 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. Zitieren
Marvin Schöller Geschrieben March 14, 2018 at 21:26 Geschrieben March 14, 2018 at 21:26 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 Zitieren
Marvin Schöller Geschrieben March 14, 2018 at 21:30 Geschrieben March 14, 2018 at 21:30 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 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 Zitieren
oro Geschrieben March 15, 2018 at 12:56 Geschrieben March 15, 2018 at 12:56 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 Zitieren
CChris Geschrieben March 15, 2018 at 14:55 Autor Geschrieben March 15, 2018 at 14:55 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. Zitieren
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.