2009-09-04 10 views
5

Eventuali duplicati:
Getting Cross-thread operation not valid
Cross-thread operation not validoperazione cross-thread non valida durante l'ascolto di una porta COM

Sto cercando di ascoltare la porta COM in modo che creo nuovo gestore per evento SerialPort.DataReceived. La logica è semplice: scrivo qualcosa in TextBox1, premo Button1 e il mio testo dovrebbe mostrarlo in Label1. Ma la mia applicazione non vuole funzionare, perché genera l'errore "Cross thread operation not valid". Ho eseguito alcune ricerche e ho trovato l'oggetto Invoke: come posso utilizzarlo nel mio esempio? Perché devo includere la logica Invoke?

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: la porta COM qui è l'interfaccia seriale RS232. Nonostante USB e tutti ci sono ancora un certo numero di dispositivi (ad esempio GPS, medici) che utilizzano la porta seriale per la comunicazione PC. – Sesh

+0

@_simon_: curioso: qual è la porta COM utilizzata in questa particolare applicazione? –

+0

@_simon_: Ho aggiornato la mia risposta –

risposta

17

La mia ipotesi è che MyDataReceivedHandler è in esecuzione su un thread diverso da quello del GUI. Per risolvere ciò, è necessario richiamare i setter Text sul thread corretto. Questo è un esempio di farlo:

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(); 
    } 
} 

Se si utilizza .NET Framework 2.0, il sopra Action<T1, T2> delegato non è disponibile, in modo da avere a definire il proprio uno:

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; 
    } 
} 

Il SetControlText metodo può essere reso più breve (e anche statica) come questo (questo funziona in entrambi i 2.0 e 3.5):

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

Allora non c'è bisogno di fare il check di InvokeRequired ogni volta, ma d'altra parte avvolgere la chiamata in un delegato anche se non è necessario. Penso che in un metodo GUI come questo qualsiasi differenza di prestazioni tra questi due sia trascurabile, quindi tendo ad usare il modulo più breve, semplicemente perché è meno codice da scrivere.

+0

Sembra che funzioni solo in 3.5. Io uso Visual Studio 2005, ora ho installato 3.5 SP1. Dove in Visual Studio 2005 posso impostare quale framework .NET sto usando? – sventevit

+0

@_simon_: Ho aggiornato la risposta con le versioni compatibili 2.0 –

+0

Nota: se l'operazione eseguita nel delegato è in esecuzione a lungo termine, può comunque bloccare l'interfaccia utente poiché il richiamo causerà semplicemente il thread dell'interfaccia utente per l'elaborazione dell'operazione.L'utilizzo di BeginInvoke su tutti i controlli che lo implementano eseguirà l'operazione in modo asincrono, senza bloccare. –

0

È inoltre possibile effettuare le seguenti operazioni ogni volta che si accede a un controllo dell'interfaccia utente da un thread diverso da quello che è stato creato il:

(NET 3,5)

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

o (NET 2.0)

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

Modifica> a volte usando Invoke per un'operazione di corsa lunga può/sarà ancora appendere l'interfaccia utente, utilizzando BeginInvoke effettua, ovviamente, che il funzionamento in modo asincrono, di un d l'ui non si bloccherà.

Problemi correlati