From 8c3ddb105f3ac1c6129b393055fd917d944f7687 Mon Sep 17 00:00:00 2001 From: Matt <5415177+ZehMatt@users.noreply.github.com> Date: Wed, 29 Apr 2026 20:16:49 +0300 Subject: [PATCH] Win32: Run file picker on dedicated STA thread to avoid MTA deadlock (#21266) * Run Win32 file picker on a dedicated thread to avoid MTA deadlock * Add threading comment in Win32StorageProvider --------- Co-authored-by: Julien Lebosquain --- .../Avalonia.Win32/Win32StorageProvider.cs | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Win32StorageProvider.cs b/src/Windows/Avalonia.Win32/Win32StorageProvider.cs index a61af0de97..6519933de7 100644 --- a/src/Windows/Avalonia.Win32/Win32StorageProvider.cs +++ b/src/Windows/Avalonia.Win32/Win32StorageProvider.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.ComponentModel; using System.Runtime.InteropServices; +using System.Threading; using System.Threading.Tasks; using Avalonia.Controls.Utils; using Avalonia.Platform.Storage; @@ -96,7 +97,13 @@ namespace Avalonia.Win32 Func convert) where TStorageItem : IStorageItem { - return Task.Factory.StartNew(() => + // TODO13: verify that we're on the correct dispatcher, matching other platforms' implementations. + // We should then be able to remove the dedicated thread and simply use IFileDialog directly (needs to be reconfirmed). + + var tcs = new TaskCompletionSource<(IReadOnlyList, int)>( + TaskCreationOptions.RunContinuationsAsynchronously); + + var thread = new Thread(() => { IReadOnlyList result = []; try @@ -179,7 +186,8 @@ namespace Avalonia.Win32 if ((uint)showResult == (uint)UnmanagedMethods.HRESULT.E_CANCELLED) { - return (result, typeIndex); + tcs.SetResult((result, typeIndex)); + return; } else if ((uint)showResult != (uint)UnmanagedMethods.HRESULT.S_OK) { @@ -210,14 +218,23 @@ namespace Avalonia.Win32 result = [convert(singleResult)]; } - return (result, typeIndex); + tcs.SetResult((result, typeIndex)); } catch (COMException ex) { var message = new Win32Exception(ex.HResult).Message; - throw new COMException(message, ex); + tcs.SetException(new COMException(message, ex)); } - }, TaskCreationOptions.LongRunning); + catch (Exception ex) + { + tcs.SetException(ex); + } + }); + thread.SetApartmentState(ApartmentState.STA); + thread.IsBackground = true; + thread.Start(); + + return tcs.Task; } private static string? GetParsingName(IShellItem shellItem)