2009-09-04 13 views
5

Powiel możliwe:
Getting Cross-thread operation not valid
Cross-thread operation not validoperacja Cross wątek nie ważne podczas słuchania do portu COM

Staram się słuchać do portu COM, tak aby utworzyć nowy moduł obsługi dla zdarzenia SerialPort.DataReceived. Logika jest prosta - piszę coś do TextBox1, wciskam Button1, a mój tekst powinien pokazywać się w Label1. Ale moja aplikacja nie chce uruchomić, ponieważ spowoduje błąd "Niepoprawna operacja wątku". Zrobiłem kilka wyszukiwania i znalazłem obiekt Invoke - w jaki sposób mogę go użyć w moim przykładzie? Dlaczego muszę uwzględnić logikę 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: Port COM to interfejs szeregowy RS232. Pomimo USB i wszystkich wciąż istnieje wiele urządzeń (np. GPS, Medical), które wykorzystują port szeregowy do komunikacji z komputerem. – Sesh

+0

@_simon_: Po prostu ciekawy: jaki port COM jest używany w tej konkretnej aplikacji? –

+0

@_simon_: Zaktualizowałem moją odpowiedź –

Odpowiedz

17

Domyślam się, że MyDataReceivedHandler jest uruchomiony na innym wątku niż GUI. Aby to naprawić, musisz wywołać setery Text we właściwym wątku. Jest to próbka tego:

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

Jeśli używasz .NET Framework 2.0, powyższy Action<T1, T2> delegat nie jest dostępny, więc trzeba będzie zdefiniować własny jedno:

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

SetControlText sposób można skrócić (a nawet statyczne) tak (ten działa zarówno w wersji 2.0 i 3.5):

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

Wtedy nie trzeba zrobić kontrolę InvokeRequired za każdym razem, ale z drugiej strony zawiniesz połączenie w delegata, nawet jeśli nie jest potrzebne. Myślę, że w takiej GUI jakakolwiek różnica w wydajności pomiędzy tymi dwoma jest pomijana, więc używam krótszej formy, po prostu dlatego, że jest mniej kodu do napisania.

+0

Wygląda na to, że działa to tylko w wersji 3.5. Używam Visual Studio 2005, teraz zainstalowałem 3.5 SP1. Gdzie w Visual Studio 2005 mogę ustawić, z której platformy .NET korzystam? – sventevit

+0

@_simon_: Zaktualizowałem odpowiedź w wersjach zgodnych z 2.0 –

+0

Uwaga: Jeśli operacja wykonywana przez delegata jest długa, nadal może blokować interfejs użytkownika, ponieważ wywołanie spowoduje tylko wątek interfejsu użytkownika w celu przetworzenia operacji.Użycie BeginInvoke na wszystkich kontrolkach, które go implementują, spowoduje wykonanie operacji asynchronicznie, bez blokowania. –

0

Można również wykonać następujące czynności, gdy kontrola dostępu do UI z innego wątku niż ten, który został stworzony na:

(.NET 3.5)

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

lub (.NET 2.0)

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

edit> Czasami za pomocą Invoke dłuższego działania uruchomionego może/będzie nadal wiszą ui korzystając BeginInvoke oczywiście dokonuje tej operacji asynchronicznie, d ui się nie zawiesi.

Powiązane problemy