InvokeRequired not reliable?

Posted by marocanu2001 on Geeks with Blogs See other posts from Geeks with Blogs or by marocanu2001
Published on Sun, 28 Mar 2010 10:16:38 GMT Indexed on 2010/03/28 11:33 UTC
Read the original article Hit count: 430

Filed under:

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

© Geeks with Blogs or respective owner