Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Fixed possible NullReferenceException with DispatcherQueue.EnqueueAsync() #12196

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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