From 28144b63759a6fd88aaeeb03415f10e5090bbaaf Mon Sep 17 00:00:00 2001 From: sosssego Date: Mon, 27 Feb 2023 09:42:50 +0000 Subject: [PATCH] [Settings]Fix backup and restore select folder when running as admin (#24164) * Settings bkp and restore Open foolder when elevated using shell32 api. * increase the size of the alocated buffer for the path based on https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry --- .../Settings.UI/Helpers/ShellGetFolder.cs | 72 +++++++++++++++++++ .../Settings.UI/Views/GeneralPage.xaml.cs | 11 +-- 2 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 src/settings-ui/Settings.UI/Helpers/ShellGetFolder.cs diff --git a/src/settings-ui/Settings.UI/Helpers/ShellGetFolder.cs b/src/settings-ui/Settings.UI/Helpers/ShellGetFolder.cs new file mode 100644 index 00000000000..9b44c885000 --- /dev/null +++ b/src/settings-ui/Settings.UI/Helpers/ShellGetFolder.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using System.Text; +using static Microsoft.PowerToys.Settings.UI.Helpers.ShellGetFolder; + +namespace Microsoft.PowerToys.Settings.UI.Helpers +{ + public class ShellGetFolder + { + [DllImport("shell32.dll")] + private static extern IntPtr SHBrowseForFolderW(ref BrowseInformation browseInfo); + + [DllImport("shell32.dll")] + private static extern int SHGetPathFromIDListW(IntPtr pidl, IntPtr pszPath); + + public delegate int BrowseCallbackProc(IntPtr hwnd, int msg, IntPtr lp, IntPtr wp); + + [StructLayout(LayoutKind.Sequential)] + public struct BrowseInformation + { + public IntPtr HwndOwner; + public IntPtr PidlRoot; + public string PszDisplayName; + public string LpszTitle; + public uint UlFlags; + public BrowseCallbackProc Lpfn; + public IntPtr LParam; + public int IImage; + } + + public static string GetFolderDialog(IntPtr hwndOwner) + { + // windows MAX_PATH with long path enable can be approximated 32k char long + // allocating more than double (unicode) to hold the path + StringBuilder sb = new StringBuilder(65000); + IntPtr bufferAddress = Marshal.AllocHGlobal(65000); + IntPtr pidl = IntPtr.Zero; + BrowseInformation browseInfo; + browseInfo.HwndOwner = hwndOwner; + browseInfo.PidlRoot = IntPtr.Zero; + browseInfo.PszDisplayName = null; + browseInfo.LpszTitle = null; + browseInfo.UlFlags = 0; + browseInfo.Lpfn = null; + browseInfo.LParam = IntPtr.Zero; + browseInfo.IImage = 0; + + try + { + pidl = SHBrowseForFolderW(ref browseInfo); + if (SHGetPathFromIDListW(pidl, bufferAddress) == 0) + { + return null; + } + + sb.Append(Marshal.PtrToStringUni(bufferAddress)); + Marshal.FreeHGlobal(bufferAddress); + } + finally + { + // Need to free pidl + Marshal.FreeCoTaskMem(pidl); + } + + return sb.ToString(); + } + } +} diff --git a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs index f3c16a8ad60..3a3a68ca11d 100644 --- a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; +using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.ViewModels; @@ -142,12 +143,12 @@ private void UpdateBackupAndRestoreStatusText(Microsoft.UI.Xaml.Documents.Hyperl private async Task PickSingleFolderDialog() { - var openPicker = new FolderPicker(); + // This function was changed to use the shell32 API to open folder dialog + // as the old one (PickSingleFolderAsync) can't work when the process is elevated + // TODO: go back PickSingleFolderAsync when it's fixed var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.GetSettingsWindow()); - WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hwnd); - openPicker.FileTypeFilter.Add("*"); - var folder = await openPicker.PickSingleFolderAsync(); - return folder?.Path; + string r = await Task.FromResult(ShellGetFolder.GetFolderDialog(hwnd)); + return r; } } }