Skip to content

Commit 06b67f0

Browse files
authored
Fix: Fixed possible NullReferenceException with DispatcherQueue.EnqueueAsync() (#12196)
1 parent fd96bc1 commit 06b67f0

34 files changed

+138
-80
lines changed

src/Files.App/App.xaml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ public void OnActivated(AppActivationArguments activatedEventArgs)
270270
var data = activatedEventArgs.Data;
271271

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

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

478-
Window.DispatcherQueue.EnqueueAsync(async () =>
478+
Window.DispatcherQueue.EnqueueOrInvokeAsync(async () =>
479479
{
480480
await Launcher.LaunchUriAsync(new Uri("files-uwp:"));
481481
}).Wait(1000);

src/Files.App/BaseLayout.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ internal set
257257
if (selectedItems.Count == 1)
258258
{
259259
SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{selectedItems.Count} {"ItemSelected/Text".GetLocalizedResource()}";
260-
DispatcherQueue.EnqueueAsync(async () =>
260+
DispatcherQueue.EnqueueOrInvokeAsync(async () =>
261261
{
262262
// Tapped event must be executed first
263263
await Task.Delay(50);
@@ -1355,7 +1355,7 @@ protected async void ValidateItemNameInputText(TextBox textBox, TextBoxBeforeTex
13551355
{
13561356
args.Cancel = true;
13571357

1358-
await DispatcherQueue.EnqueueAsync(() =>
1358+
await DispatcherQueue.EnqueueOrInvokeAsync(() =>
13591359
{
13601360
var oldSelection = textBox.SelectionStart + textBox.SelectionLength;
13611361
var oldText = textBox.Text;

src/Files.App/DataModels/NavigationControlItems/DriveItem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public static async Task<DriveItem> CreateFromPropertiesAsync(StorageFolder root
184184
item.DeviceID = deviceId;
185185
item.Root = root;
186186

187-
_ = App.Window.DispatcherQueue.EnqueueAsync(() => item.UpdatePropertiesAsync());
187+
_ = App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => item.UpdatePropertiesAsync());
188188

189189
return item;
190190
}

src/Files.App/DataModels/NavigationControlItems/LocationItem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public ulong SpaceUsed
9595
{
9696
SetProperty(ref spaceUsed, value);
9797

98-
App.Window.DispatcherQueue.EnqueueAsync(() => OnPropertyChanged(nameof(ToolTipText)));
98+
App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => OnPropertyChanged(nameof(ToolTipText)));
9999
}
100100
}
101101

src/Files.App/DataModels/SidebarPinnedModel.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,20 +115,20 @@ public async Task<LocationItem> CreateLocationItemFromPathAsync(string path)
115115
{
116116
var iconData = await FileThumbnailHelper.LoadIconFromStorageItemAsync(res.Result, 96u, ThumbnailMode.ListView);
117117
locationItem.IconData = iconData;
118-
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueAsync(() => locationItem.IconData.ToBitmapAsync());
118+
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => locationItem.IconData.ToBitmapAsync());
119119
}
120120

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

125125
if (locationItem.IconData is not null)
126-
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueAsync(() => locationItem.IconData.ToBitmapAsync());
126+
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => locationItem.IconData.ToBitmapAsync());
127127
}
128128
}
129129
else
130130
{
131-
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueAsync(() => UIHelpers.GetSidebarIconResource(Constants.ImageRes.Folder));
131+
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => UIHelpers.GetSidebarIconResource(Constants.ImageRes.Folder));
132132
locationItem.IsInvalid = true;
133133
Debug.WriteLine($"Pinned item was invalid {res.ErrorCode}, item: {path}");
134134
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using CommunityToolkit.WinUI;
2+
using Microsoft.UI.Dispatching;
3+
using System;
4+
using System.Threading.Tasks;
5+
6+
namespace Files.App.Extensions
7+
{
8+
// Window.DispatcherQueue seems to be null sometimes.
9+
// We don't know why, but as a workaround, we invoke the function directly if DispatcherQueue is null.
10+
public static class DispatcherQueueExtensions
11+
{
12+
public static Task EnqueueOrInvokeAsync(this DispatcherQueue? dispatcher, Func<Task> function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal)
13+
{
14+
if (dispatcher is not null)
15+
return dispatcher.EnqueueAsync(function, priority);
16+
else
17+
return function();
18+
}
19+
20+
public static Task<T> EnqueueOrInvokeAsync<T>(this DispatcherQueue? dispatcher, Func<Task<T>> function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal)
21+
{
22+
if (dispatcher is not null)
23+
return dispatcher.EnqueueAsync(function, priority);
24+
else
25+
return function();
26+
}
27+
28+
public static Task EnqueueOrInvokeAsync(this DispatcherQueue? dispatcher, Action function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal)
29+
{
30+
if (dispatcher is not null)
31+
return dispatcher.EnqueueAsync(function, priority);
32+
else
33+
{
34+
function();
35+
return Task.CompletedTask;
36+
}
37+
}
38+
39+
public static Task<T> EnqueueOrInvokeAsync<T>(this DispatcherQueue? dispatcher, Func<T> function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal)
40+
{
41+
if (dispatcher is not null)
42+
return dispatcher.EnqueueAsync(function, priority);
43+
else
44+
return Task.FromResult(function());
45+
}
46+
47+
}
48+
}

src/Files.App/Filesystem/Cloud/CloudDrivesManager.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using CommunityToolkit.Mvvm.DependencyInjection;
22
using CommunityToolkit.WinUI;
33
using Files.App.DataModels.NavigationControlItems;
4+
using Files.App.Extensions;
45
using Files.App.Helpers;
56
using Files.Shared;
67
using Files.Shared.Cloud;
@@ -53,7 +54,7 @@ public async Task UpdateDrivesAsync()
5354
try
5455
{
5556
cloudProviderItem.Root = await StorageFolder.GetFolderFromPathAsync(cloudProviderItem.Path);
56-
_ = App.Window.DispatcherQueue.EnqueueAsync(() => cloudProviderItem.UpdatePropertiesAsync());
57+
_ = App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => cloudProviderItem.UpdatePropertiesAsync());
5758
}
5859
catch (Exception ex)
5960
{
@@ -72,7 +73,7 @@ public async Task UpdateDrivesAsync()
7273
{
7374
cloudProviderItem.IconData = iconData;
7475
await App.Window.DispatcherQueue
75-
.EnqueueAsync(async () => cloudProviderItem.Icon = await iconData.ToBitmapAsync());
76+
.EnqueueOrInvokeAsync(async () => cloudProviderItem.Icon = await iconData.ToBitmapAsync());
7677
}
7778

7879
lock (drives)

src/Files.App/Filesystem/Search/FolderSearch.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ private ListedItem GetListedItemAsync(string itemPath, WIN32_FIND_DATA findData)
393393
{
394394
if (t.IsCompletedSuccessfully && t.Result is not null)
395395
{
396-
_ = FilesystemTasks.Wrap(() => App.Window.DispatcherQueue.EnqueueAsync(async () =>
396+
_ = FilesystemTasks.Wrap(() => App.Window.DispatcherQueue.EnqueueOrInvokeAsync(async () =>
397397
{
398398
listedItem.FileImage = await t.Result.ToBitmapAsync();
399399
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low));

src/Files.App/Helpers/DynamicDialogFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public static DynamicDialog GetFor_RenameDialog()
9898
inputText.Loaded += (s, e) =>
9999
{
100100
// dispatching to the ui thread fixes an issue where the primary dialog button would steal focus
101-
_ = inputText.DispatcherQueue.EnqueueAsync(() => inputText.Focus(FocusState.Programmatic));
101+
_ = inputText.DispatcherQueue.EnqueueOrInvokeAsync(() => inputText.Focus(FocusState.Programmatic));
102102
};
103103

104104
dialog = new DynamicDialog(new DynamicDialogViewModel()

src/Files.App/Helpers/MenuFlyoutHelper.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using CommunityToolkit.WinUI;
2+
using Files.App.Extensions;
23
using Microsoft.UI.Xaml;
34
using Microsoft.UI.Xaml.Controls;
45
using System;
@@ -101,7 +102,7 @@ private static async void SetupItems(MenuFlyout menu)
101102
return;
102103
}
103104

104-
await menu.DispatcherQueue.EnqueueAsync(() =>
105+
await menu.DispatcherQueue.EnqueueOrInvokeAsync(() =>
105106
{
106107
menu.Items.Clear();
107108
AddItems(menu.Items, itemSource);

src/Files.App/Helpers/ThemeHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ private static async void UiSettings_ColorValuesChanged(UISettings sender, objec
7575
titleBar = App.GetAppWindow(currentApplicationWindow)?.TitleBar;
7676

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

8181
private static void ApplyTheme()

src/Files.App/Interacts/BaseLayoutCommandImplementationModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public virtual async void OpenDirectoryInNewTab(RoutedEventArgs e)
100100
{
101101
foreach (ListedItem listedItem in SlimContentPage.SelectedItems)
102102
{
103-
await App.Window.DispatcherQueue.EnqueueAsync(async () =>
103+
await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(async () =>
104104
{
105105
await mainPageViewModel.AddNewTabByPathAsync(typeof(PaneHolderPage), (listedItem as ShortcutItem)?.TargetPath ?? listedItem.ItemPath);
106106
},

src/Files.App/ServicesImplementation/RemovableDrivesService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using CommunityToolkit.WinUI;
22
using Files.App.DataModels.NavigationControlItems;
3+
using Files.App.Extensions;
34
using Files.App.Filesystem;
45
using Files.App.Helpers;
56
using Files.App.Storage.WindowsStorage;
@@ -66,7 +67,7 @@ public async Task UpdateDrivePropertiesAsync(ILocatableFolder drive)
6667
var rootModified = await FilesystemTasks.Wrap(() => StorageFolder.GetFolderFromPathAsync(drive.Path).AsTask());
6768
if (rootModified && drive is DriveItem matchingDriveEjected)
6869
{
69-
_ = App.Window.DispatcherQueue.EnqueueAsync(() =>
70+
_ = App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() =>
7071
{
7172
matchingDriveEjected.Root = rootModified.Result;
7273
matchingDriveEjected.Text = rootModified.Result.DisplayName;

src/Files.App/ServicesImplementation/ThreadingService.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using CommunityToolkit.WinUI;
2+
using Files.App.Extensions;
23
using Files.Backend.Services;
34
using Microsoft.UI.Dispatching;
45
using System;
@@ -17,12 +18,12 @@ public ThreadingService()
1718

1819
public Task ExecuteOnUiThreadAsync(Action action)
1920
{
20-
return _dispatcherQueue.EnqueueAsync(action);
21+
return _dispatcherQueue.EnqueueOrInvokeAsync(action);
2122
}
2223

2324
public Task<TResult?> ExecuteOnUiThreadAsync<TResult>(Func<TResult?> func)
2425
{
25-
return _dispatcherQueue.EnqueueAsync<TResult?>(func);
26+
return _dispatcherQueue.EnqueueOrInvokeAsync<TResult?>(func);
2627
}
2728
}
2829
}

src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public async Task LoadCardThumbnailAsync()
6161

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

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

147147
private async void Drives_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
148148
{
149-
await DispatcherQueue.EnqueueAsync(async () =>
149+
await DispatcherQueue.EnqueueOrInvokeAsync(async () =>
150150
{
151151
foreach (DriveItem drive in drivesViewModel.Drives.ToList())
152152
{

src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public async Task LoadCardThumbnailAsync()
9797
}
9898
if (thumbnailData is not null && thumbnailData.Length > 0)
9999
{
100-
Thumbnail = await thumbnailData.ToBitmapAsync(Constants.Widgets.WidgetIconSize);
100+
Thumbnail = await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() => thumbnailData.ToBitmapAsync(Constants.Widgets.WidgetIconSize));
101101
}
102102
}
103103
}
@@ -247,7 +247,7 @@ private async void ModifyItem(object? sender, ModifyQuickAccessEventArgs? e)
247247
if (e is null)
248248
return;
249249

250-
await DispatcherQueue.EnqueueAsync(async () =>
250+
await DispatcherQueue.EnqueueOrInvokeAsync(async () =>
251251
{
252252
if (e.Reset)
253253
{

src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ public async Task RefreshWidget()
210210

211211
private async void Manager_RecentFilesChanged(object sender, NotifyCollectionChangedEventArgs e)
212212
{
213-
await DispatcherQueue.EnqueueAsync(async () =>
213+
await DispatcherQueue.EnqueueOrInvokeAsync(async () =>
214214
{
215215
// e.Action can only be Reset right now; naively refresh everything for simplicity
216216
await UpdateRecentsList(e);

0 commit comments

Comments
 (0)