溫故知新,遇見BeginInvoke/Invoke,適用於WPF/WinFroms的執行緒和程序間訊息通訊,深入淺出實現原始碼
阿新 • • 發佈:2021-09-06
從子執行緒操作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
介面,並且實現了Invoke
和BeginInvoke
方法來提供讓其它執行緒更新GUI介面控制元件的機制。
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);
}
}