1. 程式人生 > 其它 >溫故知新,遇見BeginInvoke/Invoke,適用於WPF/WinFroms的執行緒和程序間訊息通訊,深入淺出實現原始碼

溫故知新,遇見BeginInvoke/Invoke,適用於WPF/WinFroms的執行緒和程序間訊息通訊,深入淺出實現原始碼

從子執行緒操作UI執行緒的方法實現

https://referencesource.microsoft.com/#System/compmod/system/componentmodel/ISynchronizeInvoke.cs

關於ISynchronizeInvoke介面的定義。

//------------------------------------------------------------------------------
// <copyright file="ISynchronizeInvoke.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.ComponentModel {
    using System; 
    using System.Security.Permissions;
         
    /// <devdoc>
    ///    <para>Provides a way to synchronously or asynchronously execute a delegate.</para>
    /// </devdoc>
    public interface ISynchronizeInvoke {
    
        /// <devdoc>
        /// <para>Gets a value indicating whether the caller must call <see cref='System.ComponentModel.ISynchronizeInvoke.Invoke'/> when calling an object that implements 
        ///    this interface.</para>
        /// </devdoc>
        bool InvokeRequired{get;}
                
        /// <devdoc>
        ///    <para> 
        ///       Executes the given delegate on the main thread that this object executes on.</para>
        /// </devdoc>
        [HostProtection(Synchronization=true, ExternalThreading=true)]
        IAsyncResult BeginInvoke(Delegate method, object[] args);            
        
        /// <devdoc>
        ///    <para>Waits until the process you started by 
        ///       calling <see cref='System.ComponentModel.ISynchronizeInvoke.BeginInvoke'/> completes, and then returns
        ///       the value generated by the process.</para>
        /// </devdoc>
        object EndInvoke(IAsyncResult result);                      
        
        /// <devdoc>
        ///    <para> 
        ///       Executes the given delegate on the main thread that this object
        ///       executes on.</para>
        /// </devdoc>
        object Invoke(Delegate method, object[] args);        
    }
}

System.Windows.Forms.Control類集成了ISynchronizeInvoke介面,並且實現了InvokeBeginInvoke方法來提供讓其它執行緒更新GUI介面控制元件的機制。

https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs

namespace System.Windows.Forms
{
    public partial class Control :
    Component,
    UnsafeNativeMethods.IOleControl,
    UnsafeNativeMethods.IOleObject,
    UnsafeNativeMethods.IOleInPlaceObject,
    UnsafeNativeMethods.IOleInPlaceActiveObject,
    UnsafeNativeMethods.IOleWindow,
    UnsafeNativeMethods.IViewObject,
    UnsafeNativeMethods.IViewObject2,
    UnsafeNativeMethods.IPersist,
    UnsafeNativeMethods.IPersistStreamInit,
    UnsafeNativeMethods.IPersistPropertyBag,
    UnsafeNativeMethods.IPersistStorage,
    UnsafeNativeMethods.IQuickActivate,
    ISupportOleDropSource,
    IDropTarget,
    ISynchronizeInvoke,
    IWin32Window,
    IArrangedElement,
    IBindableComponent,
    IKeyboardToolTip {
        
    }
}

System.Windows.Forms.Control類中ISynchronizeInvoke介面的InvokeRequired的實現。

/// <include file='doc\Control.uex' path='docs/doc[@for="Control.InvokeRequired"]/*' />
/// <devdoc>
///     Determines if the caller must call invoke when making method
///     calls to this control.  Controls in windows forms are bound to a specific thread,
///     and are not thread safe.  Therefore, if you are calling a control's method
///     from a different thread, you must use the control's invoke method
///     to marshal the call to the proper thread.  This function can be used to
///     determine if you must call invoke, which can be handy if you don't know
///     what thread owns a control.
///
///     There are five functions on a control that are safe to call from any
///     thread:  GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and
///     CreateGraphics.  For all other method calls, you should use one of the
///     invoke methods.
/// </devdoc>
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.ControlInvokeRequiredDescr)
]
public bool InvokeRequired {
    get {

        using (new MultithreadSafeCallScope())
        {
            HandleRef hwnd;
            if (IsHandleCreated) {
                hwnd = new HandleRef(this, Handle);
            }
            else {
                Control marshalingControl = FindMarshalingControl();

                if (!marshalingControl.IsHandleCreated) {
                    return false;
                }

                hwnd = new HandleRef(marshalingControl, marshalingControl.Handle);
            }

            int pid;
            int hwndThread = SafeNativeMethods.GetWindowThreadProcessId(hwnd, out pid);
            int currentThread = SafeNativeMethods.GetCurrentThreadId();
            return(hwndThread != currentThread);
        }
    }
}

System.Windows.Forms.Control類中ISynchronizeInvoke介面的BeginInvoke的實現。

/// <include file='doc\Control.uex' path='docs/doc[@for="Control.BeginInvoke"]/*' />
/// <devdoc>
///     Executes the given delegate on the thread that owns this Control's
///     underlying window handle.  The delegate is called asynchronously and this
///     method returns immediately.  You may call this from any thread, even the
///     thread that owns the control's handle.  If the control's handle doesn't
///     exist yet, this will follow 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, BeginInvoke will throw an exception.  Exceptions within the
///     delegate method are considered untrapped and will be sent to the
///     application's untrapped exception handler.
///
///     There are five functions on a control that are safe to call from any
///     thread:  GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics.
///     For all other method calls, you should use one of the invoke methods to marshal
///     the call to the control's thread.
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public IAsyncResult BeginInvoke(Delegate method) {
    return BeginInvoke(method, null);
}

/// <include file='doc\Control.uex' path='docs/doc[@for="Control.BeginInvoke1"]/*' />
/// <devdoc>
///     Executes the given delegate on the thread that owns this Control's
///     underlying window handle.  The delegate is called asynchronously and this
///     method returns immediately.  You may call this from any thread, even the
///     thread that owns the control's handle.  If the control's handle doesn't
///     exist yet, this will follow 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, BeginInvoke will throw an exception.  Exceptions within the
///     delegate method are considered untrapped and will be sent to the
///     application's untrapped exception handler.
///
///     There are five functions on a control that are safe to call from any
///     thread:  GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics.
///     For all other method calls, you should use one of the invoke methods to marshal
///     the call to the control's thread.
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Advanced)]        
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
public IAsyncResult BeginInvoke(Delegate method, params Object[] args) {
    using (new MultithreadSafeCallScope()) {
        Control marshaler = FindMarshalingControl();
        return(IAsyncResult)marshaler.MarshaledInvoke(this, method, args, false);
    }
}

System.Windows.Forms.Control類中ISynchronizeInvoke介面的Invoke的實現。

/// <include file='doc\Control.uex' path='docs/doc[@for="Control.Invoke"]/*' />
/// <devdoc>
///     Executes the given delegate on the thread that owns this Control's
///     underlying window handle.  It is an error to call this on the same thread that
///     the control belongs to.  If the control's handle doesn't exist yet, this will
///     follow 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, invoke will throw
///     an exception.  Exceptions that are raised during the call will be
///     propapgated back to the caller.
///
///     There are five functions on a control that are safe to call from any
///     thread:  GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics.
///     For all other method calls, you should use one of the invoke methods to marshal
///     the call to the control's thread.
/// </devdoc>
public Object Invoke(Delegate method) {
    return Invoke(method, null);
}

/// <include file='doc\Control.uex' path='docs/doc[@for="Control.Invoke1"]/*' />
/// <devdoc>
///     Executes the given delegate on the thread that owns this Control's
///     underlying window handle.  It is an error to call this on the same thread that
///     the control belongs to.  If the control's handle doesn't exist yet, this will
///     follow 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, invoke will throw
///     an exception.  Exceptions that are raised during the call will be
///     propapgated back to the caller.
///
///     There are five functions on a control that are safe to call from any
///     thread:  GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics.
///     For all other method calls, you should use one of the invoke methods to marshal
///     the call to the control's thread.
/// </devdoc>
public Object Invoke(Delegate method, params Object[] args) {
    using (new MultithreadSafeCallScope()) {
        Control marshaler = FindMarshalingControl();
        return marshaler.MarshaledInvoke(this, method, args, true);
    }
}

參考