Skip to content

Commit

Permalink
[Settings]Fix backup and restore select folder when running as admin (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
sosssego committed Feb 27, 2023
1 parent eeca301 commit 28144b6
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 5 deletions.
72 changes: 72 additions & 0 deletions 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();
}
}
}
11 changes: 6 additions & 5 deletions src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs
Expand Up @@ -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;
Expand Down Expand Up @@ -142,12 +143,12 @@ private void UpdateBackupAndRestoreStatusText(Microsoft.UI.Xaml.Documents.Hyperl

private async Task<string> 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<string>(ShellGetFolder.GetFolderDialog(hwnd));
return r;
}
}
}

0 comments on commit 28144b6

Please sign in to comment.