|
using System; // Console
using System.Collections.Generic; // List<T>
using MS.Win32; // win32 interop
using System.Windows.Interop; // ComponentDispatcher & MSG
using Microsoft.Win32; // Registry
using System.Security; // CAS
using System.Security.Permissions; // Registry permissions
using System.Diagnostics; // Debug
using MS.Utility; // EventTrace
using System.Reflection; // Assembly
using System.Runtime.InteropServices; // SEHException
using MS.Internal; // SecurityCriticalData, TextServicesInterop
using MS.Internal.Interop; // WM
using MS.Internal.WindowsBase; // SecurityHelper
using System.Threading;
using System.ComponentModel; // EditorBrowsableAttribute, BrowsableAttribute
// Disabling 1634 and 1691:
// In order to avoid generating warnings about unknown message numbers and
// unknown pragmas when compiling C# source code with the C# compiler,
// you need to disable warnings 1634 and 1691. (Presharp Documentation)
#pragma warning disable 1634, 1691
namespace System.Windows.Threading
{
/// <summary>
/// Provides UI services for a thread.
/// </summary>
public sealed class Dispatcher
{
/// <SecurityNote>
/// Critical: This code calls into RegisterWindowMesssage which is critical
/// TreatAsSafe: This is safe to call as no external parameters are taken in
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
static Dispatcher()
{
_msgProcessQueue = UnsafeNativeMethods.RegisterWindowMessage("DispatcherProcessQueue");
_globalLock = new object();
_dispatchers = new List<WeakReference>();
_possibleDispatcher = new WeakReference(null);
_exceptionWrapper = new ExceptionWrapper();
_exceptionWrapper.Catch += new ExceptionWrapper.CatchHandler(CatchExceptionStatic);
_exceptionWrapper.Filter += new ExceptionWrapper.FilterHandler(ExceptionFilterStatic);
}
/// <summary>
/// Returns the Dispatcher for the calling thread.
/// </summary>
/// <remarks>
/// If there is no dispatcher available for the current thread,
/// and the thread allows a dispatcher to be auto-created, a new
/// dispatcher will be created.
/// <P/>
/// If there is no dispatcher for the current thread, and thread
/// does not allow one to be auto-created, an exception is thrown.
/// </remarks>
public static Dispatcher CurrentDispatcher
{
get
{
// Find the dispatcher for this thread.
Dispatcher currentDispatcher = FromThread(Thread.CurrentThread);;
// Auto-create the dispatcher if there is no dispatcher for
// this thread (if we are allowed to).
if(currentDispatcher == null)
{
currentDispatcher = new Dispatcher();
}
return currentDispatcher;
}
}
/// <summary>
/// Returns the Dispatcher for the specified thread.
/// </summary>
/// <remarks>
/// If there is no dispatcher available for the specified thread,
/// this method will return null.
/// </remarks>
public static Dispatcher FromThread(Thread thread)
{
lock(_globalLock)
{
Dispatcher dispatcher = null;
if(thread != null)
{
// Shortcut: we track one static reference to the last current
// dispatcher we gave out. For single-threaded apps, this will
// be set all the time. For multi-threaded apps, this will be
// set for periods of time during which accessing CurrentDispatcher
// is cheap. When a thread switch happens, the next call to
// CurrentDispatcher is expensive, but then the rest are fast
// again.
dispatcher = _possibleDispatcher.Target as Dispatcher;
if(dispatcher == null || dispatcher.Thread != thread)
{
// The "possible" dispatcher either was null or belongs to
// the a different thread.
dispatcher = null;
// Spin over the list of dispatchers looking for one that belongs
// to this thread. We could use TLS here, but managed TLS is very
// expensive, so we think it is cheaper to search our own data
// structure.
//
// Note: Do not cache _dispatchers.Count because we rely on it
// being updated if we encounter a dead weak reference.
for(int i = 0; i < _dispatchers.Count; i++)
{
Dispatcher d = _dispatchers[i].Target as Dispatcher;
if(d != null)
{
// Note: we compare the thread objects themselves to protect
// against threads reusing old thread IDs.
Thread dispatcherThread = d.Thread;
if(dispatcherThread == thread)
{
dispatcher = d;
// Do not exit the loop early since we are also
// looking for dead references.
}
}
else
{
// We found a dead reference, so remove it from
// the list, and adjust the index so we account
// for it.
_dispatchers.RemoveAt(i);
i--;
}
}
// Stash this dispatcher as a "possible" dispatcher for the
// next call to FromThread.
if(dispatcher != null)
{
// We expect this call to be frequent so we want to
// avoid uneccesary allocations, such as a new
// WeakReference. However, we discovered late
// in Dev11 that sometimes this code is called
// during finalization, and the existing
// WeakReference may throw when you try to change
// the Target because the GC has already discarded
// the handle for the WeakReference.
//
// Ideally we would re-work the code to avoid
// calling this method from a finalizer path. But
// that is tricky: we are destroying an HWND
// (appropriate for a finalizer) that has a managed
// WndProc, which calls Dispatcher.Invoke to get
// under the exception filters. Changing all of that
// code would be very risky at this point.
//
// There is no good API to check if running on the
// finalizer thread, or if the handle of the
// WeakReference has been reclaimed by the GC.
// The best we can do is check IsAlive, which will
// return false if either the handle has been
// reclaimed or the target has been collected.
// If that happens, we allocate a new
// WeakReference instance, rather than reusing the
// existing one.
if(_possibleDispatcher.IsAlive)
{
_possibleDispatcher.Target = dispatcher;
}
else
{
_possibleDispatcher = new WeakReference(dispatcher);
}
}
}
}
return dispatcher;
}
}
/// <summary>
/// </summary>
public Thread Thread
{
get
{
return _dispatcherThread;
}
}
/// <summary>
/// Checks that the calling thread has access to this object.
/// </summary>
/// <remarks>
/// Only the dispatcher thread may access DispatcherObjects.
/// <p/>
/// This method is public so that any thread can probe to
/// see if it has access to the DispatcherObject.
/// </remarks>
/// <returns>
/// True if the calling thread has access to this object.
/// </returns>
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public bool CheckAccess()
{
return Thread == Thread.CurrentThread;
}
/// <summary>
/// Verifies that the calling thread has access to this object.
/// </summary>
/// <remarks>
/// Only the dispatcher thread may access DispatcherObjects.
/// <p/>
/// This method is public so that derived classes can probe to
/// see if the calling thread has access to itself.
/// </remarks>
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public void VerifyAccess()
{
if(!CheckAccess())
{
throw new InvalidOperationException(SR.Get(SRID.VerifyAccess));
}
}
/// <summary>
/// Begins the process of shutting down the dispatcher.
/// </summary>
/// <remarks>
/// This API demand unrestricted UI Permission
/// </remarks>
///<SecurityNote>
/// Critical - it calls critical methods (ShutdownCallback).
/// PublicOK - it demands unrestricted UI permission.
///</SecurityNote>
[SecurityCritical]
public void BeginInvokeShutdown(DispatcherPriority priority) // NOTE: should be Priority
{
// We didn't want to enable quitting in the SEE
SecurityHelper.DemandUnrestrictedUIPermission();
BeginInvoke(priority, new ShutdownCallback(ShutdownCallbackInternal));
}
/// <summary>
/// Begins the process of shutting down the dispatcher.
/// </summary>
/// <remarks>
/// Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
/// </remarks>
///<SecurityNote>
/// Critical - it calls critical methods (ShutdownCallback).
/// PublicOK - it demands unrestricted UI permission
///</SecurityNote>
[SecurityCritical]
public void InvokeShutdown()
{
// We didn't want to enable quitting in the SEE
SecurityHelper.DemandUnrestrictedUIPermission();
CriticalInvokeShutdown();
}
///<SecurityNote>
/// Critical - it calls critical methods (ShutdownCallback).
///</SecurityNote>
[SecurityCritical]
[FriendAccessAllowed] //used by Application.ShutdownImpl() in PresentationFramework
internal void CriticalInvokeShutdown()
{
Invoke(DispatcherPriority.Send, new ShutdownCallback(ShutdownCallbackInternal)); // NOTE: should be Priority.Max
}
/// <summary>
/// Whether or not the dispatcher is shutting down.
/// </summary>
public bool HasShutdownStarted
{
get
{
return _hasShutdownStarted; // Free-Thread access OK.
}
}
/// <summary>
/// Whether or not the dispatcher has been shut down.
/// </summary>
public bool HasShutdownFinished
{
get
{
return _hasShutdownFinished; // Free-Thread access OK.
}
}
/// <summary>
/// Raised when the dispatcher is shutting down.
/// </summary>
public event EventHandler ShutdownStarted;
/// <summary>
/// Raised when the dispatcher is shut down.
/// </summary>
public event EventHandler ShutdownFinished;
/// <summary>
/// Push the main execution frame.
/// </summary>
/// <remarks>
/// This frame will continue until the dispatcher is shut down.
/// Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
/// </remarks>
///<SecurityNote>
/// Critical: This code is blocked off more as defense in depth
/// PublicOk: From a public perspective there is a link demand here
///</SecurityNote>
[UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
[SecurityCritical]
public static void Run()
{
PushFrame(new DispatcherFrame());
}
/// <summary>
/// Push an execution frame.
/// </summary>
/// <param name="frame">
/// The frame for the dispatcher to process.
/// </param>
/// <remarks>
/// Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
/// </remarks>
///<SecurityNote>
/// Critical: This code is blocked off more as defense in depth
/// PublicOk: From a public perspective there is a link demand here
///</SecurityNote>
[UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
[SecurityCritical]
public static void PushFrame(DispatcherFrame frame)
{
if(frame == null)
{
throw new ArgumentNullException("frame");
}
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
if(dispatcher._hasShutdownFinished) // Dispatcher thread - no lock needed for read
{
throw new InvalidOperationException(SR.Get(SRID.DispatcherHasShutdown));
}
if(frame.Dispatcher != dispatcher)
{
throw new InvalidOperationException(SR.Get(SRID.MismatchedDispatchers));
}
if(dispatcher._disableProcessingCount > 0)
{
throw new InvalidOperationException(SR.Get(SRID.DispatcherProcessingDisabled));
}
dispatcher.PushFrameImpl(frame);
}
/// <summary>
/// Requests that all nested frames exit.
/// </summary>
/// <remarks>
/// Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
/// </remarks>
/// <SecurityNote>
/// Critical - calls a critical method - postThreadMessage.
/// PublicOK - all we're doing is posting a current message to our thread.
/// net effect is the dispatcher "wakes up"
/// and uses the continue flag ( which may have just changed).
/// </SecurityNote>
[SecurityCritical]
public static void ExitAllFrames()
{
// We didn't want to enable exiting all frames in the SEE
SecurityHelper.DemandUnrestrictedUIPermission();
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
if(dispatcher._frameDepth > 0)
{
dispatcher._exitAllFrames = true;
// Post a message so that the message pump will wake up and
// check our continue state.
dispatcher.BeginInvoke(DispatcherPriority.Send, (Action) delegate {});
}
}
/// <summary>
/// Returns an awaitable object that can be used to queue
/// continuations at background priority.
/// </summary>
public static DispatcherPriorityAwaitable Yield()
{
return Yield(DispatcherPriority.Background);
}
/// <summary>
/// Returns an awaitable object that can be used to queue
/// continuations at the specified priority.
/// </summary>
public static DispatcherPriorityAwaitable Yield(DispatcherPriority priority)
{
ValidatePriority(priority, "priority");
Dispatcher currentDispatcher = FromThread(Thread.CurrentThread);;
if(currentDispatcher == null)
{
throw new InvalidOperationException(SR.Get(SRID.DispatcherYieldNoAvailableDispatcher));
}
return new DispatcherPriorityAwaitable(currentDispatcher, priority);
}
/// <summary>
/// Executes the specified delegate asynchronously on the thread that
/// the Dispatcher was created on.
/// </summary>
/// <param name="priority">
/// The priority that determines in what order the specified method
/// is invoked relative to the other pending methods in the Dispatcher.
/// </param>
/// <param name="method">
/// A delegate to a method that takes no parameters.
/// </param>
/// <returns>
/// An IAsyncResult object that represents the result of the
/// BeginInvoke operation.
/// </returns>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public DispatcherOperation BeginInvoke(DispatcherPriority priority, Delegate method) // NOTE: should be Priority
{
return LegacyBeginInvokeImpl(priority, method, null, 0);
}
/// <summary>
/// Executes the specified delegate asynchronously with the specified
/// arguments, on the thread that the Dispatcher was created on.
/// </summary>
/// <param name="priority">
/// The priority that determines in what order the specified method
/// is invoked relative to the other pending methods in the Dispatcher.
/// </param>
/// <param name="method">
/// A delegate to a method that takes parameters of the same number
/// and type that are contained in the args parameter.
/// </param>
/// <param name="arg">
/// A object to pass as an argument to the given method.
/// This can be null if no arguments are needed.
/// </param>
/// <returns>
/// An IAsyncResult object that represents the result of the
/// BeginInvoke operation.
/// </returns>
//[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public DispatcherOperation BeginInvoke(DispatcherPriority priority, Delegate method, object arg) // NOTE: should be Priority
{
return LegacyBeginInvokeImpl(priority, method, arg, 1);
}
/// <summary>
/// Executes the specified delegate asynchronously with the specified
/// arguments, on the thread that the Dispatcher was created on.
/// </summary>
/// <param name="priority">
/// The priority that determines in what order the specified method
/// is invoked relative to the other pending methods in the Dispatcher.
/// </param>
/// <param name="method">
/// A delegate to a method that takes parameters of the same number
/// and type that are contained in the args parameter.
/// </param>
/// <param name="arg">
/// enh
/// </param>
/// <param name="args">
/// An array of objects to pass as arguments to the given method.
/// This can be null if no arguments are needed.
/// </param>
/// <returns>
/// An IAsyncResult object that represents the result of the
/// BeginInvoke operation.
/// </returns>
//[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public DispatcherOperation BeginInvoke(DispatcherPriority priority, Delegate method, object arg, params object[] args)
{
return LegacyBeginInvokeImpl(priority, method, CombineParameters(arg, args), -1);
}
/// <summary>
/// Executes the specified delegate asynchronously with the specified
/// arguments, on the thread that the Dispatcher was created on.
/// </summary>
/// <param name="method">
/// A delegate to a method that takes parameters of the same number
/// and type that are contained in the args parameter.
/// </param>
/// <param name="args">
/// An array of objects to pass as arguments to the given method.
/// This can be null if no arguments are needed.
/// </param>
/// <returns>
/// An IAsyncResult object that represents the result of the
/// BeginInvoke operation.
/// </returns>
public DispatcherOperation BeginInvoke(Delegate method, params object[] args)
{
return LegacyBeginInvokeImpl(DispatcherPriority.Normal, method, args, -1);
}
/// <summary>
/// Executes the specified delegate asynchronously with the specified
/// arguments, on the thread that the Dispatcher was created on.
/// </summary>
/// <param name="method">
/// A delegate to a method that takes parameters of the same number
/// and type that are contained in the args parameter.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified method
/// is invoked relative to the other pending methods in the Dispatcher.
/// </param>
/// <param name="args">
/// An array of objects to pass as arguments to the given method.
/// This can be null if no arguments are needed.
/// </param>
/// <returns>
/// An IAsyncResult object that represents the result of the
/// BeginInvoke operation.
/// </returns>
public DispatcherOperation BeginInvoke(Delegate method, DispatcherPriority priority, params object[] args)
{
return LegacyBeginInvokeImpl(priority, method, args, -1);
}
/// <summary>
/// Executes the specified Action synchronously on the thread that
/// the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// An Action delegate to invoke through the dispatcher.
/// </param>
/// <remarks>
/// Note that the default priority is DispatcherPriority.Send.
/// </remarks>
public void Invoke(Action callback)
{
Invoke(callback, DispatcherPriority.Send, CancellationToken.None, TimeSpan.FromMilliseconds(-1));
}
/// <summary>
/// Executes the specified Action synchronously on the thread that
/// the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// An Action delegate to invoke through the dispatcher.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified
/// callback is invoked relative to the other pending operations
/// in the Dispatcher.
/// </param>
public void Invoke(Action callback, DispatcherPriority priority)
{
Invoke(callback, priority, CancellationToken.None, TimeSpan.FromMilliseconds(-1));
}
/// <summary>
/// Executes the specified Action synchronously on the thread that
/// the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// An Action delegate to invoke through the dispatcher.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified
/// callback is invoked relative to the other pending operations
/// in the Dispatcher.
/// </param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation.
/// If the operation has not started, it will be aborted when the
/// cancellation token is canceled. If the operation has started,
/// the operation can cooperate with the cancellation request.
/// </param>
public void Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken)
{
Invoke(callback, priority, cancellationToken, TimeSpan.FromMilliseconds(-1));
}
/// <summary>
/// Executes the specified Action synchronously on the thread that
/// the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// An Action delegate to invoke through the dispatcher.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified
/// callback is invoked relative to the other pending operations
/// in the Dispatcher.
/// </param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation.
/// If the operation has not started, it will be aborted when the
/// cancellation token is canceled. If the operation has started,
/// the operation can cooperate with the cancellation request.
/// </param>
/// <param name="timeout">
/// The minimum amount of time to wait for the operation to start.
/// Once the operation has started, it will complete before this method
/// returns.
/// </param>
/// <SecurityNote>
/// Critical:This code causes arbitrary delegate to execute
/// asynchronously, also calls critical code.
/// Safe: Executing the delegate asynchronously is OK because we
/// capture the ExecutionContext inside the DispatcherOperation.
/// </SecurityNote>
[SecuritySafeCritical]
public void Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout)
{
if(callback == null)
{
throw new ArgumentNullException("callback");
}
ValidatePriority(priority, "priority");
if( timeout.TotalMilliseconds < 0 &&
timeout != TimeSpan.FromMilliseconds(-1))
{
throw new ArgumentOutOfRangeException("timeout");
}
// Fast-Path: if on the same thread, and invoking at Send priority,
// and the cancellation token is not already canceled, then just
// call the callback directly.
if(!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess())
{
SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current;
try
{
DispatcherSynchronizationContext newSynchronizationContext;
if(BaseCompatibilityPreferences.GetReuseDispatcherSynchronizationContextInstance())
{
newSynchronizationContext = _defaultDispatcherSynchronizationContext;
}
else
{
if(BaseCompatibilityPreferences.GetFlowDispatcherSynchronizationContextPriority())
{
newSynchronizationContext = new DispatcherSynchronizationContext(this, priority);
}
else
{
newSynchronizationContext = new DispatcherSynchronizationContext(this, DispatcherPriority.Normal);
}
}
SynchronizationContext.SetSynchronizationContext(newSynchronizationContext);
callback();
return;
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext);
}
}
// Slow-Path: go through the queue.
DispatcherOperation operation = new DispatcherOperation(this, priority, callback);
InvokeImpl(operation, cancellationToken, timeout);
}
/// <summary>
/// Executes the specified Func<TResult> synchronously on the
/// thread that the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher.
/// </param>
/// <returns>
/// The return value from the delegate being invoked.
/// </returns>
/// <remarks>
/// Note that the default priority is DispatcherPriority.Send.
/// </remarks>
/// <SecurityNote>
/// Critical:This code causes arbitrary delegate to execute
/// asynchronously, also calls critical code.
/// Safe: Executing the delegate asynchronously is OK because we
/// capture the ExecutionContext inside the DispatcherOperation.
/// </SecurityNote>
public TResult Invoke<TResult>(Func<TResult> callback)
{
return Invoke(callback, DispatcherPriority.Send, CancellationToken.None, TimeSpan.FromMilliseconds(-1));
}
/// <summary>
/// Executes the specified Func<TResult> synchronously on the
/// thread that the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified
/// callback is invoked relative to the other pending operations
/// in the Dispatcher.
/// </param>
/// <returns>
/// The return value from the delegate being invoked.
/// </returns>
/// <SecurityNote>
/// Critical:This code causes arbitrary delegate to execute
/// asynchronously, also calls critical code.
/// Safe: Executing the delegate asynchronously is OK because we
/// capture the ExecutionContext inside the DispatcherOperation.
/// </SecurityNote>
public TResult Invoke<TResult>(Func<TResult> callback, DispatcherPriority priority)
{
return Invoke(callback, priority, CancellationToken.None, TimeSpan.FromMilliseconds(-1));
}
/// <summary>
/// Executes the specified Func<TResult> synchronously on the
/// thread that the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified
/// callback is invoked relative to the other pending operations
/// in the Dispatcher.
/// </param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation.
/// If the operation has not started, it will be aborted when the
/// cancellation token is canceled. If the operation has started,
/// the operation can cooperate with the cancellation request.
/// </param>
/// <returns>
/// The return value from the delegate being invoked.
/// </returns>
/// <SecurityNote>
/// Critical:This code causes arbitrary delegate to execute
/// asynchronously, also calls critical code.
/// Safe: Executing the delegate asynchronously is OK because we
/// capture the ExecutionContext inside the DispatcherOperation.
/// </SecurityNote>
public TResult Invoke<TResult>(Func<TResult> callback, DispatcherPriority priority, CancellationToken cancellationToken)
{
return Invoke(callback, priority, cancellationToken, TimeSpan.FromMilliseconds(-1));
}
/// <summary>
/// Executes the specified Func<TResult> synchronously on the
/// thread that the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified
/// callback is invoked relative to the other pending operations
/// in the Dispatcher.
/// </param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation.
/// If the operation has not started, it will be aborted when the
/// cancellation token is canceled. If the operation has started,
/// the operation can cooperate with the cancellation request.
/// </param>
/// <param name="timeout">
/// The minimum amount of time to wait for the operation to start.
/// Once the operation has started, it will complete before this method
/// returns.
/// </param>
/// <returns>
/// The return value from the delegate being invoked.
/// </returns>
/// <SecurityNote>
/// Critical:This code causes arbitrary delegate to execute
/// asynchronously, also calls critical code.
/// Safe: Executing the delegate asynchronously is OK because we
/// capture the ExecutionContext inside the DispatcherOperation.
/// </SecurityNote>
[SecuritySafeCritical]
public TResult Invoke<TResult>(Func<TResult> callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout)
{
if(callback == null)
{
throw new ArgumentNullException("callback");
}
ValidatePriority(priority, "priority");
if( timeout.TotalMilliseconds < 0 &&
timeout != TimeSpan.FromMilliseconds(-1))
{
throw new ArgumentOutOfRangeException("timeout");
}
// Fast-Path: if on the same thread, and invoking at Send priority,
// and the cancellation token is not already canceled, then just
// call the callback directly.
if(!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess())
{
SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current;
try
{
DispatcherSynchronizationContext newSynchronizationContext;
if(BaseCompatibilityPreferences.GetReuseDispatcherSynchronizationContextInstance())
{
newSynchronizationContext = _defaultDispatcherSynchronizationContext;
}
else
{
if(BaseCompatibilityPreferences.GetFlowDispatcherSynchronizationContextPriority())
{
newSynchronizationContext = new DispatcherSynchronizationContext(this, priority);
}
else
{
newSynchronizationContext = new DispatcherSynchronizationContext(this, DispatcherPriority.Normal);
}
}
SynchronizationContext.SetSynchronizationContext(newSynchronizationContext);
return callback();
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext);
}
}
// Slow-Path: go through the queue.
DispatcherOperation<TResult> operation = new DispatcherOperation<TResult>(this, priority, callback);
return (TResult) InvokeImpl(operation, cancellationToken, timeout);
}
/// <summary>
/// Executes the specified Action asynchronously on the thread
/// that the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// An Action delegate to invoke through the dispatcher.
/// </param>
/// <returns>
/// An operation representing the queued delegate to be invoked.
/// </returns>
/// <remarks>
/// Note that the default priority is DispatcherPriority.Normal.
/// </remarks>
public DispatcherOperation InvokeAsync(Action callback)
{
return InvokeAsync(callback, DispatcherPriority.Normal, CancellationToken.None);
}
/// <summary>
/// Executes the specified Action asynchronously on the thread
/// that the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// An Action delegate to invoke through the dispatcher.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified
/// callback is invoked relative to the other pending operations
/// in the Dispatcher.
/// </param>
/// <returns>
/// An operation representing the queued delegate to be invoked.
/// </returns>
/// <returns>
/// An operation representing the queued delegate to be invoked.
/// </returns>
public DispatcherOperation InvokeAsync(Action callback, DispatcherPriority priority)
{
return InvokeAsync(callback, priority, CancellationToken.None);
}
/// <summary>
/// Executes the specified Action asynchronously on the thread
/// that the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// An Action delegate to invoke through the dispatcher.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified
/// callback is invoked relative to the other pending operations
/// in the Dispatcher.
/// </param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation.
/// If the operation has not started, it will be aborted when the
/// cancellation token is canceled. If the operation has started,
/// the operation can cooperate with the cancellation request.
/// </param>
/// <returns>
/// An operation representing the queued delegate to be invoked.
/// </returns>
/// <SecurityNote>
/// Critical:This code causes arbitrary delegate to execute asynchronously, also calls critical code.
/// Safe: Executing the delegate asynchronously is OK because we capture the ExecutionContext.
/// </SecurityNote>
[SecuritySafeCritical]
public DispatcherOperation InvokeAsync(Action callback, DispatcherPriority priority, CancellationToken cancellationToken)
{
if(callback == null)
{
throw new ArgumentNullException("callback");
}
ValidatePriority(priority, "priority");
DispatcherOperation operation = new DispatcherOperation(this, priority, callback);
InvokeAsyncImpl(operation, cancellationToken);
return operation;
}
/// <summary>
/// Executes the specified Func<TResult> asynchronously on the
/// thread that the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher.
/// </param>
/// <returns>
/// An operation representing the queued delegate to be invoked.
/// </returns>
/// <remarks>
/// Note that the default priority is DispatcherPriority.Normal.
/// </remarks>
public DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback)
{
return InvokeAsync(callback, DispatcherPriority.Normal, CancellationToken.None);
}
/// <summary>
/// Executes the specified Func<TResult> asynchronously on the
/// thread that the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified
/// callback is invoked relative to the other pending operations
/// in the Dispatcher.
/// </param>
/// <returns>
/// An operation representing the queued delegate to be invoked.
/// </returns>
public DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback, DispatcherPriority priority)
{
return InvokeAsync(callback, priority, CancellationToken.None);
}
/// <summary>
/// Executes the specified Func<TResult> asynchronously on the
/// thread that the Dispatcher was created on.
/// </summary>
/// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified
/// callback is invoked relative to the other pending operations
/// in the Dispatcher.
/// </param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation.
/// If the operation has not started, it will be aborted when the
/// cancellation token is canceled. If the operation has started,
/// the operation can cooperate with the cancellation request.
/// </param>
/// <returns>
/// An operation representing the queued delegate to be invoked.
/// </returns>
/// <SecurityNote>
/// Critical:This code causes arbitrary delegate to execute
/// asynchronously, also calls critical code.
/// Safe: Executing the delegate asynchronously is OK because we
/// capture the ExecutionContext inside the DispatcherOperation.
/// </SecurityNote>
[SecuritySafeCritical]
public DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback, DispatcherPriority priority, CancellationToken cancellationToken)
{
if(callback == null)
{
throw new ArgumentNullException("callback");
}
ValidatePriority(priority, "priority");
DispatcherOperation<TResult> operation = new DispatcherOperation<TResult>(this, priority, callback);
InvokeAsyncImpl(operation, cancellationToken);
return operation;
}
/// <SecurityNote>
/// Critical:This code causes arbitrary delegate to execute
/// asynchronously, also calls critical code.
/// Safe: Executing the delegate asynchronously is OK because we
/// capture the ExecutionContext inside the DispatcherOperation.
/// </SecurityNote>
[SecuritySafeCritical]
private DispatcherOperation LegacyBeginInvokeImpl(DispatcherPriority priority, Delegate method, object args, int numArgs)
{
ValidatePriority(priority, "priority");
if(method == null)
{
throw new ArgumentNullException("method");
}
DispatcherOperation operation = new DispatcherOperation(this, method, priority, args, numArgs);
InvokeAsyncImpl(operation, CancellationToken.None);
return operation;
}
/// <SecurityNote>
/// Critical:This code causes arbitrary delegate to execute asynchronously, also calls critical code.
/// </SecurityNote>
[SecurityCritical]
private void InvokeAsyncImpl(DispatcherOperation operation, CancellationToken cancellationToken)
{
DispatcherHooks hooks = null;
bool succeeded = false;
// Could be a non-dispatcher thread, lock to read
lock(_instanceLock)
{
if (!cancellationToken.IsCancellationRequested &&
!_hasShutdownFinished &&
!Environment.HasShutdownStarted)
{
// Add the operation to the work queue
operation._item = _queue.Enqueue(operation.Priority, operation);
// Make sure we will wake up to process this operation.
succeeded = RequestProcessing();
if (succeeded)
{
// Grab the hooks to use inside the lock; but we will
// call them below, outside of the lock.
hooks = _hooks;
}
else
{
// Dequeue the item since we failed to request
// processing for it. Note we will mark it aborted
// below.
_queue.RemoveItem(operation._item);
}
}
}
if (succeeded == true)
{
// We have enqueued the operation. Register a callback
// with the cancellation token to abort the operation
// when cancellation is requested.
if(cancellationToken.CanBeCanceled)
{
CancellationTokenRegistration cancellationRegistration = cancellationToken.Register(s => ((DispatcherOperation)s).Abort(), operation);
// Revoke the cancellation when the operation is done.
operation.Aborted += (s,e) => cancellationRegistration.Dispose();
operation.Completed += (s,e) => cancellationRegistration.Dispose();
}
if(hooks != null)
{
hooks.RaiseOperationPosted(this, operation);
}
if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info))
{
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientUIContextPost, EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, operation.Priority, operation.Name, operation.Id);
}
}
else
{
// We failed to enqueue the operation, and the caller that
// created the operation does not expose it before we return,
// so it is safe to modify the operation outside of the lock.
// Just mark the operation as aborted, which we can safely
// return to the user.
operation._status = DispatcherOperationStatus.Aborted;
operation._taskSource.SetCanceled();
}
}
/// <summary>
/// Executes the specified delegate synchronously on the thread that
/// the Dispatcher was created on.
/// </summary>
/// <param name="priority">
/// The priority that determines in what order the specified method
/// is invoked relative to the other pending methods in the Dispatcher.
/// </param>
/// <param name="method">
/// A delegate to a method that takes no parameters.
/// </param>
/// <returns>
/// The return value from the delegate being invoked, or null if
/// the delegate has no return value.
/// </returns>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public object Invoke(DispatcherPriority priority, Delegate method)
{
return LegacyInvokeImpl(priority, TimeSpan.FromMilliseconds(-1), method, null, 0);
}
/// <summary>
/// Executes the specified delegate synchronously with the specified
/// arguments, on the thread that the Dispatcher was created on.
/// </summary>
/// <param name="priority">
/// The priority that determines in what order the specified method
/// is invoked relative to the other pending methods in the Dispatcher.
/// </param>
/// <param name="method">
/// A delegate to a method that takes parameters of the same number
/// and type that are contained in the args parameter.
/// </param>
/// <param name="arg">
/// An object to pass as an argument to the given method.
/// This can be null if no arguments are needed.
/// </param>
/// <returns>
/// The return value from the delegate being invoked, or null if
/// the delegate has no return value.
/// </returns>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public object Invoke(DispatcherPriority priority, Delegate method, object arg)
{
return LegacyInvokeImpl(priority, TimeSpan.FromMilliseconds(-1), method, arg, 1);
}
/// <summary>
/// Executes the specified delegate synchronously with the specified
/// arguments, on the thread that the Dispatcher was created on.
/// </summary>
/// <param name="priority">
/// The priority that determines in what order the specified method
/// is invoked relative to the other pending methods in the Dispatcher.
/// </param>
/// <param name="method">
/// A delegate to a method that takes parameters of the same number
/// and type that are contained in the args parameter.
/// </param>
/// <param name="arg">
/// </param>
/// <param name="args">
/// An array of objects to pass as arguments to the given method.
/// This can be null if no arguments are needed.
/// </param>
/// <returns>
/// The return value from the delegate being invoked, or null if
/// the delegate has no return value.
/// </returns>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public object Invoke(DispatcherPriority priority, Delegate method, object arg, params object[] args)
{
return LegacyInvokeImpl(priority, TimeSpan.FromMilliseconds(-1), method, CombineParameters(arg, args), -1);
}
/// <summary>
/// Executes the specified delegate synchronously on the thread that
/// the Dispatcher was created on.
/// </summary>
/// <param name="priority">
/// The priority that determines in what order the specified method
/// is invoked relative to the other pending methods in the Dispatcher.
/// </param>
/// <param name="timeout">
/// The minimum amount of time to wait for the operation to start.
/// Once the operation has started, it will complete before this method
/// returns.
/// </param>
/// <param name="method">
/// A delegate to a method that takes no parameters.
/// </param>
/// <returns>
/// The return value from the delegate being invoked, or null if
/// the delegate has no return value.
/// </returns>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public object Invoke(DispatcherPriority priority, TimeSpan timeout, Delegate method)
{
return LegacyInvokeImpl(priority, timeout, method, null, 0);
}
/// <summary>
/// Executes the specified delegate synchronously with the specified
/// arguments, on the thread that the Dispatcher was created on.
/// </summary>
/// <param name="priority">
/// The priority that determines in what order the specified method
/// is invoked relative to the other pending methods in the Dispatcher.
/// </param>
/// <param name="timeout">
/// The minimum amount of time to wait for the operation to start.
/// Once the operation has started, it will complete before this method
/// returns.
/// </param>
/// <param name="method">
/// A delegate to a method that takes parameters of the same number
/// and type that are contained in the args parameter.
/// </param>
/// <param name="arg">
/// An object to pass as an argument to the given method.
/// This can be null if no arguments are needed.
/// </param>
/// <returns>
/// The return value from the delegate being invoked, or null if
/// the delegate has no return value.
/// </returns>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public object Invoke(DispatcherPriority priority, TimeSpan timeout, Delegate method, object arg)
{
return LegacyInvokeImpl(priority, timeout, method, arg, 1);
}
/// <summary>
/// Executes the specified delegate synchronously with the specified
/// arguments, on the thread that the Dispatcher was created on.
/// </summary>
/// <param name="priority">
/// The priority that determines in what order the specified method
/// is invoked relative to the other pending methods in the Dispatcher.
/// </param>
/// <param name="timeout">
/// The minimum amount of time to wait for the operation to start.
/// Once the operation has started, it will complete before this method
/// returns.
/// </param>
/// <param name="method">
/// A delegate to a method that takes parameters of the same number
/// and type that are contained in the args parameter.
/// </param>
/// <param name="arg">
/// </param>
/// <param name="args">
/// An array of objects to pass as arguments to the given method.
/// This can be null if no arguments are needed.
/// </param>
/// <returns>
/// The return value from the delegate being invoked, or null if
/// the delegate has no return value.
/// </returns>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public object Invoke(DispatcherPriority priority, TimeSpan timeout, Delegate method, object arg, params object[] args)
{
return LegacyInvokeImpl(priority, timeout, method, CombineParameters(arg, args), -1);
}
/// <summary>
/// Executes the specified delegate synchronously on the thread that
/// the Dispatcher was created on.
/// </summary>
/// <param name="method">
/// A delegate to a method that takes parameters of the same number
/// and type that are contained in the args parameter.
/// </param>
/// <param name="args">
/// An array of objects to pass as arguments to the given method.
/// This can be null if no arguments are needed.
/// </param>
/// <returns>
/// The return value from the delegate being invoked, or null if
/// the delegate has no return value.
/// </returns>
public object Invoke(Delegate method, params object[] args)
{
return LegacyInvokeImpl(DispatcherPriority.Normal, TimeSpan.FromMilliseconds(-1), method, args, -1);
}
/// <summary>
/// Executes the specified delegate synchronously on the thread that
/// the Dispatcher was created on.
/// </summary>
/// <param name="method">
/// A delegate to a method that takes parameters of the same number
/// and type that are contained in the args parameter.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified method
/// is invoked relative to the other pending methods in the Dispatcher.
/// </param>
/// <param name="args">
/// An array of objects to pass as arguments to the given method.
/// This can be null if no arguments are needed.
/// </param>
/// <returns>
/// The return value from the delegate being invoked, or null if
/// the delegate has no return value.
/// </returns>
public object Invoke(Delegate method, DispatcherPriority priority, params object[] args)
{
return LegacyInvokeImpl(priority, TimeSpan.FromMilliseconds(-1), method, args, -1);
}
/// <summary>
/// Executes the specified delegate synchronously on the thread that
/// the Dispatcher was created on.
/// </summary>
/// <param name="method">
/// A delegate to a method that takes parameters of the same number
/// and type that are contained in the args parameter.
/// </param>
/// <param name="timeout">
/// The minimum amount of time to wait for the operation to start.
/// Once the operation has started, it will complete before this method
/// returns.
/// </param>
/// <param name="args">
/// An array of objects to pass as arguments to the given method.
/// This can be null if no arguments are needed.
/// </param>
/// <returns>
/// The return value from the delegate being invoked, or null if
/// the delegate has no return value.
/// </returns>
public object Invoke(Delegate method, TimeSpan timeout, params object[] args)
{
return LegacyInvokeImpl(DispatcherPriority.Normal, timeout, method, args, -1);
}
/// <summary>
/// Executes the specified delegate synchronously on the thread that
/// the Dispatcher was created on.
/// </summary>
/// <param name="method">
/// A delegate to a method that takes parameters of the same number
/// and type that are contained in the args parameter.
/// </param>
/// <param name="timeout">
/// The minimum amount of time to wait for the operation to start.
/// Once the operation has started, it will complete before this method
/// returns.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified method
/// is invoked relative to the other pending methods in the Dispatcher.
/// </param>
/// <param name="args">
/// An array of objects to pass as arguments to the given method.
/// This can be null if no arguments are needed.
/// </param>
/// <returns>
/// The return value from the delegate being invoked, or null if
/// the delegate has no return value.
/// </returns>
public object Invoke(Delegate method, TimeSpan timeout, DispatcherPriority priority, params object[] args)
{
return LegacyInvokeImpl(priority, timeout, method, args, -1);
}
/// <SecurityNote>
/// Critical:This code causes arbitrary delegate to execute
/// asynchronously, also calls critical code.
/// Safe: Executing the delegate asynchronously is OK because we
/// capture the ExecutionContext inside the DispatcherOperation.
/// </SecurityNote>
[SecuritySafeCritical]
internal object LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, object args, int numArgs)
{
ValidatePriority(priority, "priority");
if(priority == DispatcherPriority.Inactive)
{
throw new ArgumentException(SR.Get(SRID.InvalidPriority), "priority");
}
if(method == null)
{
throw new ArgumentNullException("method");
}
if( timeout.TotalMilliseconds < 0 &&
timeout != TimeSpan.FromMilliseconds(-1))
{
if(CheckAccess())
{
// Application Compat
// In versions before 4.5, when invoking on the same
// thread, any negative timeout was effectively an
// infinite wait. When invoking across threads, any
// negative timeout other than -1ms was an error which
// threw an exception internally when waiting on an event.
timeout = TimeSpan.FromMilliseconds(-1);
}
else
{
throw new ArgumentOutOfRangeException("timeout");
}
}
// Fast-Path: if on the same thread, and invoking at Send priority,
// then just call the callback directly within the exception wrappers.
if(priority == DispatcherPriority.Send && CheckAccess())
{
SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current;
try
{
DispatcherSynchronizationContext newSynchronizationContext;
if(BaseCompatibilityPreferences.GetReuseDispatcherSynchronizationContextInstance())
{
newSynchronizationContext = _defaultDispatcherSynchronizationContext;
}
else
{
if(BaseCompatibilityPreferences.GetFlowDispatcherSynchronizationContextPriority())
{
newSynchronizationContext = new DispatcherSynchronizationContext(this, priority);
}
else
{
newSynchronizationContext = new DispatcherSynchronizationContext(this, DispatcherPriority.Normal);
}
}
SynchronizationContext.SetSynchronizationContext(newSynchronizationContext);
return WrappedInvoke(method, args, numArgs, null);
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext);
}
}
// Slow-Path: go through the queue.
DispatcherOperation operation = new DispatcherOperation(this, method, priority, args, numArgs);
return InvokeImpl(operation, CancellationToken.None, timeout);
}
/// <SecurityNote>
/// Critical:This code causes arbitrary delegate to execute asynchronously, also calls critical code.
/// </SecurityNote>
[SecurityCritical]
private object InvokeImpl(DispatcherOperation operation, CancellationToken cancellationToken, TimeSpan timeout)
{
object result = null;
Debug.Assert(timeout.TotalMilliseconds >= 0 || timeout == TimeSpan.FromMilliseconds(-1));
Debug.Assert(operation.Priority != DispatcherPriority.Send || !CheckAccess()); // should be handled by caller
if(!cancellationToken.IsCancellationRequested)
{
// This operation must be queued since it was invoked either to
// another thread, or at a priority other than Send.
InvokeAsyncImpl(operation, cancellationToken);
CancellationToken ctTimeout = CancellationToken.None;
CancellationTokenRegistration ctTimeoutRegistration = new CancellationTokenRegistration();
CancellationTokenSource ctsTimeout = null;
if(timeout.TotalMilliseconds >= 0)
{
// Create a CancellationTokenSource that will abort the
// operation after the timeout. Note that this does not
// cancel the operation, just abort it if it is still pending.
ctsTimeout = new CancellationTokenSource(timeout);
ctTimeout = ctsTimeout.Token;
ctTimeoutRegistration = ctTimeout.Register(s => ((DispatcherOperation)s).Abort(), operation);
}
// We have already registered with the cancellation tokens
// (both provided by the user, and one for the timeout) to
// abort the operation when they are canceled. If the
// operation has already started when the timeout expires,
// we still wait for it to complete. This is different
// than simply waiting on the operation with a timeout
// because we are the ones queueing the dispatcher
// operation, not the caller. We can't leave the operation
// in a state that it might execute if we return that it did not
// invoke.
try
{
operation.Wait();
Debug.Assert(operation.Status == DispatcherOperationStatus.Completed ||
operation.Status == DispatcherOperationStatus.Aborted);
// Old async semantics return from Wait without
// throwing an exception if the operation was aborted.
// There is no need to test the timout condition, since
// the old async semantics would just return the result,
// which would be null.
// This should not block because either the operation
// is using the old async sematics, or the operation
// completed successfully.
result = operation.Result;
}
catch(OperationCanceledException)
{
Debug.Assert(operation.Status == DispatcherOperationStatus.Aborted);
// New async semantics will throw an exception if the
// operation was aborted. Here we convert that
// exception into a timeout exception if the timeout
// has expired (admittedly a weak relationship
// assuming causality).
if (ctTimeout.IsCancellationRequested)
{
// The operation was canceled because of the
// timeout, throw a TimeoutException instead.
throw new TimeoutException();
}
else
{
// The operation was canceled from some other reason.
throw;
}
}
finally
{
ctTimeoutRegistration.Dispose();
if (ctsTimeout != null)
{
ctsTimeout.Dispose();
}
}
}
return result;
}
/// <summary>
/// Disable the event processing of the dispatcher.
/// </summary>
/// <remarks>
/// This is an advanced method intended to elliminate the chance of
/// unrelated reentrancy. The effect of disabling processing is:
/// 1) CLR locks will not pump messages internally.
/// 2) No one is allowed to push a frame.
/// 3) No message processing is permitted.
/// </remarks>
public DispatcherProcessingDisabled DisableProcessing()
{
VerifyAccess();
// Turn off processing.
_disableProcessingCount++;
DispatcherProcessingDisabled dpd = new DispatcherProcessingDisabled();
dpd._dispatcher = this;
return dpd;
}
/*
/// <summary>
/// Reports the range of priorities that are considered
/// as foreground priorities.
/// </summary>
/// <remarks>
/// A foreground priority is processed before input.
/// </remarks>
public static PriorityRange ForegroundPriorityRange
{
get
{
return _foregroundPriorityRange;
}
}
/// <summary>
/// Reports the range of priorities that are considered
/// as background priorities.
/// </summary>
/// <remarks>
/// A background priority is processed after input.
/// </remarks>
public static PriorityRange BackgroundPriorityRange
{
get
{
return _backgroundPriorityRange;
}
}
/// <summary>
/// Reports the range of priorities that are considered
/// as idle priorities.
/// </summary>
/// <remarks>
/// An idle priority is processed periodically after background
/// priorities have been processed.
/// </remarks>
public static PriorityRange IdlePriorityRange
{
get
{
return _idlePriorityRange;
}
}
/// <summary>
/// Represents a convenient foreground priority.
/// </summary>
/// <remarks>
/// A foreground priority is processed before input. In general
/// you should define your own foreground priority to allow for
/// more fine-grained ordering of queued items.
/// </remarks>
public static Priority ForegroundPriority
{
get
{
return _foregroundPriority;
}
}
/// <summary>
/// Represents a convenient background priority.
/// </summary>
/// <remarks>
/// A background priority is processed after input. In general you
/// should define your own background priority to allow for more
/// fine-grained ordering of queued items.
/// </remarks>
public static Priority BackgroundPriority
{
get
{
return _backgroundPriority;
}
}
/// <summary>
/// Represents a convenient idle priority.
/// </summary>
/// <remarks>
/// An idle priority is processed periodically after background
/// priorities have been processed. In general you should define
/// your own idle priority to allow for more fine-grained ordering
/// of queued items.
/// </remarks>
public static Priority IdlePriority
{
get
{
return _idlePriority;
}
}
*/
/// <summary>
/// Validates that a priority is suitable for use by the dispatcher.
/// </summary>
/// <param name="priority">
/// The priority to validate.
/// </param>
/// <param name="parameterName">
/// The name if the argument to report in the ArgumentException
/// that is raised if the priority is not suitable for use by
/// the dispatcher.
/// </param>
public static void ValidatePriority(DispatcherPriority priority, string parameterName) // NOTE: should be Priority
{
// First make sure the Priority is valid.
// Priority.ValidatePriority(priority, paramName);
// Second, make sure the priority is in a range recognized by
// the dispatcher.
if(!_foregroundPriorityRange.Contains(priority) &&
!_backgroundPriorityRange.Contains(priority) &&
!_idlePriorityRange.Contains(priority) &&
DispatcherPriority.Inactive != priority) // NOTE: should be Priority.Min
{
// If we move to a Priority class, this exception will have to change too.
throw new System.ComponentModel.InvalidEnumArgumentException(parameterName, (int)priority, typeof(DispatcherPriority));
}
}
/// <summary>
/// Checks that the calling thread has access to this object.
/// </summary>
/// <remarks>
/// Only the dispatcher thread may access DispatcherObjects.
/// <p/>
/// This method is public so that any thread can probe to
/// see if it has access to the DispatcherObject.
/// Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
/// </remarks>
/// <returns>
/// True if the calling thread has access to this object.
/// </returns>
/// <SecurityNote>
/// Critical: Accesses _hooks, which is critical.
/// TreatAsSafe: link-demands
/// </SecurityNote>
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)]
public DispatcherHooks Hooks
{
[SecurityCritical]
[UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
get
{
DispatcherHooks hooks = null;
lock(_instanceLock)
{
if(_hooks == null)
{
_hooks = new DispatcherHooks();
}
hooks = _hooks;
}
return hooks;
}
}
/// <summary>
/// Occurs when an untrapped thread exception is thrown.
/// </summary>
/// <remarks>
/// Raised during the filter stage for an exception raised during
/// execution of a delegate via Invoke or BeginInvoke.
/// <P/>
/// The callstack is not unwound at this time (first-chance exception).
/// <P/>
/// Listeners to this event must be written with care to avoid
/// creating secondary exceptions and to catch any that occur.
/// It is recommended to avoid allocating memory or doing any
/// heavylifting if possible.
/// Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
/// </remarks>
/// <SecurityNote>
/// Critical: partially-trusted code is not allowed to access our exception filter.
/// TreatAsSafe: link-demands
/// </SecurityNote>
public event DispatcherUnhandledExceptionFilterEventHandler UnhandledExceptionFilter
{
[SecurityCritical]
[UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
add
{
_unhandledExceptionFilter += value;
}
[SecurityCritical]
[UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
remove
{
_unhandledExceptionFilter -= value;
}
}
/// <summary>
/// Occurs when an untrapped thread exception is thrown.
/// </summary>
/// <remarks>
/// Raised when an exception was caught that was raised during
/// execution of a delegate via Invoke or BeginInvoke.
/// <P/>
/// A handler can mark the exception as handled which will prevent
/// the internal "final" exception handler from being called.
/// <P/>
/// Listeners to this event must be written with care to avoid
/// creating secondary exceptions and to catch any that occur.
/// It is recommended to avoid allocating memory or doing any
/// heavylifting if possible.
/// </remarks>
public event DispatcherUnhandledExceptionEventHandler UnhandledException;
/// <summary>
/// Reserved Dispatcher member
/// </summary>
internal object Reserved0
{
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
get { return _reserved0; }
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
set { _reserved0 = value; }
}
/// <summary>
/// Reserved Dispatcher member
/// </summary>
internal object Reserved1
{
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
get { return _reserved1; }
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
set { _reserved1 = value; }
}
/// <summary>
/// Reserved Dispatcher member
/// </summary>
internal object Reserved2
{
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
get { return _reserved2; }
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
set { _reserved2 = value; }
}
/// <summary>
/// Reserved Dispatcher member
/// </summary>
internal object Reserved3
{
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
get { return _reserved3; }
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
set { _reserved3 = value; }
}
/// <summary>
/// Reserved Dispatcher member
/// </summary>
internal object Reserved4
{
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
get { return _reserved4; }
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
set { _reserved4 = value; }
}
/// <summary>
/// Reserved Dispatcher member for PtsCache
/// </summary>
internal object PtsCache
{
// This gets multiplexed with the log for "request processing" failures.
// See OnRequestProcessingFailure. (DDVSO 127745)
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
get
{
if (!_hasRequestProcessingFailed)
return _reservedPtsCache;
Tuple<Object, List<String>> tuple = _reservedPtsCache as Tuple<Object, List<String>>;
if (tuple == null)
return _reservedPtsCache;
else
return tuple.Item1;
}
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
set
{
if (!_hasRequestProcessingFailed)
_reservedPtsCache = value;
else
{
Tuple<Object, List<String>> tuple = _reservedPtsCache as Tuple<Object, List<String>>;
List<String> list = (tuple != null) ? tuple.Item2 : new List<String>();
_reservedPtsCache = new Tuple<Object, List<String>>(value, list);
}
}
}
internal object InputMethod
{
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
get { return _reservedInputMethod; }
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
set { _reservedInputMethod = value; }
}
/// <SecurityNote>
/// Critical: Since it hands out the InputManager
/// </SecurityNote>
internal object InputManager
{
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
[SecurityCritical]
get { return _reservedInputManager; }
[FriendAccessAllowed] // Built into Base, used by Core or Framework.
[SecurityCritical]
set { _reservedInputManager = value; }
}
///<SecurityNote>
/// Critical: Does an elevation via an unsafeNativeMethods call
/// TreatAsSafe: stores critical data in SecurityCritical wrapper
///</SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private Dispatcher()
{
_queue = new PriorityQueue<DispatcherOperation>();
_tlsDispatcher = this; // use TLS for ownership only
_dispatcherThread = Thread.CurrentThread;
// Add ourselves to the map of dispatchers to threads.
lock(_globalLock)
{
_dispatchers.Add(new WeakReference(this));
}
_unhandledExceptionEventArgs = new DispatcherUnhandledExceptionEventArgs(this);
_exceptionFilterEventArgs = new DispatcherUnhandledExceptionFilterEventArgs(this);
_defaultDispatcherSynchronizationContext = new DispatcherSynchronizationContext(this);
// Create the message-only window we use to receive messages
// that tell us to process the queue.
MessageOnlyHwndWrapper window = new MessageOnlyHwndWrapper();
_window = new SecurityCriticalData<MessageOnlyHwndWrapper>( window );
_hook = new HwndWrapperHook(WndProcHook);
_window.Value.AddHook(_hook);
// DDVSO:447590
// Verify that the accessibility switches are set prior to any major UI code running.
AccessibilitySwitches.VerifySwitches(this);
}
// creates a "sentinel" dispatcher. It doesn't do anything, and should never
// be called except for CheckAccess and VerifyAccess (which fail).
// See DispatcherObject.MakeSentinel() for more.
// [The 'isSentinel' parameter is ignored - it only serves to distinguish
// this ctor from others.]
internal Dispatcher(bool isSentinel)
{
Debug.Assert(isSentinel, "this ctor is only for creating a 'sentinel' dispatcher");
// set thread so that CheckAccess and VerifyAccess fail
_dispatcherThread = null;
// set other members so that incoming calls (which shouldn't happen)
// do as little as possible
_startingShutdown = true;
_hasShutdownStarted = true;
_hasShutdownFinished = true;
}
///<SecurityNote>
/// Critical - it calls critical methods (ShutdownImpl). it can initiate a shutdown process, disabled
/// in partial trust.
///</SecurityNote>
[SecurityCritical]
private void StartShutdownImpl()
{
if(!_startingShutdown)
{
// We only need this to prevent reentrancy if the ShutdownStarted event
// tries to shut down again.
_startingShutdown = true;
// Call the ShutdownStarted event before we actually mark ourselves
// as shutting down. This is so the handlers can actaully do work
// when they get this event without throwing exceptions.
if(ShutdownStarted != null)
{
ShutdownStarted(this, EventArgs.Empty);
}
_hasShutdownStarted = true;
// Because we may have to defer the actual shutting-down until
// later, we need to remember the execution context we started
// the shutdown from.
//
// Note that we demanded permissions when BeginInvokeShutdown
// or InvokeShutdown were called. So if there were not enough
// permissions, we would have thrown then.
//
CulturePreservingExecutionContext shutdownExecutionContext = CulturePreservingExecutionContext.Capture();
_shutdownExecutionContext = new SecurityCriticalDataClass<CulturePreservingExecutionContext>(shutdownExecutionContext);
// Tell Win32 to exit the message loop for this thread.
// NOTE: I removed this code because of bug 1062099.
//
// UnsafeNativeMethods.PostQuitMessage(0);
if(_frameDepth > 0)
{
// If there are any frames running, we have to wait for them
// to unwind before we can safely destroy the dispatcher.
}
else
{
// The current thread is not spinning inside of the Dispatcher,
// so we can go ahead and destroy it.
ShutdownImpl();
}
}
}
//<SecurityNote>
// Critical - Calls ShutdownImplInSecurityContext with the execution context that was
// active when the shutdown was initiated.
//</SecurityNote>
[SecurityCritical]
private void ShutdownImpl()
{
if(!_hasShutdownFinished) // Dispatcher thread - no lock needed for read
{
if(_shutdownExecutionContext != null && _shutdownExecutionContext.Value != null)
{
// Continue using the execution context that was active when the shutdown
// was initiated.
CulturePreservingExecutionContext.Run(_shutdownExecutionContext.Value, new ContextCallback(ShutdownImplInSecurityContext), null);
}
else
{
// It is possible to be called from WM_DESTROY, in which case no one has begun
// the shutdown process, so there is no execution context to use.
ShutdownImplInSecurityContext(null);
}
_shutdownExecutionContext = null;
}
}
//<SecurityNote>
// Critical - as it accesses security critical data ( window handle)
//</SecurityNote>
[SecurityCritical]
private void ShutdownImplInSecurityContext(Object state)
{
// Call the ShutdownFinished event before we actually mark ourselves
// as shut down. This is so the handlers can actaully do work
// when they get this event without throwing exceptions.
if(ShutdownFinished != null)
{
ShutdownFinished(this, EventArgs.Empty);
}
// Destroy the message-only window we use to process Win32 messages
//
// Note: we need to do this BEFORE we actually mark the dispatcher
// as shutdown. This is because the window will need the dispatcher
// to execute the window proc.
MessageOnlyHwndWrapper window = null;
lock(_instanceLock)
{
window = _window.Value;
_window = new SecurityCriticalData<MessageOnlyHwndWrapper>(null);
}
window.Dispose();
// Mark this dispatcher as shut down. Attempts to BeginInvoke
// or Invoke will result in an exception.
lock(_instanceLock)
{
_hasShutdownFinished = true; // Dispatcher thread - lock to write
}
// Now that the queue is off-line, abort all pending operations,
// including inactive ones.
DispatcherOperation operation = null;
do
{
lock(_instanceLock)
{
if(_queue.MaxPriority != DispatcherPriority.Invalid)
{
operation = _queue.Peek();
}
else
{
operation = null;
}
}
if(operation != null)
{
operation.Abort();
}
} while(operation != null);
// clear out the fields that could be holding onto large graphs of objects.
lock(_instanceLock)
{
// We should not need the queue any more.
_queue = null;
// We should not need the timers any more.
_timers = null;
// Clear out the reserved fields.
_reserved0 = null;
_reserved1 = null;
_reserved2 = null;
_reserved3 = null;
_reserved4 = null;
// _reservedPtsCache = null; // PTS needs this in a finalizer... the PTS code should not assume access to this in their finalizer.
_reservedInputMethod = null;
_reservedInputManager = null;
}
// Note: the Dispatcher is still held in TLS. This maintains the 1-1 relationship
// between the thread and the Dispatcher. However the dispatcher is basically
// dead - it has been marked as _hasShutdownFinished, and most operations are
// now illegal.
}
// Returns whether or not the priority was set.
//[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647
/// <SecurityNote>
/// Critical: accesses _hooks
/// TreatAsSafe: does not expose _hooks
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
internal bool SetPriority(DispatcherOperation operation, DispatcherPriority priority) // NOTE: should be Priority
{
bool notify = false;
DispatcherHooks hooks = null;
lock(_instanceLock)
{
if(_queue != null && operation._item.IsQueued)
{
_queue.ChangeItemPriority(operation._item, priority);
notify = true;
if(notify)
{
// Make sure we will wake up to process this operation.
RequestProcessing();
hooks = _hooks;
}
}
}
if (notify)
{
if(hooks != null)
{
hooks.RaiseOperationPriorityChanged(this, operation);
}
if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info))
{
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientUIContextPromote, EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, priority, operation.Name, operation.Id);
}
}
return notify;
}
// Returns whether or not the operation was removed.
//[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647
/// <SecurityNote>
/// Critical: accesses _hooks
/// TreatAsSafe: does not expose _hooks
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
internal bool Abort(DispatcherOperation operation)
{
bool notify = false;
DispatcherHooks hooks = null;
lock(_instanceLock)
{
if(_queue != null && operation._item.IsQueued)
{
_queue.RemoveItem(operation._item);
operation._status = DispatcherOperationStatus.Aborted;
notify = true;
hooks = _hooks;
}
}
if (notify)
{
if(hooks != null)
{
hooks.RaiseOperationAborted(this, operation);
}
if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info))
{
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientUIContextAbort, EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, operation.Priority, operation.Name, operation.Id);
}
}
return notify;
}
//[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647
/// <SecurityNote>
/// Critical: This code can be used to process input and calls into DispatcherOperation.Invoke which
/// is critical
/// </SecurityNote>
[SecurityCritical]
private void ProcessQueue()
{
DispatcherPriority maxPriority = DispatcherPriority.Invalid; // NOTE: should be Priority.Invalid
DispatcherOperation op = null;
DispatcherHooks hooks = null;
//
// Dequeue the next operation if appropriate.
lock(_instanceLock)
{
_postedProcessingType = PROCESS_NONE;
// We can only do background processing if there is
// no input in the Win32 queue.
bool backgroundProcessingOK = !IsInputPending();
maxPriority = _queue.MaxPriority;
if(maxPriority != DispatcherPriority.Invalid && // Nothing. NOTE: should be Priority.Invalid
maxPriority != DispatcherPriority.Inactive) // Not processed. // NOTE: should be Priority.Min
{
if(_foregroundPriorityRange.Contains(maxPriority) || backgroundProcessingOK)
{
op = _queue.Dequeue();
hooks = _hooks;
}
}
// Hm... we are grabbing this here... but it could change while we are invoking
// the operation... maybe we should move this code to after the invoke?
maxPriority = _queue.MaxPriority;
// If there is more to do, request processing for it.
RequestProcessing();
}
if(op != null)
{
bool eventlogged = false;
if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info))
{
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientUIContextDispatchBegin, EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, op.Priority, op.Name, op.Id);
eventlogged = true;
}
if(hooks != null)
{
hooks.RaiseOperationStarted(this, op);
}
op.Invoke();
if(hooks != null)
{
hooks.RaiseOperationCompleted(this, op);
}
if (eventlogged)
{
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientUIContextDispatchEnd, EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info);
if (_idlePriorityRange.Contains(maxPriority))
{
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientUIContextIdle, EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info);
}
}
// All done, ready for reentrancy in case the completions are inlined.
op.InvokeCompletions();
}
}
internal delegate void ShutdownCallback();
///<SecurityNote>
/// Critical - it calls critical methods (StartShutdownImpl). it can initiate a shutdown process, disabled
/// in partial trust.
///</SecurityNote>
[SecurityCritical]
private void ShutdownCallbackInternal()
{
StartShutdownImpl();
}
//<SecurityNote>
// Critical - as this calls critical methods (GetMessage, TranslateMessage, DispatchMessage).
// TreatAsSafe - as the critical method is not leaked out, and not controlled by external inputs.
//</SecurityNote>
[SecurityCritical, SecurityTreatAsSafe ]
private void PushFrameImpl(DispatcherFrame frame)
{
SynchronizationContext oldSyncContext = null;
SynchronizationContext newSyncContext = null;
MSG msg = new MSG();
_frameDepth++;
try
{
// Change the CLR SynchronizationContext to be compatable with our Dispatcher.
oldSyncContext = SynchronizationContext.Current;
newSyncContext = new DispatcherSynchronizationContext(this);
SynchronizationContext.SetSynchronizationContext(newSyncContext);
try
{
while(frame.Continue)
{
if (!GetMessage(ref msg, IntPtr.Zero, 0, 0))
break;
TranslateAndDispatchMessage(ref msg);
}
// If this was the last frame to exit after a quit, we
// can now dispose the dispatcher.
if(_frameDepth == 1)
{
if(_hasShutdownStarted)
{
ShutdownImpl();
}
}
}
finally
{
// Restore the old SynchronizationContext.
SynchronizationContext.SetSynchronizationContext(oldSyncContext);
}
}
finally
{
_frameDepth--;
if(_frameDepth == 0)
{
// We have exited all frames.
_exitAllFrames = false;
}
}
}
//<SecurityNote>
// SecurityCritical - as this does unsafe operations.
//</SecurityNote>
[SecurityCritical]
private bool GetMessage(ref MSG msg, IntPtr hwnd, int minMessage, int maxMessage)
{
// If Any TextServices for Cicero is not installed GetMessagePump() returns null.
// If TextServices are there, we can get ITfMessagePump and have to use it instead of
// Win32 GetMessage().
bool result;
UnsafeNativeMethods.ITfMessagePump messagePump = GetMessagePump();
try
{
if (messagePump == null)
{
// We have foreground items to process.
// By posting a message, Win32 will service us fairly promptly.
result = UnsafeNativeMethods.GetMessageW(ref msg,
new HandleRef(this, hwnd),
minMessage,
maxMessage);
}
else
{
int intResult;
messagePump.GetMessageW(
ref msg,
hwnd,
minMessage,
maxMessage,
out intResult);
if (intResult == -1)
{
throw new Win32Exception();
}
else if (intResult == 0)
{
result = false;
}
else
{
result = true;
}
}
}
finally
{
if (messagePump != null) Marshal.ReleaseComObject(messagePump);
}
return result;
}
// Get ITfMessagePump interface from Cicero.
/// <SecurityNote>
/// Critical - calls critical code, created objects deal with raw input
/// </SecurityNote>
[SecurityCritical]
private UnsafeNativeMethods.ITfMessagePump GetMessagePump()
{
UnsafeNativeMethods.ITfMessagePump messagePump = null;
if (_isTSFMessagePumpEnabled)
{
// If the current thread is not STA, Cicero just does not work.
// Probably this Dispatcher is running for worker thread.
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
{
// If there is no text services, we don't have to use ITfMessagePump.
if (TextServicesLoader.ServicesInstalled)
{
UnsafeNativeMethods.ITfThreadMgr threadManager;
threadManager = TextServicesLoader.Load();
// ThreadManager does not exist. No MessagePump yet.
if (threadManager != null)
{
// QI ITfMessagePump.
messagePump = threadManager as UnsafeNativeMethods.ITfMessagePump;
}
}
}
}
return messagePump;
}
/// <summary>
/// Enables/disables ITfMessagePump handshake with Text Services Framework.
/// </summary>
/// <remarks>
/// PresentationCore's TextServicesManager sets this property false when
/// no WPF element has focus. This is important to ensure that native
/// controls receive unfiltered input.
/// </remarks>
[FriendAccessAllowed] // Used by TextServicesManager in PresentationCore.
internal bool IsTSFMessagePumpEnabled
{
set
{
_isTSFMessagePumpEnabled = value;
}
}
//<SecurityNote>
// SecurityCritical - as this does unsafe operations.
//</SecurityNote>
[SecurityCritical]
private void TranslateAndDispatchMessage(ref MSG msg)
{
bool handled = false;
handled = ComponentDispatcher.RaiseThreadMessage(ref msg);
if(!handled)
{
UnsafeNativeMethods.TranslateMessage(ref msg);
UnsafeNativeMethods.DispatchMessage(ref msg);
}
}
//<SecurityNote>
// Critical - as it accesses security critical data ( window handle)
//</SecurityNote>
[SecurityCritical]
private IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
WindowMessage message = (WindowMessage)msg;
if(_disableProcessingCount > 0)
{
throw new InvalidOperationException(SR.Get(SRID.DispatcherProcessingDisabledButStillPumping));
}
if(message == WindowMessage.WM_DESTROY)
{
if(!_hasShutdownStarted && !_hasShutdownFinished) // Dispatcher thread - no lock needed for read
{
// Aack! We are being torn down rudely! Try to
// shut the dispatcher down as nicely as we can.
ShutdownImpl();
}
}
else if(message == _msgProcessQueue)
{
ProcessQueue();
}
else if(message == WindowMessage.WM_TIMER && (int) wParam == TIMERID_BACKGROUND)
{
// This timer is just used to process background operations.
// Stop the timer so that it doesn't fire again.
SafeNativeMethods.KillTimer(new HandleRef(this, hwnd), TIMERID_BACKGROUND);
ProcessQueue();
}
else if(message == WindowMessage.WM_TIMER && (int) wParam == TIMERID_TIMERS)
{
// We want 1-shot only timers. So stop the timer
// that just fired.
KillWin32Timer();
PromoteTimers(Environment.TickCount);
}
// We are about to return to the OS. If there is nothing left
// to do in the queue, then we will effectively go to sleep.
// This is the condition that means Idle.
DispatcherHooks hooks = null;
bool idle = false;
lock(_instanceLock)
{
idle = (_postedProcessingType < PROCESS_BACKGROUND);
if (idle)
{
hooks = _hooks;
}
}
if (idle)
{
if(hooks != null)
{
hooks.RaiseDispatcherInactive(this);
}
ComponentDispatcher.RaiseIdle();
}
return IntPtr.Zero ;
}
///<SecurityNote>
/// SecurityCritical - as this code performs an elevation.
/// TreatAsSafe - this method returns "I have input that can be processed".
/// equivalent to saying a 'key has been hit'.
/// Considered safe.
///</SecurityNote>
[SecurityCritical, SecurityTreatAsSafe ]
private bool IsInputPending()
{
int retVal = 0;
// We need to know if there is any pending input in the Win32
// queue because we want to only process Avalon "background"
// items after Win32 input has been processed.
//
// Win32 provides the GetQueueStatus API -- but it has a major
// drawback: it only counts "new" input. This means that
// sometimes it could return false, even if there really is input
// that needs to be processed. This results in very hard to
// find bugs.
//
// Luckily, Win32 also provides the MsgWaitForMultipleObjectsEx
// API. While more awkward to use, this API can return queue
// status information even if the input is "old". The various
// flags we use are:
//
// QS_INPUT
// This represents any pending input - such as mouse moves, or
// key presses. It also includes the new GenericInput messages.
//
// QS_EVENT
// This is actually a private flag that represents the various
// events that can be queued in Win32. Some of these events
// can cause input, but Win32 doesn't include them in the
// QS_INPUT flag. An example is WM_MOUSELEAVE.
//
// QS_POSTMESSAGE
// If there is already a message in the queue, we need to process
// it before we can process input.
//
// MWMO_INPUTAVAILABLE
// This flag indicates that any input (new or old) is to be
// reported.
//
retVal = UnsafeNativeMethods.MsgWaitForMultipleObjectsEx(0, null, 0,
NativeMethods.QS_INPUT |
NativeMethods.QS_EVENT |
NativeMethods.QS_POSTMESSAGE,
NativeMethods.MWMO_INPUTAVAILABLE);
return retVal == 0;
}
/// <SecurityNote>
/// Critical: Calls CriticalRequestProcessing.
/// TreatAsSafe: does not force the processing
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private bool RequestProcessing()
{
return CriticalRequestProcessing(false);
}
/// <SecurityNote>
/// Critical: This code controls the timing of when the Dispatcher
/// invokes the next operation.
/// </SecurityNote>
[SecurityCritical]
internal bool CriticalRequestProcessing(bool force)
{
bool succeeded = true;
// This method is called from within the instance lock. So we
// can reliably check the _window field without worrying about
// it being changed out from underneath us during shutdown.
if (IsWindowNull())
return false;
DispatcherPriority priority = _queue.MaxPriority;
if (priority != DispatcherPriority.Invalid &&
priority != DispatcherPriority.Inactive)
{
// If forcing the processing request, we will discard any
// existing request (timer or message) and request again.
if (force)
{
if (_postedProcessingType == PROCESS_BACKGROUND)
{
SafeNativeMethods.KillTimer(new HandleRef(this, _window.Value.Handle), TIMERID_BACKGROUND);
}
else if (_postedProcessingType == PROCESS_FOREGROUND)
{
MSG msg = new MSG();
UnsafeNativeMethods.PeekMessage(ref msg, new HandleRef(this, _window.Value.Handle), _msgProcessQueue, _msgProcessQueue, NativeMethods.PM_REMOVE);
}
_postedProcessingType = PROCESS_NONE;
}
if (_foregroundPriorityRange.Contains(priority))
{
succeeded = RequestForegroundProcessing();
}
else
{
succeeded = RequestBackgroundProcessing();
}
}
return succeeded;
}
/// <SecurityNote>
/// Critical: This code accesses window
/// TreatAsSafe: This code is ok to call since it does not expose the resource
/// </SecurityNote>
[SecurityCritical,SecurityTreatAsSafe]
private bool IsWindowNull()
{
if(_window.Value == null)
{
return true;
}
return false;
}
//<SecurityNote>
// Critical as it access critical data - for the window handle.
// TreatAsSafe - as this is a request to do queued work. Analogous to VB's DoEvents()
//</SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private bool RequestForegroundProcessing()
{
if(_postedProcessingType < PROCESS_FOREGROUND)
{
// If we have already set a timer to do background processing,
// make sure we stop it before posting a message for foreground
// processing.
if(_postedProcessingType == PROCESS_BACKGROUND)
{
SafeNativeMethods.KillTimer(new HandleRef(this, _window.Value.Handle), TIMERID_BACKGROUND);
}
_postedProcessingType = PROCESS_FOREGROUND;
// We have foreground items to process.
// By posting a message, Win32 will service us fairly promptly.
bool succeeded = UnsafeNativeMethods.TryPostMessage(new HandleRef(this, _window.Value.Handle), _msgProcessQueue, IntPtr.Zero, IntPtr.Zero);
if (!succeeded)
{
OnRequestProcessingFailure("TryPostMessage");
}
return succeeded;
}
return true;
}
//<SecurityNote>
// Critical - as it accesses critical data - to get the Window Handle.
// TreatAsSafe - as this method would be ok to expose publically, this is just a request for Timer processing.
//</SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private bool RequestBackgroundProcessing()
{
bool succeeded = true;
if(_postedProcessingType < PROCESS_BACKGROUND)
{
// If there is Win32 input pending, we can't do any background
// processing until it is done. We use a short timer to
// get processing time after the input.
if(IsInputPending())
{
_postedProcessingType = PROCESS_BACKGROUND;
succeeded = SafeNativeMethods.TrySetTimer(new HandleRef(this, _window.Value.Handle), TIMERID_BACKGROUND, DELTA_BACKGROUND);
if (!succeeded)
{
OnRequestProcessingFailure("TrySetTimer");
}
}
else
{
succeeded = RequestForegroundProcessing();
}
}
return succeeded;
}
// Request{Foreground|Background}Processing can encounter failures from an
// underlying OS method. We cannot recover from these failures - they are
// typically due to the application flooding the message queue, or starving
// the dispatcher's message pump until the queue floods, and thus outside
// the control of WPF. WPF ignores the failures, but that can leave the
// dispatcher in a non-responsive state, waiting for a message that will
// never arrive. It's difficult or impossible to determine whether
// this has happened from a crash dump.
//
// To help this diagnosis, we now record the fact that the failure has
// happened in the member
// _hasRequestProcessingFailed
// When this bool is true, you can delve into
// _reservedPtsCache
// to find a list of strings with some more rudimentary diagnostic information.
// Unfortunately, we cannot tell why the message queue is full or who has
// filled it - that knowledge is only available to the kernel. (DDVSO 127745)
private void OnRequestProcessingFailure(string methodName)
{
if (!_hasRequestProcessingFailed)
{
// initialize the failure log, multiplexed under _reservedPtsCache.
// (this member was chosen after instrumented tests revealed it
// usually has the least activity of any of the existing fields)
_reservedPtsCache = new Tuple<Object, List<String>>(_reservedPtsCache, new List<String>());
_hasRequestProcessingFailed = true;
}
// add a new entry to the failure log
Tuple<Object, List<String>> tuple = _reservedPtsCache as Tuple<Object, List<String>>;
if (tuple != null)
{
List<String> list = tuple.Item2;
list.Add(String.Format(System.Globalization.CultureInfo.InvariantCulture,
"{0:O} {1} failed", DateTime.Now, methodName));
// keep the list from growing too large
// (although usually it will have only one entry)
if (list.Count > 1000)
{
// keep the earliest and latest failures
list.RemoveRange(100, list.Count-200);
// acknowledge the gap
list.Insert(100, "... entries removed to conserve memory ...");
}
}
// handle the failure, according to app's preference
switch (BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailure)
{
case BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailureOptions.Continue:
break;
case BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailureOptions.Throw:
throw new InvalidOperationException(SR.Get(SRID.DispatcherRequestProcessingFailed));
case BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailureOptions.Reset:
_postedProcessingType = PROCESS_NONE;
break;
}
}
internal void PromoteTimers(int currentTimeInTicks)
{
try
{
List<DispatcherTimer> timers = null;
long timersVersion = 0;
lock(_instanceLock)
{
if(!_hasShutdownFinished) // Could be a non-dispatcher thread, lock to read
{
if(_dueTimeFound && _dueTimeInTicks - currentTimeInTicks <= 0)
{
timers = _timers;
timersVersion = _timersVersion;
}
}
}
if(timers != null)
{
DispatcherTimer timer = null;
int iTimer = 0;
do
{
lock(_instanceLock)
{
timer = null;
// If the timers collection changed while we are in the middle of
// looking for timers, start over.
if(timersVersion != _timersVersion)
{
timersVersion = _timersVersion;
iTimer = 0;
}
while(iTimer < _timers.Count)
{
// WARNING: this is vulnerable to wrapping
if(timers[iTimer]._dueTimeInTicks - currentTimeInTicks <= 0)
{
// Remove this timer from our list.
// Do not increment the index.
timer = timers[iTimer];
timers.RemoveAt(iTimer);
break;
}
else
{
iTimer++;
}
}
}
// Now that we are outside of the lock, promote the timer.
if(timer != null)
{
timer.Promote();
}
} while(timer != null);
}
}
finally
{
UpdateWin32Timer();
}
}
internal void AddTimer(DispatcherTimer timer)
{
lock(_instanceLock)
{
if(!_hasShutdownFinished) // Could be a non-dispatcher thread, lock to read
{
_timers.Add(timer);
_timersVersion++;
}
}
UpdateWin32Timer();
}
internal void RemoveTimer(DispatcherTimer timer)
{
lock(_instanceLock)
{
if(!_hasShutdownFinished) // Could be a non-dispatcher thread, lock to read
{
_timers.Remove(timer);
_timersVersion++;
}
}
UpdateWin32Timer();
}
internal void UpdateWin32Timer() // Called from DispatcherTimer
{
if(CheckAccess())
{
UpdateWin32TimerFromDispatcherThread(null);
}
else
{
BeginInvoke(DispatcherPriority.Send,
new DispatcherOperationCallback(UpdateWin32TimerFromDispatcherThread),
null);
}
}
private object UpdateWin32TimerFromDispatcherThread(object unused)
{
lock(_instanceLock)
{
if(!_hasShutdownFinished) // Dispatcher thread, does not technically need the lock to read
{
bool oldDueTimeFound = _dueTimeFound;
int oldDueTimeInTicks = _dueTimeInTicks;
_dueTimeFound = false;
_dueTimeInTicks = 0;
if(_timers.Count > 0)
{
// We could do better if we sorted the list of timers.
for(int i = 0; i < _timers.Count; i++)
{
DispatcherTimer timer = _timers[i];
if(!_dueTimeFound || timer._dueTimeInTicks - _dueTimeInTicks < 0)
{
_dueTimeFound = true;
_dueTimeInTicks = timer._dueTimeInTicks;
}
}
}
if(_dueTimeFound)
{
if(!_isWin32TimerSet || !oldDueTimeFound || (oldDueTimeInTicks != _dueTimeInTicks))
{
SetWin32Timer(_dueTimeInTicks);
}
}
else if(oldDueTimeFound)
{
KillWin32Timer();
}
}
}
return null;
}
///<SecurityNote>
/// Critical - accesses critical data
/// TreatAsSafe - we think it's ok to expose timers in the SEE.
/// a denial-of-service attack may be possible - but these are low-pri and possible in many other ways.
/// we can never bring down the iexplore process.
///</SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private void SetWin32Timer(int dueTimeInTicks)
{
if(!IsWindowNull())
{
int delta = dueTimeInTicks - Environment.TickCount;
if(delta < 1)
{
delta = 1;
}
// We are being called on the dispatcher thread so we can rely on
// _window.Value being non-null without taking the instance lock.
SafeNativeMethods.SetTimer(
new HandleRef(this, _window.Value.Handle),
TIMERID_TIMERS,
delta);
_isWin32TimerSet = true;
}
}
///<SecurityNote>
/// Critical - accesses critical data _window.Value.Handle
/// TreatAsSafe - OK to stop a dispatcher timer.
///</SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private void KillWin32Timer()
{
if(!IsWindowNull())
{
// We are being called on the dispatcher thread so we can rely on
// _window.Value being non-null without taking the instance lock.
SafeNativeMethods.KillTimer(
new HandleRef(this, _window.Value.Handle),
TIMERID_TIMERS);
_isWin32TimerSet = false;
}
}
// Exception filter returns true if exception should be caught.
/// <SecurityNote>
/// Critical: calls ExceptionFilter, which is critical
/// </SecurityNote>
[SecurityCritical]
private static bool ExceptionFilterStatic(object source, Exception e)
{
Dispatcher d = (Dispatcher)source;
return d.ExceptionFilter(e);
}
/// <SecurityNote>
/// Critical: accesses _unhandledExceptionFilter
/// </SecurityNote>
[SecurityCritical]
private bool ExceptionFilter(Exception e)
{
// see whether this dispatcher has already seen the exception.
// This can happen when the dispatcher is re-entered via
// PushFrame (or similar).
if (!e.Data.Contains(ExceptionDataKey))
{
// first time we've seen this exception - add data to the exception
e.Data.Add(ExceptionDataKey, null);
}
else
{
// we've seen this exception before - don't catch it
return false;
}
// By default, Request catch if there's anyone signed up to catch it;
bool requestCatch = HasUnhandledExceptionHandler;
// The app can hook up an ExceptionFilter to avoid catching it.
// ExceptionFilter will run REGARDLESS of whether there are exception handlers.
if (_unhandledExceptionFilter != null)
{
// The default requestCatch value that is passed in the args
// should be returned unchanged if filters don't set them explicitly.
_exceptionFilterEventArgs.Initialize(e, requestCatch);
bool bSuccess = false;
try
{
_unhandledExceptionFilter(this, _exceptionFilterEventArgs);
bSuccess = true;
}
finally
{
if (bSuccess)
{
requestCatch = _exceptionFilterEventArgs.RequestCatch;
}
// For bSuccess is false,
// To be in line with default behavior of structured exception handling,
// we would want to set requestCatch to false, however, this means one
// poorly programmed filter will knock out all dispatcher exception handling.
// If an exception filter fails, we run with whatever value is set thus far.
}
}
return requestCatch;
}
// This returns false when caller should rethrow the exception.
// true means Exception is "handled" and things just continue on.
private static bool CatchExceptionStatic(object source, Exception e)
{
Dispatcher dispatcher = (Dispatcher)source;
return dispatcher.CatchException(e);
}
// The exception filter called for catching an unhandled exception.
private bool CatchException(Exception e)
{
bool handled = false;
if (UnhandledException != null)
{
_unhandledExceptionEventArgs.Initialize(e, false);
bool bSuccess = false;
try
{
UnhandledException(this, _unhandledExceptionEventArgs);
handled = _unhandledExceptionEventArgs.Handled;
bSuccess = true;
}
finally
{
if (!bSuccess)
handled = false;
}
}
return(handled);
}
// This is called by DRT (via reflection) to see if there is a UnhandledException handler.
private bool HasUnhandledExceptionHandler
{
get { return (UnhandledException != null); }
}
[FriendAccessAllowed] //also used by ResourceReferenceExpression in PresentationFramework
internal object WrappedInvoke(Delegate callback, object args, int numArgs, Delegate catchHandler)
{
return _exceptionWrapper.TryCatchWhen(this, callback, args, numArgs, catchHandler);
}
private object[] CombineParameters(object arg, object[] args)
{
object[] parameters = new object[1 + (args == null ? 1 : args.Length)];
parameters[0] = arg;
if (args != null)
{
Array.Copy(args, 0, parameters, 1, args.Length);
}
else
{
parameters[1] = null;
}
return parameters;
}
private const int PROCESS_NONE = 0;
private const int PROCESS_BACKGROUND = 1;
private const int PROCESS_FOREGROUND = 2;
private const int TIMERID_BACKGROUND = 1;
private const int TIMERID_TIMERS = 2;
private const int DELTA_BACKGROUND = 1;
private static List<WeakReference> _dispatchers;
private static WeakReference _possibleDispatcher;
private static object _globalLock;
[ThreadStatic]
private static Dispatcher _tlsDispatcher; // use TLS for ownership only
private Thread _dispatcherThread;
private int _frameDepth;
internal bool _exitAllFrames; // used from DispatcherFrame
private bool _startingShutdown;
internal bool _hasShutdownStarted; // used from DispatcherFrame
private SecurityCriticalDataClass<CulturePreservingExecutionContext> _shutdownExecutionContext;
internal int _disableProcessingCount; // read by DispatcherSynchronizationContext, decremented by DispatcherProcessingDisabled
//private static Priority _foregroundBackgroundBorderPriority = new Priority(Priority.Min, Priority.Max, "Dispatcher.ForegroundBackgroundBorder");
//private static Priority _backgroundIdleBorderPriority = new Priority(Priority.Min, _foregroundBackgroundBorderPriority, "Dispatcher.BackgroundIdleBorder");
//private static Priority _foregroundPriority = new Priority(_foregroundBackgroundBorderPriority, Priority.Max, "Dispatcher.Foreground");
//private static Priority _backgroundPriority = new Priority(_backgroundIdleBorderPriority, _foregroundBackgroundBorderPriority, "Dispatcher.Background");
//private static Priority _idlePriority = new Priority(Priority.Min, _backgroundIdleBorderPriority, "Dispatcher.Idle");
//private static PriorityRange _foregroundPriorityRange = new PriorityRange(_foregroundBackgroundBorderPriority, false, Priority.Max, true);
//private static PriorityRange _backgroundPriorityRange = new PriorityRange(_backgroundIdleBorderPriority, false, _foregroundBackgroundBorderPriority, false);
//private static PriorityRange _idlePriorityRange = new PriorityRange(Priority.Min, false, _backgroundIdleBorderPriority, false);
private static PriorityRange _foregroundPriorityRange = new PriorityRange(DispatcherPriority.Loaded, true, DispatcherPriority.Send, true);
private static PriorityRange _backgroundPriorityRange = new PriorityRange(DispatcherPriority.Background, true, DispatcherPriority.Input, true);
private static PriorityRange _idlePriorityRange = new PriorityRange(DispatcherPriority.SystemIdle, true, DispatcherPriority.ContextIdle, true);
private SecurityCriticalData<MessageOnlyHwndWrapper> _window;
/// <SecurityNote>
/// Critical: If disclosed, would allow untrusted parties to listen to raw messages.
/// </SecurityNote>
[SecurityCritical]
private HwndWrapperHook _hook;
private int _postedProcessingType;
/// <SecurityNote>
/// Critical: This code gets set by RegisterWindowMessage which is under an elevation.
/// </SecurityNote>
[SecurityCritical]
private static WindowMessage _msgProcessQueue;
private static ExceptionWrapper _exceptionWrapper;
private static readonly object ExceptionDataKey = new object();
// Preallocated arguments for exception handling.
// This helps avoid allocations in the handler code, a potential
// source of secondary exceptions (i.e. in Out-Of-Memory cases).
private DispatcherUnhandledExceptionEventArgs _unhandledExceptionEventArgs;
/// <SecurityNote>
/// Do not expose to partially trusted code.
/// </SecurityNote>
[SecurityCritical]
private DispatcherUnhandledExceptionFilterEventHandler _unhandledExceptionFilter;
private DispatcherUnhandledExceptionFilterEventArgs _exceptionFilterEventArgs;
private object _reserved0;
private object _reserved1;
private object _reserved2;
private object _reserved3;
private object _reserved4;
private object _reservedPtsCache;
private object _reservedInputMethod;
private object _reservedInputManager;
internal DispatcherSynchronizationContext _defaultDispatcherSynchronizationContext;
internal object _instanceLock = new object(); // Also used by DispatcherOperation
private PriorityQueue<DispatcherOperation> _queue;
private List<DispatcherTimer> _timers = new List<DispatcherTimer>();
private long _timersVersion;
private bool _dueTimeFound;
private int _dueTimeInTicks;
private bool _isWin32TimerSet;
// This can be read from any thread, but only written by the dispatcher thread.
// Dispatcher Thread - lock _instanceLock only on write
// Non-Dispatcher Threads - lock _instanceLock on read
private bool _hasShutdownFinished;
// Enables/disables ITfMessagePump handshake with Text Services Framework.
private bool _isTSFMessagePumpEnabled;
// For diagnosing situations where dispatcher stops responding due to failure
// of TryPostMessage or TrySetTimer. If this is true (in a crash dump, say)
// delve into _reservedPtsCache to find more about the failure(s).
private bool _hasRequestProcessingFailed;
/// <SecurityNote>
/// Do not expose hooks to partial trust.
/// </SecurityNote>
[SecurityCritical]
private DispatcherHooks _hooks;
}
}
|