2009-09-04 17 views
5

Duplicar posibles:
Getting Cross-thread operation not valid
Cross-thread operation not validoperación de la Cruz-hilo no válida mientras se escucha a un puerto COM

Estoy tratando de escuchar en el puerto COM para que se crea nuevo manejador para el evento SerialPort.DataReceived. La lógica es simple: escribo algo en TextBox1, presiono Button1 y mi texto debe mostrarse en Label1. Pero mi aplicación no quiere ejecutarse, porque arroja el error "Operación de hilo cruzado no válido". Realicé una búsqueda y encontré el objeto Invocar, ¿cómo puedo usarlo en mi ejemplo? ¿Por qué necesito incluir la lógica de Invocar?

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: puerto COM aquí es la interfaz serie RS232. A pesar del USB y de todo, todavía hay una serie de dispositivos (por ejemplo, GPS, médicos) que utilizan el puerto serie para la comunicación de PC. – Sesh

+0

@_simon_: Simplemente curioso: ¿para qué sirve el puerto COM en esta aplicación en particular? –

+0

@_simon_: Actualicé mi respuesta –

Respuesta

17

Mi conjetura es que MyDataReceivedHandler se ejecuta en un hilo diferente a la interfaz gráfica de usuario. Para arreglar eso, necesita invocar los setters Text en el hilo correcto. Esta es una muestra de ello:

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

Si está utilizando .NET Framework 2.0, el delegado Action<T1, T2> arriba no está disponible, por lo que tendrá que definir su propio:

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

El SetControlText método puede hacerse más corto (e incluso estática) como esto (esto funciona en ambos 2.0 y 3.5):

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

entonces usted no tiene que hacer la comprobación de InvokeRequired cada vez, pero, por otro lado, envolverá la llamada en un delegado, incluso si no es necesario. Creo que en un método GUI como este, cualquier diferencia de rendimiento entre esos dos es negligente, así que tiendo a usar la forma más corta, simplemente porque es menos código para escribir.

+0

Parece que esto solo funciona en 3.5. Uso Visual Studio 2005, ahora instalé 3.5 SP1. ¿En qué lugar de Visual Studio 2005 puedo establecer qué .NET framework estoy usando? – sventevit

+0

@_simon_: He actualizado la respuesta con las versiones compatibles de 2.012 –

+0

Nota: Si la operación realizada en el delegado es larga, aún puede bloquear la UI ya que la invocación solo hará que el subproceso de la UI procese la operación.El uso de BeginInvoke en todos los controles que lo implementan realizará la operación de forma asíncrona, sin bloqueo. –

0

También puede hacer lo siguiente cada vez que se accede a un control de interfaz de usuario de un hilo diferente a la que se creó en:

(.NET 3,5)

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

o (.NET 2.0)

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

edición> a veces utilizando la invocación de una operación de larga ejecución puede/todavía se colgará la interfaz de usuario, utilizando BeginInvoke obviamente lleva a cabo esa operación asíncrona, una d el ui no se colgará.

Cuestiones relacionadas