2009-09-04 2 views
5

Mögliche Duplizieren:
Getting Cross-thread operation not valid
Cross-thread operation not validCross-Thread-Betrieb nicht gültig, während an einen COM-Port lauscht

Ich versuche, COM-Port zu hören, so dass ich neue Handler erstellen für SerialPort.DataReceived-Ereignis. Die Logik ist einfach - ich schreibe etwas in TextBox1, drücke Button1 und mein Text sollte sich selbst in Label1 zeigen. Aber meine Anwendung möchte nicht ausgeführt werden, weil sie den Fehler 'Cross thread operation not valid' verursacht. Ich habe etwas gesucht und Invoke Objekt gefunden - wie kann ich es in meinem Beispiel verwenden? Warum muss ich Invoke Logik hinzufügen?

namespace WindowsApplication1 
{ 
public partial class Form1 : Form 
{ 
    SerialPort sp = new SerialPort(); 

    public Form1() 
    { 
     InitializeComponent(); 
     sp.DataReceived += MyDataReceivedHandler; 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 

    } 

    private void MyDataReceivedHandler(object sender, SerialDataReceivedEventArgs e) 
    { 
     try 
     { 
      //sp.PortName = "COM3"; 
      //sp.Open(); 
      Label1.Text = sp.ReadLine(); 
     } 
     catch (Exception exception) 
     { 
      RichTextBox1.Text = exception.Message + "\n\n" + exception.Data; 
     } 
     finally 
     { 
      sp.Close(); 
     } 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     try 
     { 
      sp.PortName = "COM3"; 
      sp.Open(); 
      sp.WriteLine(TextBox1.Text); 
     } 
     catch (Exception exception) 
     { 
      RichTextBox1.Text = exception.Message + "\n\n" + exception.Data; 
     } 
     finally 
     { 
      sp.Close(); 
     } 
    } 
} 

}

+0

@ Peter: COM-Port ist hier die serielle RS232-Schnittstelle. Trotz USB und allem gibt es immer noch eine Reihe von Geräten (z. B. GPS, Medical), die den seriellen Anschluss für PC-Kommunikation verwenden. – Sesh

+0

@_simon_: Nur neugierig: Was ist der COM-Port für diese spezielle Anwendung verwendet? –

+0

@_simon_: Ich habe meine Antwort aktualisiert –

Antwort

17

Meine Vermutung ist, dass MyDataReceivedHandler als die GUI auf einem anderen Thread ausgeführt wird. Um das zu beheben, müssen Sie die Text Setter auf den richtigen Thread aufrufen. Dies ist ein Beispiel so tun:

public void SetControlText(Control control, string text) 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke(new Action<Control,string>(SetControlText), new object[] { control, text }); 
    } 
    else 
    { 
     control.Text = text; 
    } 
} 

private void MyDataReceivedHandler(object sender, SerialDataReceivedEventArgs e) 
{ 
    try 
    { 
     //sp.PortName = "COM3"; 
     //sp.Open(); 
     SetControlText(Label1, sp.ReadLine()); 
    } 
    catch (Exception exception) 
    { 
     SetControlText(RichTextBox1, exception.Message + "\n\n" + exception.Data); 
    } 
    finally 
    { 
     sp.Close(); 
    } 
} 

Wenn Sie .NET Framework 2.0 verwenden, die oben Action<T1, T2> Delegat nicht verfügbar ist, so dass Sie Ihre eigenen definieren müssen:

private delegate void SetControlTextHandler(Control control, string text); 

public void SetControlText(Control control, string text) 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke(new SetControlTextHandler(SetControlText), new object[] { control, text }); 
    } 
    else 
    { 
     control.Text = text; 
    } 
} 

Die SetControlText Methode kann kürzer (und sogar statisch) gemacht werden, wie dies (das funktioniert sowohl in 2.0 und 3.5):

public static void SetControlText(Control control, string text) 
{ 
    ´control.Invoke((MethodInvoker)delegate { control.Text = text; }); 
} 

Dann müssen Sie die Kontrolle vonnicht tunjedes Mal, aber andererseits wickeln Sie den Anruf in einen Delegaten, auch wenn es nicht benötigt wird. Ich denke, dass in einer GUI-Methode wie dieser jeder Leistungsunterschied zwischen diesen beiden vernachlässigbar ist, daher neige ich dazu, die kürzere Form zu verwenden, einfach weil es weniger Code zum Schreiben gibt.

+0

Es scheint, dass dies nur in 3.5 funktioniert. Ich benutze Visual Studio 2005, jetzt habe ich 3.5 SP1 installiert. Wo kann ich in Visual Studio 2005 einstellen, welches .NET Framework ich verwende? – sventevit

+0

@_simon_: Ich habe die Antwort mit 2.0 kompatiblen Versionen aktualisiert. –

+0

Hinweis: Wenn der im Delegaten ausgeführte Vorgang lange ausgeführt wird, kann er die Benutzeroberfläche weiterhin blockieren, da der Vorgang nur durch den UI-Thread ausgelöst wird.Wenn BeginInvoke für alle Steuerelemente verwendet wird, die es implementieren, wird die Operation asynchron ohne Blockierung ausgeführt. –

0

Sie können auch wie folgt vorgehen, wenn ein UI-Steuerelement von einem anderen Thread Zugriff auf als das es wurde erstellt am:

(.NET 3,5)

myControl.BeginInvoke(new MethodInvoker(() => myControl.whatever = whatever;)); 

oder (.NET 2.0)

myControl.BeginInvoke(new MethodInvoker(delegate { myControl.whatever = whatever;)); 

bearbeiten> Manchmal Invoke verwendet für einen langen laufenden Betrieb kann/wird noch die ui hängen, mit BeginInvoke führt offensichtlich, dass die Operation asynchron, ein d das UI wird nicht hängen.

Verwandte Themen