Entwickler-Ecke
IO, XML und Registry - Threadübergreifendervorgang - Comport lesen und ausgeben
oneitis - Di 16.03.10 15:11
Titel: Threadübergreifendervorgang - Comport lesen und ausgeben
Hallo liebe c-sharp community... das forum hier hat mir durchs lesen schon sehr viel geholfen :) deswegen bin ich überhaupt so weit gekommen :D
Folgendes Programm A schickt über Com1 (der mit Com2 verbunden ist) den String "Testline" und Programm B soll diesen auf Com 2 empfangen.
Programm A funktioniert einwandfrei und sendet auf knopfdruck den String.
Programm B öffnet den Comport und wenn ich auf meinen readButton drücke schreibt mir das programm mit serialport1.readexisting() auch den String in meine Textbox. Jetzt wollt ich nur nicht jedes mal den Knopf drücken müssen um die Empfangenen Daten in die Textbox zu bringen.
Also habe ich (glaub ich eh von hier im forum abgeschaut) folgendes geschrieben:
C#-Quelltext
1: 2: 3: 4:
| void SerialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { textBox1.AppendText(serialPort1.ReadExisting()); } |
Aber so bald er Datenempfängt, kommt die Fehlermeldung "Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement textBox1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde."
Ich bin noch nicht wirklich lange bei csharp (oh wunder sonst würd ich ja nicht so ein topic eröffnen) und ja ich habe die suchfunktion und google verwendet.
Wenn ich mich nicht irre muss ich Delegate oder invoke dafür verwenden, doch damit hab ich onch nicht gearbeitet und ich habs versucht zu verstehen wie Delegate funktioniert aber so einfach war das nicht :D da hat mir selbst mein schlaues buch nicht viel geholfen beim verstehen...
Hier der ganze code:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54:
| 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;
namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); }
private void button3_Click(object sender, EventArgs e) { textBox1.Clear(); }
private void openb_Click(object sender, EventArgs e) {
serialPort1.PortName = "COM2"; serialPort1.BaudRate = 9600; serialPort1.DataBits = 8; serialPort1.Parity = Parity.None; serialPort1.StopBits = StopBits.One; serialPort1.ReadTimeout = 9000;
serialPort1.Open(); if (serialPort1.IsOpen) { textBox1.AppendText(serialPort1.PortName + " wurde geöffnet!\r\n"); } else { textBox1.AppendText(serialPort1.PortName + " konnte nicht geöffnet werden!\r\n"); }
serialPort1.DataReceived += new SerialDataReceivedEventHandler(SerialPort1_DataReceived); }
void SerialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { textBox1.AppendText(serialPort1.ReadExisting()); }
private void closeb_Click(object sender, EventArgs e) { serialPort1.Close(); if (!serialPort1.IsOpen) { textBox1.AppendText(serialPort1.PortName + " wurde geschlossen!\r\n"); } } } } |
Normalerweise poste ich sowas nicht, weil ich theorethisch mit genug übung es selbst schaffen könnte, aber im moment hab ich keinen kopf dafür und ich wäre sehr erfreut wenn mir wer helfen könnte ^^
lg Kurt
danielf - Di 16.03.10 15:33
Hi,
da hast du vollkommen recht.
Um ein Delegate aufzurufen musst du es zuerst deklarieren. Das tust du so:
C#-Quelltext
1:
| private delegate void <name>Delegate(<params>) |
Im Code überprüfst du dann, ob ein Invoke für das Control benötigt wird.
C#-Quelltext
1:
| control.InvokeRequired |
Falls dies der Fall ist erstellst du ein Delegate von dem Typ wie du es oben angelegt hast:
C#-Quelltext
1:
| Delegate d = new <name>Delegate(<params>); |
und rufst es über die Invoke Methode des Controls auf (ansonsten normaler Zugriff)
C#-Quelltext
1:
| control.Invoke(d, <params>); |
Kann dann bei dir so aussehen:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| void SerialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { AppendText(textBox1, serialPort1.ReadExisting()); }
private delegate void AppendTextDelegate(TextBox textBox, string text);
private void AppendText(TextBox textBox, string text) { if (textBox.InvokeRequired) { textBox.Invoke(new AppendTextDelegate(AppendText), textBox, text); } else { textBox.AppendText(text); } } |
oneitis - Di 16.03.10 17:47
Danke schön, jetzt funktionierts, ich habs zwar noch nicht ganz genau verstanden, aber ich hab gerade ein aha erlebnis gehabt :)
werd das ganze jetzt auseinander nehmen und so lang herum probieren bis ichs check ;)
danke noch mal :)
oneitis - Mo 22.03.10 14:07
Ich habs echt versucht und auch teilweise verstanden, aber es klappt einfach nicht so wie ich will... hat jemand vielleicht einen link der delegate und die inwoke funktion genau erklärt?
mein problem is, dass ich ne eingabeaufforderung hab und auch result und eine variable brauche von der eingabe...
und diese dann in der hauptform in textfeld ausgegeben wird...
also hat wer nen link zum lernen? :)
ps: ja ich weiß man soll es nicht aber daweil hab ich das problem mit CheckForIllegalCrossThreadCalls = false; gelöst ^^
danielf - Mo 22.03.10 15:12
Das Gerüst habe ich dir ja schon aufgezeigt. Was hast du daraus gemacht? Wenn du uns deinen Code schickst, können wir dir bestimmt weiterhelfen.
oneitis - Mo 22.03.10 16:01
Habs versucht aber klappt ned, morgen wird warscheinlich eh ein kollege vorbei kommen und es mir erklären :)
hier mal mein ganzer code vom versuch...(ab)zeile 132 und zeile 92
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243:
| 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.IO; using System.Data.SqlClient;
namespace WindowsFormsApplication1 { public partial class Form1 : Form { public string compuff; public string produktEingabe; public Form1() { InitializeComponent(); string[] ports = SerialPort.GetPortNames(); combox.DataSource = ports; }
private void button3_Click(object sender, EventArgs e) { textBox1.Clear(); }
private void openb_Click(object sender, EventArgs e) {
serialPort1.PortName = combox.SelectedValue.ToString() ; serialPort1.BaudRate = 9600; serialPort1.DataBits = 8; serialPort1.Parity = Parity.None; serialPort1.StopBits = StopBits.One; serialPort1.ReadTimeout = 9000;
try { serialPort1.Open(); } catch (IOException fehler) { MessageBox.Show(fehler.Message); } catch (UnauthorizedAccessException fehler2) { MessageBox.Show(fehler2.Message); } catch (Exception fehler3) { MessageBox.Show(fehler3.Message); }
if (serialPort1.IsOpen) { textBox1.AppendText(serialPort1.PortName + " wurde geöffnet!\r\n"); openb.Enabled = false; closeb.Enabled = true; combox.Enabled = false; } else { textBox1.AppendText(serialPort1.PortName + " konnte nicht geöffnet werden!\r\n"); }
serialPort1.DataReceived += new SerialDataReceivedEventHandler(SerialPort1_DataReceived); }
void SerialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { compuff = serialPort1.ReadExisting(); AppendText(textBox1, compuff + "\r\n"); try { SqlConnectionStringBuilder sqlcon = new SqlConnectionStringBuilder(); sqlcon.DataSource = @"LAPTOP\SQLEXPRESS"; sqlcon.InitialCatalog = "readcom"; sqlcon.IntegratedSecurity = true; SqlConnection connect = new SqlConnection(sqlcon.ConnectionString); connect.Open(); if (connect.State == ConnectionState.Closed) { AppendText(textBox1, "Verbindung wurde nicht hergestellt!\r\n"); }
string check = "SELECT COUNT(*) " + "FROM readcomprodukte WHERE ID = '" + compuff + "'";
SqlCommand cmd = new SqlCommand(check, connect); int vorhanden = Convert.ToInt32(cmd.ExecuteScalar()); if (vorhanden == 0) { InputText(compuff); AppendText(textBox1, "Produkteingabe: " + produktEingabe); }
if (vorhanden == 1) { try { string read = "SELECT ID, Produkte " + "FROM readcomprodukte WHERE ID = '" + compuff + "'";
SqlCommand cmd4 = new SqlCommand(read, connect); SqlDataReader dr = cmd4.ExecuteReader(); while (dr.Read()) AppendText(textBox2,((string)dr["Produkte"])+"\r\n");
dr.Close(); connect.Close();
} catch (Exception sqlfehler) { MessageBox.Show(sqlfehler.Message); } }
if (vorhanden > 1) { AppendText(textBox1, "ID mehrfach vorhanden!"); } } catch (Exception sqlfehler) { MessageBox.Show(sqlfehler.Message); }
}
private delegate void AppendTextDelegate(TextBox textBox, string text); private delegate string InputTextDelegate(string compuff); private string InputText(string compuff) { inputdialog input = new inputdialog(compuff); input.Invoke(new InputTextDelegate(InputText), compuff);
if (input.InvokeRequired) { input.Invoke(new InputTextDelegate(InputText), compuff); }
return input.produktEingabe; }
private void AppendText(TextBox textBox, string text) { if (textBox.InvokeRequired) { textBox.Invoke(new AppendTextDelegate(AppendText), textBox, text); }
else { textBox.AppendText(text);
} }
private void closeb_Click(object sender, EventArgs e) { serialPort1.Close(); if (!serialPort1.IsOpen) { textBox1.AppendText(serialPort1.PortName + " wurde geschlossen!\r\n"); closeb.Enabled = false; openb.Enabled = true; combox.Enabled = true; } }
private void saveb_Click(object sender, EventArgs e) { saveFileDialog1.InitialDirectory = @"c:\"; saveFileDialog1.Filter = "Textdateien (*.txt)|*.txt|Alle Dateien (*.*)|*.*"; DialogResult erg = saveFileDialog1.ShowDialog();
if (erg == DialogResult.OK) { StreamWriter writer = new StreamWriter(saveFileDialog1.FileName, true); writer.WriteLine(textBox2.Text); writer.Close(); } }
private void sendB_Click(object sender, EventArgs e) { try { SqlConnectionStringBuilder sqlcon = new SqlConnectionStringBuilder(); sqlcon.DataSource = @"LAPTOP\SQLEXPRESS"; sqlcon.InitialCatalog = "readcom"; sqlcon.IntegratedSecurity = true; SqlConnection connect = new SqlConnection(sqlcon.ConnectionString); connect.Open(); if (connect.State == ConnectionState.Open) { textBox1.AppendText("Sql verbindung hergestellt!"); } else { textBox1.AppendText("Verbindung wurde nicht hergestellt!"); }
string read = "SELECT ID, Produkte " + "FROM readcomprodukte";
SqlCommand cmd = new SqlCommand(read, connect); SqlDataReader dr = cmd.ExecuteReader(); while (dr.Read()) AppendText(textBox2, ("\r\n" + (string)dr["ID"] + " " + (string)dr["Produkte"])); dr.Close(); connect.Close();
} catch (Exception sqlfehler) { MessageBox.Show(sqlfehler.Message); }
}
private void button1_Click(object sender, EventArgs e) { try { SqlConnectionStringBuilder sqlcon = new SqlConnectionStringBuilder(); sqlcon.DataSource = @"LAPTOP\SQLEXPRESS"; sqlcon.InitialCatalog = "readcom"; sqlcon.IntegratedSecurity = true; SqlConnection connect = new SqlConnection(sqlcon.ConnectionString); connect.Open(); if (connect.State == ConnectionState.Open) { textBox2.AppendText("Sql verbindung hergestellt!"); } else { textBox2.AppendText("Verbindung wurde nicht hergestellt!"); }
string read = "DELETE FROM readcomprodukte " + "WHERE ID='9001521000242'";
SqlCommand cmd = new SqlCommand(read, connect); cmd.ExecuteNonQuery(); connect.Close();
} catch (Exception sqlfehler) { MessageBox.Show(sqlfehler.Message); } } } } |
danielf - Mo 22.03.10 18:01
In zeile 117 überprüfst du, ob ein Invoke benötigt wird, aber in Zeile 116 führst du schon eins aus :)
Ausserdem rufst du ja in zeile 116 auch wieder die gleiche Methode auf.. und landest somit in einer Endlosschleife --> Stack overflow!
Bitte schau dir mein gerüst nochmal an, ich bin mir sicher wenn du dich ziemlich streng daran hälst bekommst du es hin.
Hier [
http://www.csharper.net/blog/methodinvoker___anonymous_methods___teh_r0x0r.aspx] findest du auch noch ein buntes Beispiel.
Gruß
oneitis - Do 01.04.10 10:01
Hey leute, wollte mich nur noch mal bedanken, habe jetzt endlich zeit gefunden mich hinzusetzen und mir die links anzuschauen und mich einzuarbeiten :)
und man siehe da... ein wunder :)
hier meine lösung, vielleicht noch etwas unelegant weil ich dialogresult nicht direkt verwende sondern den umweg über meine public bool InputResultOK gehe aber es erfüllt seinen zweck :)
falls ihr noch tipps habt immer her damit ;)
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| private delegate void func(string compuff); private void inputdialogmethode(string compuff) { inputdialog input = new inputdialog(compuff); input.ShowDialog(this); produktEingabe = input.produktEingabe; if (input.DialogResult == DialogResult.OK) { InputResultOk = true; } if (input.DialogResult != DialogResult.OK) { InputResultOk = false; }
} |
C#-Quelltext
1: 2:
| func inputdialogdel = new func(inputdialogmethode); Invoke(inputdialogdel,compuff); |
noch mals danke, super forum hier :)
Kha - Do 01.04.10 21:33
Ich nehme mal an, dass die SQL-Abfrage nicht absichtlich im Nebenthread ausgeführt wird, warum also nicht in DataReceived sofort invoken und danach gemütlich im Hauptthread weiterprogrammieren ;) :?:
oneitis - Fr 09.04.10 09:04
weil ich noch keine ahnung habe vom richtigen(effizienten) programmieren ;)
Könnte ich es also so lösen: So bald die Daten vom Comport in die Variable geschrieben werden, wird ein Event ausgelöst. Mit diesen Event würde ich dann wiederum die Sql-Abfrage im Hauptthread starten. <-?
ps: vorteil? nachteil? keine ahnung wäre aber echt begeistert wenn mir wer den sinn dahinter erklärt... is sicher basic vom programmieren ^^
Kha - Fr 09.04.10 13:50
oneitis hat folgendes geschrieben : |
Könnte ich es also so lösen: So bald die Daten vom Comport in die Variable geschrieben werden, wird ein Event ausgelöst. Mit diesen Event würde ich dann wiederum die Sql-Abfrage im Hauptthread starten. <-? |
Wenn du irgendwann Logik strikt von der Ausgabe trennst und in eine andere Klasse verfrachtest, würden sich Events anbieten, ja. Aber danach sieht es bis jetzt eher nicht aus ;) . Ich meinte lediglich etwas in dieser Richtung:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| SerialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { Invoke(new Action(SynchronousDataReceived)); }
void SynchronousDataReceived() { } |
oneitis hat folgendes geschrieben : |
ps: vorteil? nachteil? |
Die Einfachheit dürfte offensichtlich sein :) . Wenn du längere Berechnungen durchführst, wären die in einem Nebenthread schon gut aufgehoben, um die UI nicht zu blockieren, aber wie gesagt: danach sah es mir nicht aus.
oneitis - Fr 09.04.10 14:50
Verdammt... also wenn das jetzt so funktioniert wie ich mir das vorstelle, dann hätte ich mir einiges ersparen können... (wobei ich sagen muss, sonst hätte ich nie so "viel" dazu gelernt)
Muss ich ausprobieren so bald ich wieder dazu komme, schaut sehr gut aus...
ps: danke für die erklärung, war echt noch nie in einem forum, wo einem wirklich so gut geholfen wurde... bzw. so konstruktive beiträge gepostet wurden :)
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!