Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Curious about the implementation of Control.Invoke()

What exactly does Control.Invoke(Delegate) do to get the delegate to run on the GUI thread? Furthermore, Its my understanding that invoke will block until the invoked function its done. How does it achieve this?

I would like some good gritty details. I'm hoping to learn something interesting.

like image 601
Kratz Avatar asked Aug 04 '11 21:08

Kratz


People also ask

What is invoke () in C#?

The Invoke method searches up the control's parent chain until it finds a control or form that has a window handle if the current control's underlying window handle does not exist yet. If no appropriate handle can be found, the Invoke method will throw an exception.


2 Answers

Edit: The control implements ISynchronizeInvoke interface, You can make the same effect using the SynchronizationContext and call Post when you call Invoke. something like:

public object Invoke(Delegate method, object[] args)
{
    if (method == null)
    {
        throw new ArgumentNullException("method");
    }

    object objectToGet = null;

    SendOrPostCallback invoker = new SendOrPostCallback(
    delegate(object data)
    {
        objectToGet = method.DynamicInvoke(args);
    });

    _currentContext.Send(new SendOrPostCallback(invoker), method.Target);

    return objectToGet;
}

Further investigation using Reflector shows that the Invoke uses some native API calls to achieve that:

private object MarshaledInvoke(Control caller, Delegate method, object[] args, bool synchronous)
{
    int num;
    if (!this.IsHandleCreated)
    {
        throw new InvalidOperationException(SR.GetString("ErrorNoMarshalingThread"));
    }
    if (((ActiveXImpl) this.Properties.GetObject(PropActiveXImpl)) != null)
    {
        IntSecurity.UnmanagedCode.Demand();
    }
    bool flag = false;
    if ((SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, this.Handle), out num) == SafeNativeMethods.GetCurrentThreadId()) && synchronous)
    {
        flag = true;
    }
    ExecutionContext executionContext = null;
    if (!flag)
    {
        executionContext = ExecutionContext.Capture();
    }
    ThreadMethodEntry entry = new ThreadMethodEntry(caller, this, method, args, synchronous, executionContext);
    lock (this)
    {
        if (this.threadCallbackList == null)
        {
            this.threadCallbackList = new Queue();
        }
    }
    lock (this.threadCallbackList)
    {
        if (threadCallbackMessage == 0)
        {
            threadCallbackMessage = SafeNativeMethods.RegisterWindowMessage(Application.WindowMessagesVersion + "_ThreadCallbackMessage");
        }
        this.threadCallbackList.Enqueue(entry);
    }
    if (flag)
    {
        this.InvokeMarshaledCallbacks();
    }
    else
    {
        UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);
    }
    if (!synchronous)
    {
        return entry;
    }
    if (!entry.IsCompleted)
    {
        this.WaitForWaitHandle(entry.AsyncWaitHandle);
    }
    if (entry.exception != null)
    {
        throw entry.exception;
    }
    return entry.retVal;
}
like image 90
Jalal Said Avatar answered Sep 17 '22 11:09

Jalal Said


If I want to know internals, I usually fire up ILSpy and look at the decompiled sources of the BCL. Alternatively, you could download the Mono or the Rotor sources.

like image 32
Uwe Keim Avatar answered Sep 19 '22 11:09

Uwe Keim