views:

304

answers:

4

I was testing a program I am writing and I got this error message: Cross-thread operation not valid: Control 'lblStatus' accessed from a thread other than the thread it was created on

The code is a bit massive and I am not sure which part is causing this error to post a smaller segment. However here is some info that might be of use.

I am not using any "threading" explicitly. I am guessing something else is automatically creating multiple threads - I am using a Wii remote hardware access library and I am doing graphics manipulation.

The stack trace indicates, that a call is made to an on change event handler, which calls a function inside which lblStatus is tried to be modified - but fails.

I was wondering how do you debug these types of errors. I am using Visual Studio 2008.

EDIT

One thing I want to clarify, I do not know how this different thread even came up. How would I even find this? the source of the different thread.

+2  A: 

You can't access visual elements from any thread other than the main thread. You need to use Dispatcher.Invoke() to call a delegate which updates the visual elements.

Example:

this.Dispatcher.Invoke((Action)(() => lblStatus.Content = "Hello"));

or (without lambda):

this.Dispatcher.Invoke((Action)delegate() { lblStatus.Content = "Hello"; });
Jake
@Jake: Thanks for your answer. I should have clarified, I am not using WPF, but winforms.
aip.cd.aish
A: 

You need to combine the use of Delegates with your threads..

Assign your method to a delegate and then invoke the delegate through thread.

Bibhas
+4  A: 
public void SetStatus(string msg)
{
    if (lblStatus.InvokeRequired)
        lblStatus.Invoke(new MethodInvoker(delegate
        {
            lblStatus.Text = msg;
        }));
    else
        lblStatus.Text = msg;
}

This will get your label text updated.

For a BeginInvoke, this is the way I know (I know there are more elegant implementations) - but I haven't tried this in a multi-threaded app yet:

Action<string> setStatus= target.AppendText;

void OnSomeEvent (object sender, EventArgs e)
{ 
    IAsyncRes iares = setStatus.BeginInvoke("status message", null, null); 
    setStatus.EndInvoke(iares);
}

public void SetStatus(string msg)
{ lblStatus.Text = msg; }

For the different methods of synchronizing to the control thread, SnOrfus references an excellent link. My example above on BeginInvoke is not correct for synchronizing to the control's thread.

As far as what is causing the thread: About WiimoteChanged event

dboarman
+1: or use BeginInvoke if you don't want to block on the UI thread.
SnOrfus
@SnOrfus: but only if you're willing to call EndInvoke when done.
John Saunders
@dboarman: Thanks for the link on the WiimoteChanged event. I feel a bit more comfortable knowing this.
aip.cd.aish
@John Saunders. You don't have to call EndInvoke on Control.BeginInvoke() (http://weblogs.asp.net/justin_rogers/pages/126345.aspx)
SnOrfus
@SnOrfus: is that guaranteed, or is it just how it's currently implemented?
John Saunders
@John Saunders: I don't know how guaranteed it is, but the msdn specifically notes that you don't have to call EndInvoke. From msdn: "You can call EndInvoke to retrieve the return value from the delegate, if neccesary, but this is not required."
SnOrfus
@SnOrfus: I call that a guarantee. Can you post the link to where you saw that?
John Saunders
@John Saunders: Sure can. http://msdn.microsoft.com/en-us/library/0b1bf3y3.aspx
SnOrfus
@SnOrfus: thanks
John Saunders
A: 

One thing I want to clarify, I do not know how this different thread even came up

If your app is not multithreaded, the problem could be caused by the Visual Studio hosting process. The Visual Studio hosting process starts up a main thread and then creates a new UI thread that the application is run on. In some circumstances this can lead to a cross-thread exception, for example when accessing Form.ActiveForm before calling Application.Run.

If this is your problem, you can uncheck "Enable the Visual Studio hosting process" in the Debug tab of Project Properties.

Joe