From 1dc013f4127368b34e1b34689e3a9d6bb2a79329 Mon Sep 17 00:00:00 2001 From: Randy Date: Tue, 11 Apr 2023 14:29:40 -0700 Subject: [PATCH] [Registry Preview] Moving to a different API to call the File Picker (#25260) * Moving from FileOpenPicker Moving from FileOpenPicker to a Win32/PInvoke version, so it can be opened while running as Admin. * Update Resources.resw Replacing a lost string. * Save file picker also crashed Switched to Win32-based SafeFilePicker Cleaned up some of the code which should now pass spell checking and removed pragmas --- .../RegistryPreviewUI/FileName.cs | 39 +++++++++++++++ .../RegistryPreviewUI/MainWindow.Events.cs | 50 +++++++++---------- .../RegistryPreviewUI/OpenFileName.cs | 36 +++++++++++++ .../RegistryPreviewUI.csproj | 1 + .../RegistryPreviewUI/SaveFileName.cs | 38 ++++++++++++++ .../Strings/en-US/Resources.resw | 10 +++- 6 files changed, 145 insertions(+), 29 deletions(-) create mode 100644 src/modules/registrypreview/RegistryPreviewUI/FileName.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUI/OpenFileName.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUI/SaveFileName.cs diff --git a/src/modules/registrypreview/RegistryPreviewUI/FileName.cs b/src/modules/registrypreview/RegistryPreviewUI/FileName.cs new file mode 100644 index 0000000000..7464fff039 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/FileName.cs @@ -0,0 +1,39 @@ +// 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; + +namespace RegistryPreview +{ + // Workaround for File Pickers that don't work while running as admin, per: + // https://github.com/microsoft/WindowsAppSDK/issues/2504 + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct FileName + { + public int StructSize; + public IntPtr HwndOwner; + public IntPtr Instance; + public string Filter; + public string CustomFilter; + public int MaxCustFilter; + public int FilterIndex; + public string File; + public int MaxFile; + public string FileTitle; + public int MaxFileTitle; + public string InitialDir; + public string Title; + public int Flags; + public short FileOffset; + public short FileExtension; + public string DefExt; + public IntPtr CustData; + public IntPtr Hook; + public string TemplateName; + public IntPtr PtrReserved; + public int Reserved; + public int FlagsEx; + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs index 949bb3b7b0..90eb9f2c24 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs @@ -134,18 +134,18 @@ namespace RegistryPreview } } - // Pull in a new REG file - FileOpenPicker fileOpenPicker = new FileOpenPicker(); - fileOpenPicker.ViewMode = PickerViewMode.List; - fileOpenPicker.CommitButtonText = resourceLoader.GetString("OpenButtonText"); - fileOpenPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary; - fileOpenPicker.FileTypeFilter.Add(".reg"); + // Pull in a new REG file - we have to use the direct Win32 method because FileOpenPicker crashes when it's + // called while running as admin + string filename = OpenFilePicker.ShowDialog( + resourceLoader.GetString("FilterRegistryName") + '\0' + "*.reg" + '\0' + resourceLoader.GetString("FilterAllFiles") + '\0' + "*.*" + '\0' + '\0', + resourceLoader.GetString("OpenDialogTitle")); - // Get the HWND so we an open the modal - IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this); - InitializeWithWindow.Initialize(fileOpenPicker, hWnd); + if (filename == string.Empty || File.Exists(filename) == false) + { + return; + } - StorageFile storageFile = await fileOpenPicker.PickSingleFileAsync(); + StorageFile storageFile = await StorageFile.GetFileFromPathAsync(filename); if (storageFile != null) { @@ -174,27 +174,23 @@ namespace RegistryPreview /// /// Uses a picker to save out a copy of the current reg file /// - private async void SaveAsButton_Click(object sender, RoutedEventArgs e) + private void SaveAsButton_Click(object sender, RoutedEventArgs e) { - // Save out a new REG file and then open it - FileSavePicker fileSavePicker = new FileSavePicker(); - fileSavePicker.CommitButtonText = resourceLoader.GetString("SaveButtonText"); - fileSavePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary; - fileSavePicker.FileTypeChoices.Add("Registry file", new List() { ".reg" }); - fileSavePicker.SuggestedFileName = resourceLoader.GetString("SuggestFileName"); + // Save out a new REG file and then open it - we have to use the direct Win32 method because FileOpenPicker crashes when it's + // called while running as admin + string filename = SaveFilePicker.ShowDialog( + resourceLoader.GetString("SuggestFileName"), + resourceLoader.GetString("FilterRegistryName") + '\0' + "*.reg" + '\0' + resourceLoader.GetString("FilterAllFiles") + '\0' + "*.*" + '\0' + '\0', + resourceLoader.GetString("SaveDialogTitle")); - // Get the HWND so we an save the modal - IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this); - InitializeWithWindow.Initialize(fileSavePicker, hWnd); - - StorageFile storageFile = await fileSavePicker.PickSaveFileAsync(); - - if (storageFile != null) + if (filename == string.Empty) { - App.AppFilename = storageFile.Path; - SaveFile(); - UpdateToolBarAndUI(OpenRegistryFile(App.AppFilename)); + return; } + + App.AppFilename = filename; + SaveFile(); + UpdateToolBarAndUI(OpenRegistryFile(App.AppFilename)); } /// diff --git a/src/modules/registrypreview/RegistryPreviewUI/OpenFileName.cs b/src/modules/registrypreview/RegistryPreviewUI/OpenFileName.cs new file mode 100644 index 0000000000..c6dc243aeb --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/OpenFileName.cs @@ -0,0 +1,36 @@ +// 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.Runtime.InteropServices; + +namespace RegistryPreview +{ + // Workaround for File Pickers that don't work while running as admin, per: + // https://github.com/microsoft/WindowsAppSDK/issues/2504 + public static partial class OpenFilePicker + { + [DllImport("comdlg32.dll", SetLastError = true, CharSet = CharSet.Auto)] + private static extern bool GetOpenFileName(ref FileName openFileName); + + public static string ShowDialog(string filter, string dialogTitle) + { + FileName openFileName = default(FileName); + openFileName.StructSize = Marshal.SizeOf(openFileName); + + openFileName.Filter = filter; + openFileName.File = new string(new char[256]); + openFileName.MaxFile = openFileName.File.Length; + openFileName.FileTitle = new string(new char[64]); + openFileName.MaxFileTitle = openFileName.FileTitle.Length; + openFileName.Title = dialogTitle; + + if (GetOpenFileName(ref openFileName)) + { + return openFileName.File; + } + + return string.Empty; + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewUI.csproj b/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewUI.csproj index 4abd57ccda..9e68b9e7b4 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewUI.csproj +++ b/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewUI.csproj @@ -24,6 +24,7 @@ PowerToys RegistryPreview RegistryPreview true + true diff --git a/src/modules/registrypreview/RegistryPreviewUI/SaveFileName.cs b/src/modules/registrypreview/RegistryPreviewUI/SaveFileName.cs new file mode 100644 index 0000000000..0e0f2d58a7 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/SaveFileName.cs @@ -0,0 +1,38 @@ +// 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.Runtime.InteropServices; + +namespace RegistryPreview +{ + // Workaround for File Pickers that don't work while running as admin, per: + // https://github.com/microsoft/WindowsAppSDK/issues/2504 + public static partial class SaveFilePicker + { + [DllImport("comdlg32.dll", SetLastError = true, CharSet = CharSet.Auto)] + private static extern bool GetSaveFileName(ref FileName saveFileName); + + public static string ShowDialog(string suggestedFilename, string filter, string dialogTitle) + { + FileName saveFileName = default(FileName); + saveFileName.StructSize = Marshal.SizeOf(saveFileName); + + saveFileName.Filter = filter; + saveFileName.File = new string(new char[256]); + saveFileName.MaxFile = saveFileName.File.Length; + saveFileName.File = string.Concat(suggestedFilename, saveFileName.File); + saveFileName.FileTitle = new string(new char[64]); + saveFileName.MaxFileTitle = saveFileName.FileTitle.Length; + saveFileName.Title = dialogTitle; + saveFileName.DefExt = "reg"; + + if (GetSaveFileName(ref saveFileName)) + { + return saveFileName.File; + } + + return string.Empty; + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw b/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw index 6108bf8eec..d64bc8f063 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw +++ b/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw @@ -132,6 +132,12 @@ The REG file cannot be written to. + + All files (*.*) + + + Registry files (*.reg) + doesn't appear to be a valid registry file! @@ -153,8 +159,8 @@ Open file... - - Open + + Open Registry file... Reload from file