InvokeRequired not reliable?
- by marocanu2001
InvalidOperationException: Cross-thread operation not valid: Control 'progressBar' accessed from a thread other than the thread it was created ...
Now this is not a nice way to start a day! not even if it's a Monday!
So you have seen this and already thought, come on, this is an old one, just ask whether InvokeRequired before calling the method in the control, something like this:
if (progressBar.InvokeRequired) {
progressBar.Invoke ( new MethodInvoker( delegate { progressBar.Value = 50; }) )
}else
progressBar.Value = 50;
Unfortunately this was not working the way I would have expected, I got this error, debugged and though in debugging the InvokeRequired had become true , the error was thrown on the branch that did not required Invoke.
So there was a scenario where InvokeRequired returned false and still accessing the control was not on the right thread ...
Problem was that I kept showing and hiding the little form showing the progressbar. The progressbar was updating on an event , ProgressChanged and I could not guarantee the little form was loaded by the time the event was thrown.
So , spotted the problem, if none of the parents of the control you try to modify is created at the time of the method invoking, the InvokeRequired returns true! That causes your code to execute on the woring thread. Of course, updating UI before the win dow was created is not a legitimate action either, still I would have expected a different error.
MSDN:
"If the control's handle does not yet exist, InvokeRequired searches up the control's parent chain until it finds a control or form that does have a window handle. If no appropriate handle can be found, the InvokeRequired method returns false.
This means that InvokeRequired can return false if Invoke is not required (the call occurs on the same thread), or if the control was created on a different thread but the control's handle has not yet been created."
Have a look at InvokeRequired's implementation:
public bool InvokeRequired
{
get
{
HandleRef hWnd;
int lpdwProcessId;
if (this.IsHandleCreated)
{
hWnd = new HandleRef(this, this.Handle);
}
else
{
Control wrapper = this.FindMarshallingControl();
if (!wrapper.IsHandleCreated)
{
return false; // <==========
}
hWnd = new HandleRef(wrapper, wrapper.Handle);
}
int windowThreadProcessId = SafeNativeMethods.GetWindowThreadProcessId(hWnd, out lpdwProcessId);
int currentThreadId = SafeNativeMethods.GetCurrentThreadId();
return (windowThreadProcessId != currentThreadId);
}
}
Here 's a good article about this and a workaround http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html