2

So I am getting this cross thread error and I cannot figure it out. Here is my base code before I attempted to muck around with it.

Bascally what the code is going is calling a batch file which then calls a java file. The outputdata is then redirected to the console in real time. When I just redirect the output just to the C# console, it works fine. But I want the same info to print out into a rich text box within the app. VS 2010 complaines at rchsdtOut.Text = e.Data.ToString(); that Cross-thread operation not valid: Control 'rchsdtOut' accessed from a thread other than the thread it was created on.

I have tried looking this up, and I do admit I am new to threading, so any help on how to easy accomplish this would be appreciated.

   //Declare and instantiate a new process component.
    System.Diagnostics.Process process1;
    process1 = new System.Diagnostics.Process();
    process1.StartInfo.UseShellExecute = false;
    process1.StartInfo.RedirectStandardOutput = true;
    process1.StartInfo.CreateNoWindow = true;
    process1.StartInfo.FileName = "cmd.exe";
    process1.StartInfo.Arguments = "BATFile.bat";
    process1.OutputDataReceived += (s, a) => myMethod(a);
    process1.Start();
    process1.BeginOutputReadLine(); 
    process1.WaitForExit();
    process1.Close(); 


    private void myMethod(DataReceivedEventArgs e) {       
       if (e.Data != null)
        {         
           rchsdtOut.Text = e.Data.ToString();
           Console.WriteLine(e.Data.ToString());

        } 
    }//end of private
user1158745
  • 2,402
  • 9
  • 41
  • 60

4 Answers4

6

Try this line:

process1.OutputDataReceived += (s, a) => rchsdtOut.Invoke(new System.Action(()=> myMethod(a)));
Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • I added that line, but vs2010 indicates that cannot convert lambda expression type 'system.delegate' becuase it's not a delegate type. – user1158745 Jan 19 '12 at 18:13
5

It's not legal to access a WinForms control from a thread other than the one it was created on. You need to use Invoke or BeginInvoke to get control back to the appropriate thread.

private void myMethod(DataReceivedEventArgs e) {       
  if (e.Data != null) {
    Action action = () => rchstdOut.Text = e.Data.ToString();
    rchstdOut.Invoke(action, null);
    Console.WriteLine(e.Data.ToString());
  }
}
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • +1 This is a better solution than mine, because it waits to marshal until it is sure that the payload isn't null. – Chris Shain Jan 19 '12 at 17:39
  • @user1158745 that's really odd. Try using BeginInvoke instead of Invoke – JaredPar Jan 19 '12 at 18:23
  • Well the app doesn't appear to hang, but nothing is logged. – user1158745 Jan 19 '12 at 19:52
  • @user1158745 that likely means you aren't pumping your UI thread to let messages through. The problem appears to be the call to `WaitForExit`. That will block the UI thread and messages until the app completes. Try removing that call and it should start working as expected – JaredPar Jan 19 '12 at 19:53
  • Worked like a charm. Only issue, it doesn't seem to be appending to the richtextbox, but just overwriting it. anyideas? – user1158745 Jan 19 '12 at 20:08
  • This seemed to do is Action action = () => rchsdtOut.Text += "\r\n" + e.Data.ToString(); Thanks for your help. – user1158745 Jan 19 '12 at 20:10
0

Well rchsdtOut can only be upodated from the UI thread while your method is called from another. There are several solutions. If you want to have a general method to update controls you can check the control.InvokeRequired property (or this.Dispatcher.CheckAccess() in WPF) and use a delegate.

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

    private void UpdateTextControl(Control control, string text)
    {
        if (control.InvokeRequired)
        {
            Invoke(new UpdateTextControlDelegate(UpdateTextControl), new object[] { control, text});
            return;
        }

        control.Text = text;
    }

...

if (e.Data != null)
{         
     UpdateTextControl(rchsdtOut, e.Data.ToString());
     Console.WriteLine(e.Data.ToString());
} 
Strillo
  • 2,952
  • 13
  • 15
0

You cannot access a Form Control from a thread other then where it was created. You will need to create some other object that both of the threads can access. Producer Consumer Problem

Community
  • 1
  • 1