Skip to content

Commit

Permalink
Fix: Fixed possible NullReferenceException with `DispatcherQueue.Enqu…
Browse files Browse the repository at this point in the history
…eueAsync()` (#12196)
  • Loading branch information
hishitetsu authored Apr 25, 2023
1 parent fd96bc1 commit 06b67f0
Show file tree
Hide file tree
Showing 34 changed files with 138 additions and 80 deletions.
4 changes: 2 additions & 2 deletions src/Files.App/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ public void OnActivated(AppActivationArguments activatedEventArgs)
var data = activatedEventArgs.Data;

// InitializeApplication accesses UI, needs to be called on UI thread
_ = Window.DispatcherQueue.EnqueueAsync(() => Window.InitializeApplication(data));
_ = Window.DispatcherQueue.EnqueueOrInvokeAsync(() => Window.InitializeApplication(data));
}

/// <summary>
Expand Down Expand Up @@ -475,7 +475,7 @@ private static void AppUnhandledException(Exception ex, bool shouldShowNotificat
userSettingsService.AppSettingsService.RestoreTabsOnStartup = true;
userSettingsService.GeneralSettingsService.LastCrashedTabList = lastSessionTabList;

Window.DispatcherQueue.EnqueueAsync(async () =>
Window.DispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
await Launcher.LaunchUriAsync(new Uri("files-uwp:"));
}).Wait(1000);
Expand Down
4 changes: 2 additions & 2 deletions src/Files.App/BaseLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ internal set
if (selectedItems.Count == 1)
{
SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{selectedItems.Count} {"ItemSelected/Text".GetLocalizedResource()}";
DispatcherQueue.EnqueueAsync(async () =>
DispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
// Tapped event must be executed first
await Task.Delay(50);
Expand Down Expand Up @@ -1355,7 +1355,7 @@ protected async void ValidateItemNameInputText(TextBox textBox, TextBoxBeforeTex
{
args.Cancel = true;

await DispatcherQueue.EnqueueAsync(() =>
await DispatcherQueue.EnqueueOrInvokeAsync(() =>
{
var oldSelection = textBox.SelectionStart + textBox.SelectionLength;
var oldText = textBox.Text;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public static async Task<DriveItem> CreateFromPropertiesAsync(StorageFolder root
item.DeviceID = deviceId;
item.Root = root;

_ = App.Window.DispatcherQueue.EnqueueAsync(() => item.UpdatePropertiesAsync());
_ = App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => item.UpdatePropertiesAsync());

return item;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public ulong SpaceUsed
{
SetProperty(ref spaceUsed, value);

App.Window.DispatcherQueue.EnqueueAsync(() => OnPropertyChanged(nameof(ToolTipText)));
App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => OnPropertyChanged(nameof(ToolTipText)));
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/Files.App/DataModels/SidebarPinnedModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,20 @@ public async Task<LocationItem> CreateLocationItemFromPathAsync(string path)
{
var iconData = await FileThumbnailHelper.LoadIconFromStorageItemAsync(res.Result, 96u, ThumbnailMode.ListView);
locationItem.IconData = iconData;
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueAsync(() => locationItem.IconData.ToBitmapAsync());
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => locationItem.IconData.ToBitmapAsync());
}

if (locationItem.IconData is null)
{
locationItem.IconData = await FileThumbnailHelper.LoadIconWithoutOverlayAsync(path, 96u);

if (locationItem.IconData is not null)
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueAsync(() => locationItem.IconData.ToBitmapAsync());
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => locationItem.IconData.ToBitmapAsync());
}
}
else
{
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueAsync(() => UIHelpers.GetSidebarIconResource(Constants.ImageRes.Folder));
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => UIHelpers.GetSidebarIconResource(Constants.ImageRes.Folder));
locationItem.IsInvalid = true;
Debug.WriteLine($"Pinned item was invalid {res.ErrorCode}, item: {path}");
}
Expand Down
48 changes: 48 additions & 0 deletions src/Files.App/Extensions/DispatcherQueueExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using System;
using System.Threading.Tasks;

namespace Files.App.Extensions
{
// Window.DispatcherQueue seems to be null sometimes.
// We don't know why, but as a workaround, we invoke the function directly if DispatcherQueue is null.
public static class DispatcherQueueExtensions
{
public static Task EnqueueOrInvokeAsync(this DispatcherQueue? dispatcher, Func<Task> function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal)
{
if (dispatcher is not null)
return dispatcher.EnqueueAsync(function, priority);
else
return function();
}

public static Task<T> EnqueueOrInvokeAsync<T>(this DispatcherQueue? dispatcher, Func<Task<T>> function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal)
{
if (dispatcher is not null)
return dispatcher.EnqueueAsync(function, priority);
else
return function();
}

public static Task EnqueueOrInvokeAsync(this DispatcherQueue? dispatcher, Action function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal)
{
if (dispatcher is not null)
return dispatcher.EnqueueAsync(function, priority);
else
{
function();
return Task.CompletedTask;
}
}

public static Task<T> EnqueueOrInvokeAsync<T>(this DispatcherQueue? dispatcher, Func<T> function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal)
{
if (dispatcher is not null)
return dispatcher.EnqueueAsync(function, priority);
else
return Task.FromResult(function());
}

}
}
5 changes: 3 additions & 2 deletions src/Files.App/Filesystem/Cloud/CloudDrivesManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.WinUI;
using Files.App.DataModels.NavigationControlItems;
using Files.App.Extensions;
using Files.App.Helpers;
using Files.Shared;
using Files.Shared.Cloud;
Expand Down Expand Up @@ -53,7 +54,7 @@ public async Task UpdateDrivesAsync()
try
{
cloudProviderItem.Root = await StorageFolder.GetFolderFromPathAsync(cloudProviderItem.Path);
_ = App.Window.DispatcherQueue.EnqueueAsync(() => cloudProviderItem.UpdatePropertiesAsync());
_ = App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => cloudProviderItem.UpdatePropertiesAsync());
}
catch (Exception ex)
{
Expand All @@ -72,7 +73,7 @@ public async Task UpdateDrivesAsync()
{
cloudProviderItem.IconData = iconData;
await App.Window.DispatcherQueue
.EnqueueAsync(async () => cloudProviderItem.Icon = await iconData.ToBitmapAsync());
.EnqueueOrInvokeAsync(async () => cloudProviderItem.Icon = await iconData.ToBitmapAsync());
}

lock (drives)
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Filesystem/Search/FolderSearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ private ListedItem GetListedItemAsync(string itemPath, WIN32_FIND_DATA findData)
{
if (t.IsCompletedSuccessfully && t.Result is not null)
{
_ = FilesystemTasks.Wrap(() => App.Window.DispatcherQueue.EnqueueAsync(async () =>
_ = FilesystemTasks.Wrap(() => App.Window.DispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
listedItem.FileImage = await t.Result.ToBitmapAsync();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low));
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Helpers/DynamicDialogFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public static DynamicDialog GetFor_RenameDialog()
inputText.Loaded += (s, e) =>
{
// dispatching to the ui thread fixes an issue where the primary dialog button would steal focus
_ = inputText.DispatcherQueue.EnqueueAsync(() => inputText.Focus(FocusState.Programmatic));
_ = inputText.DispatcherQueue.EnqueueOrInvokeAsync(() => inputText.Focus(FocusState.Programmatic));
};

dialog = new DynamicDialog(new DynamicDialogViewModel()
Expand Down
3 changes: 2 additions & 1 deletion src/Files.App/Helpers/MenuFlyoutHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CommunityToolkit.WinUI;
using Files.App.Extensions;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
Expand Down Expand Up @@ -101,7 +102,7 @@ private static async void SetupItems(MenuFlyout menu)
return;
}

await menu.DispatcherQueue.EnqueueAsync(() =>
await menu.DispatcherQueue.EnqueueOrInvokeAsync(() =>
{
menu.Items.Clear();
AddItems(menu.Items, itemSource);
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Helpers/ThemeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private static async void UiSettings_ColorValuesChanged(UISettings sender, objec
titleBar = App.GetAppWindow(currentApplicationWindow)?.TitleBar;

// Dispatch on UI thread so that we have a current appbar to access and change
await currentApplicationWindow.DispatcherQueue.EnqueueAsync(ApplyTheme);
await currentApplicationWindow.DispatcherQueue.EnqueueOrInvokeAsync(ApplyTheme);
}

private static void ApplyTheme()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public virtual async void OpenDirectoryInNewTab(RoutedEventArgs e)
{
foreach (ListedItem listedItem in SlimContentPage.SelectedItems)
{
await App.Window.DispatcherQueue.EnqueueAsync(async () =>
await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
await mainPageViewModel.AddNewTabByPathAsync(typeof(PaneHolderPage), (listedItem as ShortcutItem)?.TargetPath ?? listedItem.ItemPath);
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using CommunityToolkit.WinUI;
using Files.App.DataModels.NavigationControlItems;
using Files.App.Extensions;
using Files.App.Filesystem;
using Files.App.Helpers;
using Files.App.Storage.WindowsStorage;
Expand Down Expand Up @@ -66,7 +67,7 @@ public async Task UpdateDrivePropertiesAsync(ILocatableFolder drive)
var rootModified = await FilesystemTasks.Wrap(() => StorageFolder.GetFolderFromPathAsync(drive.Path).AsTask());
if (rootModified && drive is DriveItem matchingDriveEjected)
{
_ = App.Window.DispatcherQueue.EnqueueAsync(() =>
_ = App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() =>
{
matchingDriveEjected.Root = rootModified.Result;
matchingDriveEjected.Text = rootModified.Result.DisplayName;
Expand Down
5 changes: 3 additions & 2 deletions src/Files.App/ServicesImplementation/ThreadingService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CommunityToolkit.WinUI;
using Files.App.Extensions;
using Files.Backend.Services;
using Microsoft.UI.Dispatching;
using System;
Expand All @@ -17,12 +18,12 @@ public ThreadingService()

public Task ExecuteOnUiThreadAsync(Action action)
{
return _dispatcherQueue.EnqueueAsync(action);
return _dispatcherQueue.EnqueueOrInvokeAsync(action);
}

public Task<TResult?> ExecuteOnUiThreadAsync<TResult>(Func<TResult?> func)
{
return _dispatcherQueue.EnqueueAsync<TResult?>(func);
return _dispatcherQueue.EnqueueOrInvokeAsync<TResult?>(func);
}
}
}
4 changes: 2 additions & 2 deletions src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public async Task LoadCardThumbnailAsync()

// Thumbnail data is valid, set the item icon
if (thumbnailData is not null && thumbnailData.Length > 0)
Thumbnail = await thumbnailData.ToBitmapAsync(Constants.Widgets.WidgetIconSize);
Thumbnail = await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => thumbnailData.ToBitmapAsync(Constants.Widgets.WidgetIconSize));
}

public int CompareTo(DriveCardItem? other) => Item.Path.CompareTo(other?.Item?.Path);
Expand Down Expand Up @@ -146,7 +146,7 @@ public DrivesWidget()

private async void Drives_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
await DispatcherQueue.EnqueueAsync(async () =>
await DispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
foreach (DriveItem drive in drivesViewModel.Drives.ToList())
{
Expand Down
4 changes: 2 additions & 2 deletions src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public async Task LoadCardThumbnailAsync()
}
if (thumbnailData is not null && thumbnailData.Length > 0)
{
Thumbnail = await thumbnailData.ToBitmapAsync(Constants.Widgets.WidgetIconSize);
Thumbnail = await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => thumbnailData.ToBitmapAsync(Constants.Widgets.WidgetIconSize));
}
}
}
Expand Down Expand Up @@ -247,7 +247,7 @@ private async void ModifyItem(object? sender, ModifyQuickAccessEventArgs? e)
if (e is null)
return;

await DispatcherQueue.EnqueueAsync(async () =>
await DispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
if (e.Reset)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ public async Task RefreshWidget()

private async void Manager_RecentFilesChanged(object sender, NotifyCollectionChangedEventArgs e)
{
await DispatcherQueue.EnqueueAsync(async () =>
await DispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
// e.Action can only be Reset right now; naively refresh everything for simplicity
await UpdateRecentsList(e);
Expand Down
Loading

0 comments on commit 06b67f0

Please sign in to comment.