7

I have a winforms application, the issue has to do with threading. Since I am calling 'MyCustomCode() which creates a new thread, and calls the method 'SomeMethod()' which then accesses MessageBox.Show(...).

The problem has to do with threading, since the newly created thread is trying to access a control that was created on another thread.

I am getting the error:

Cross-thread operation not valid: Control 'TestForm' accessed from a thread other than the thread it was created on.

public TestForm()
{
    InitializeComponent();


    // custom code
    //
    MyCustomCode();


}

public void SomeMethod()
{

    // ***** This causes an error  ****

    MessageBox.Show(this,   
        ex.Message, 
        "Error", 
        MessageBoxButtons.OK, 
        MessageBoxIcon.Error
    );
}



private void InitializeAutoUpdater()
{
        // Seperate thread is spun to keep polling for updates
        ThreadStart ts = new ThreadStart(SomeMethod);
        pollThread = new Thread(ts);
        pollThread.Start();
}

Update

If you look at this example http://www.codeproject.com/KB/cs/vanillaupdaterblock.aspx, the method CheckAndUpdate is calling MessageBox.Show(..) that is what my problem is. I would have thought that code was good to go!

Funny thing is that this code was working just fine on Friday???

  • could it be because I installed .net 3.5? Is this a 3.5 'feature'? I doubt it but it is the only explanation! –  Sep 29 '08 at 16:35
  • Does this answer your question? [Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on](https://stackoverflow.com/questions/142003/cross-thread-operation-not-valid-control-accessed-from-a-thread-other-than-the) – Peter Duniho Jun 27 '21 at 16:04
  • Does this answer your question? [How do I update the GUI from another thread?](https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread) – Peter Duniho Jun 27 '21 at 16:05

9 Answers9

9

You cannot acces UI elements from multiple threads.

One way to solve this is to call the Invoke method of a control with a delegate to the function wich use the UI elements (like the message box). Somethin like:

public delegate void InvokeDelegate();

public void SomeMethod()
{

    button1.Invoke((InvokeDelegate)doUIStuff);


}


void doUIStuff()
{
           MessageBox.Show(this,   
                ex.Message, 
                "Error", 
                MessageBoxButtons.OK, 
                MessageBoxIcon.Error
            );
}
albertein
  • 26,396
  • 5
  • 54
  • 57
  • You should check if invoke is required, e.g. button1.InvokeRequired. – RickL Sep 29 '08 at 16:29
  • And if the handle of the form is not yet created, InvokeRequired always return false. That is why SynchronizationContext could be more recommended. – Romain Verdier Sep 29 '08 at 16:38
7

to avoid cross-thread exceptions (InvalidOperationException), here is the code pattern:

protected delegate void someGuiFunctionDelegate(int iParam);

protected void someGuiFunction(int iParam)
{
    if (this.InvokeRequired)
    {
        someGuiFunctionDelegate dlg = new 
            someGuiFunctionDelegate(this.someGuiFunction);
        this.Invoke(dlg, new object[] { iParam });
        return;
    }

    //do something with the GUI control here
}

i agree that this is annoying, but it is an artifact of the fact that windows GUI controls are not thread-safe. The exception can be turned off with a flag somewhere or other, but don't do that as it can lead to extremely hard to find bugs.

Steven A. Lowe
  • 60,273
  • 18
  • 132
  • 202
3

To keep things simple you can look into using the BackGroundWorker class. This class will provide a framework for to handle your threading and progress notification events. Your ui thread will handle the progress event and display the error message you pass back.

Aaron Fischer
  • 20,853
  • 18
  • 75
  • 116
2

OR

Romain Verdier
  • 12,833
  • 7
  • 57
  • 77
1

I know this is an older post, but I recently found an elegant solution to this problem using generics and extension methods. This is a combination of the authors works and some comments.

A Generic Method for Cross-thread Winforms Access

http://www.codeproject.com/KB/cs/GenericCrossThread.aspx

public static void Manipulate<T>(this T control, Action<T> action) where T : Control
{
    if (control.InvokeRequired)
    {
        control.Invoke(new Action<T, Action<T>>(Manipulate),
                    new object[] { control, action });
    }
    else
    { action(control); }
}

This can be called in the following manner, for simplicity I used a label.

someLabel.Manipulate(lbl => lbl.Text = "Something");
Arical
  • 324
  • 2
  • 5
0

You should NOT use BeginInvoke, you should use Invoke, then once you grasp that, you can look into using BeginInvoke if really needed.

leppie
  • 115,091
  • 17
  • 196
  • 297
0
'*******************************************************************
' Get a new processor and fire it off on a new thread.
'*******************************************************************
fpProc = New Processor(confTable, paramFile, keyCount)
AddHandler fpProc.LogEntry, AddressOf LogEntry_Handler
Dim myThread As System.Threading.Thread = New System.Threading.Thread(AddressOf fpProc.ProcessEntry)
myThread.Start()

Then in the parent app you have:

'*************************************************************************
'     Sub: LogEntry_Handler()
'  Author: Ron Savage
'    Date: 08/29/2007
'
' This routine handles the LogEntry events raised by the Processor class
' running in a thread.
'*************************************************************************
Private Sub LogEntry_Handler(ByVal logLevel As Integer, ByVal logMsg As String) Handles fProc.LogEntry
 writeLogMessage(logMsg);
End Sub

That's what I do.

Ron Savage
  • 10,923
  • 4
  • 26
  • 35
  • It uses the event message queue to handle inter process communications (thread to parent in this case. :-) I have an "unknown number" of threads all sending updates to the same parent window. – Ron Savage Sep 29 '08 at 16:26
  • I'm in agreement with leppie. – RickL Sep 29 '08 at 16:31
0

Check for InvokeRequired

0

I praticularly like a recursive call.

public delegate void InvokeDelegate(string errMessage); 

    public void SomeMethod() 
    { 
        doUIStuff("my error message");
    } 


    void doUIStuff(string errMessage) 
    { 
        if (button1.InvokeRequired)
            button1.Invoke((InvokeDelegate)doUIStuff(errMessage)); 
        else
        {
               MessageBox.Show(this,    
                    ex.Message,  
                    errMessage,  
                    MessageBoxButtons.OK,  
                    MessageBoxIcon.Error 
                ); 
        }
    } 
Joel Barsotti
  • 3,031
  • 7
  • 35
  • 59