using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using Windows.System; namespace NotificationFlyoutSample { public static class DispatcherQueueExtensions { /// /// Indicates whether or not is available. /// private static bool IsHasThreadAccessPropertyAvailable => true; public static Task EnqueueAsync(this DispatcherQueue dispatcher, Action function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal) { // Run the function directly when we have thread access. // Also reuse Task.CompletedTask in case of success, // to skip an unnecessary heap allocation for every invocation. if (IsHasThreadAccessPropertyAvailable && dispatcher.HasThreadAccess) { try { function(); return Task.CompletedTask; } catch (Exception e) { return Task.FromException(e); } } static Task TryEnqueueAsync(DispatcherQueue dispatcher, Action function, DispatcherQueuePriority priority) { var taskCompletionSource = new TaskCompletionSource(); if (!dispatcher.TryEnqueue(priority, () => { try { function(); taskCompletionSource.SetResult(null); } catch (Exception e) { taskCompletionSource.SetException(e); } })) { taskCompletionSource.SetException(GetEnqueueException("Failed to enqueue the operation")); } return taskCompletionSource.Task; } return TryEnqueueAsync(dispatcher, function, priority); } /// /// Invokes a given function on the target and returns a /// that completes when the invocation of the function is completed. /// /// The return type of to relay through the returned . /// The target to invoke the code on. /// The to invoke. /// The priority level for the function to invoke. /// A that completes when the invocation of is over. /// If the current thread has access to , will be invoked directly. public static Task EnqueueAsync(this DispatcherQueue dispatcher, Func function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal) { if (IsHasThreadAccessPropertyAvailable && dispatcher.HasThreadAccess) { try { return Task.FromResult(function()); } catch (Exception e) { return Task.FromException(e); } } static Task TryEnqueueAsync(DispatcherQueue dispatcher, Func function, DispatcherQueuePriority priority) { var taskCompletionSource = new TaskCompletionSource(); if (!dispatcher.TryEnqueue(priority, () => { try { taskCompletionSource.SetResult(function()); } catch (Exception e) { taskCompletionSource.SetException(e); } })) { taskCompletionSource.SetException(GetEnqueueException("Failed to enqueue the operation")); } return taskCompletionSource.Task; } return TryEnqueueAsync(dispatcher, function, priority); } /// /// Invokes a given function on the target and returns a /// that acts as a proxy for the one returned by the given function. /// /// The target to invoke the code on. /// The to invoke. /// The priority level for the function to invoke. /// A that acts as a proxy for the one returned by . /// If the current thread has access to , will be invoked directly. public static Task EnqueueAsync(this DispatcherQueue dispatcher, Func function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal) { // If we have thread access, we can retrieve the task directly. // We don't use ConfigureAwait(false) in this case, in order // to let the caller continue its execution on the same thread // after awaiting the task returned by this function. if (IsHasThreadAccessPropertyAvailable && dispatcher.HasThreadAccess) { try { if (function() is Task awaitableResult) { return awaitableResult; } return Task.FromException(GetEnqueueException("The Task returned by function cannot be null.")); } catch (Exception e) { return Task.FromException(e); } } static Task TryEnqueueAsync(DispatcherQueue dispatcher, Func function, DispatcherQueuePriority priority) { var taskCompletionSource = new TaskCompletionSource(); if (!dispatcher.TryEnqueue(priority, async () => { try { if (function() is Task awaitableResult) { await awaitableResult.ConfigureAwait(false); taskCompletionSource.SetResult(null); } else { taskCompletionSource.SetException(GetEnqueueException("The Task returned by function cannot be null.")); } } catch (Exception e) { taskCompletionSource.SetException(e); } })) { taskCompletionSource.SetException(GetEnqueueException("Failed to enqueue the operation")); } return taskCompletionSource.Task; } return TryEnqueueAsync(dispatcher, function, priority); } /// /// Invokes a given function on the target and returns a /// that acts as a proxy for the one returned by the given function. /// /// The return type of to relay through the returned . /// The target to invoke the code on. /// The to invoke. /// The priority level for the function to invoke. /// A that relays the one returned by . /// If the current thread has access to , will be invoked directly. public static Task EnqueueAsync(this DispatcherQueue dispatcher, Func> function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal) { if (IsHasThreadAccessPropertyAvailable && dispatcher.HasThreadAccess) { try { if (function() is Task awaitableResult) { return awaitableResult; } return Task.FromException(GetEnqueueException("The Task returned by function cannot be null.")); } catch (Exception e) { return Task.FromException(e); } } static Task TryEnqueueAsync(DispatcherQueue dispatcher, Func> function, DispatcherQueuePriority priority) { var taskCompletionSource = new TaskCompletionSource(); if (!dispatcher.TryEnqueue(priority, async () => { try { if (function() is Task awaitableResult) { var result = await awaitableResult.ConfigureAwait(false); taskCompletionSource.SetResult(result); } else { taskCompletionSource.SetException(GetEnqueueException("The Task returned by function cannot be null.")); } } catch (Exception e) { taskCompletionSource.SetException(e); } })) { taskCompletionSource.SetException(GetEnqueueException("Failed to enqueue the operation")); } return taskCompletionSource.Task; } return TryEnqueueAsync(dispatcher, function, priority); } /// /// Creates an to return when an enqueue operation fails. /// /// The message of the exception. /// An with a specified message. [MethodImpl(MethodImplOptions.NoInlining)] private static InvalidOperationException GetEnqueueException(string message) { return new InvalidOperationException(message); } } }