diff --git a/.github/workflows/package-submissions.yml b/.github/workflows/package-submissions.yml
index 5ef20fec67c..e99a5e73789 100644
--- a/.github/workflows/package-submissions.yml
+++ b/.github/workflows/package-submissions.yml
@@ -1,4 +1,4 @@
-name: Submit Microsoft.PowerToys package to Windows Package Manager Community Repository
+name: Submit Microsoft.PowerToys package to Windows Package Manager Community Repository
# based off of https://github.com/nushell/nushell/blob/main/.github/workflows/winget-submission.yml
on:
@@ -7,24 +7,25 @@ on:
types: [published]
jobs:
-
winget:
name: Publish winget package
runs-on: windows-latest
steps:
- name: Submit package to Windows Package Manager Community Repository
run: |
-
+
$wingetPackage = "Microsoft.PowerToys"
$gitToken = "${{ secrets.PT_WINGET }}"
-
+
$github = Invoke-RestMethod -uri "https://api.github.com/repos/Microsoft/PowerToys/releases"
-
+
$targetRelease = $github | Where-Object -Property name -match 'Release'| Select -First 1
- $installerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*x64' | Select -ExpandProperty browser_download_url
- $installerUrlArm = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*arm64' | Select -ExpandProperty browser_download_url
+ $installerUserX64Url = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysUserSetup.*x64' | Select -ExpandProperty browser_download_url
+ $installerMachineX64Url = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*x64' | Select -ExpandProperty browser_download_url
+ $installerUserArmUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysUserSetup.*arm64' | Select -ExpandProperty browser_download_url
+ $installerMachineArmUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*arm64' | Select -ExpandProperty browser_download_url
$ver = $targetRelease.tag_name.Trim("v")
-
+
# getting latest wingetcreate file
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
- .\wingetcreate.exe update $wingetPackage -s -v $ver -u $installerUrl $installerUrlArm -t $gitToken
+ .\wingetcreate.exe update $wingetPackage -s -v $ver -u $installerUserX64Url $installerMachineX64Url $installerUserArmUrl $installerMachineArmUrl -t $gitToken
diff --git a/README.md b/README.md
index 1c9fb7fc426..0031147aba5 100644
--- a/README.md
+++ b/README.md
@@ -37,10 +37,10 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
Go to [Microsoft PowerToys GitHub releases page][github-release-link], click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user.
- - **For x64 processors (most common) per-user installer:** [PowerToysUserSetup-0.69.0-x64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.69.0/PowerToysUserSetup-0.69.0-x64.exe)
- - **For x64 processors per-machine installer:** [PowerToysSetup-0.69.0-x64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.69.0/PowerToysSetup-0.69.0-x64.exe)
- - **For ARM64 processors per-user installer:** [PowerToysUserSetup-0.69.0-arm64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.69.0/PowerToysUserSetup-0.69.0-arm64.exe)
- - **For ARM64 processors per-machine installer:** [PowerToysSetup-0.69.0-arm64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.69.0/PowerToysSetup-0.69.0-arm64.exe)
+ - **For x64 processors (most common) per-user installer:** [PowerToysUserSetup-0.69.1-x64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.69.1/PowerToysUserSetup-0.69.1-x64.exe)
+ - **For x64 processors per-machine installer:** [PowerToysSetup-0.69.1-x64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.69.1/PowerToysSetup-0.69.1-x64.exe)
+ - **For ARM64 processors per-user installer:** [PowerToysUserSetup-0.69.1-arm64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.69.1/PowerToysUserSetup-0.69.1-arm64.exe)
+ - **For ARM64 processors per-machine installer:** [PowerToysSetup-0.69.1-arm64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.69.1/PowerToysSetup-0.69.1-arm64.exe)
This is our preferred method.
diff --git a/installer/PowerToysSetup/FileLocksmith.wxs b/installer/PowerToysSetup/FileLocksmith.wxs
index d0d49c3bb5a..f2417ed0f0a 100644
--- a/installer/PowerToysSetup/FileLocksmith.wxs
+++ b/installer/PowerToysSetup/FileLocksmith.wxs
@@ -35,6 +35,15 @@
+
+
+
+
+
+
+
+
+
@@ -42,8 +51,11 @@
+
+
+
diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs
index 8ed3729a452..a45d0e40448 100644
--- a/installer/PowerToysSetup/Product.wxs
+++ b/installer/PowerToysSetup/Product.wxs
@@ -452,6 +452,9 @@
+
+
+
@@ -501,6 +504,9 @@
+
+
+
diff --git a/installer/PowerToysSetup/RegistryPreview.wxs b/installer/PowerToysSetup/RegistryPreview.wxs
index 7beeb08f4fb..74804f6c6e8 100644
--- a/installer/PowerToysSetup/RegistryPreview.wxs
+++ b/installer/PowerToysSetup/RegistryPreview.wxs
@@ -22,6 +22,15 @@
+
+
+
+
+
+
+
+
+
@@ -29,7 +38,10 @@
+
+
+
diff --git a/installer/PowerToysSetup/generateFileList.ps1 b/installer/PowerToysSetup/generateFileList.ps1
index 5925ebedb20..7e0d8548a0a 100644
--- a/installer/PowerToysSetup/generateFileList.ps1
+++ b/installer/PowerToysSetup/generateFileList.ps1
@@ -49,7 +49,7 @@ if ($isWinAppSdkProj -eq $True) {
$fileExclusionList = @("*Test*", "*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe") + $interopFilesList + $winAppSDKfilesList
-$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*png", "*gif", "*ico", "*cur", "*svg", "index.html", "reg.js", "monacoSpecialLanguages.js", "resources.pri")
+$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*png", "*gif", "*ico", "*cur", "*svg", "index.html", "reg.js", "monacoSpecialLanguages.js", "resources.pri", "NLog.config")
$dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll")
diff --git a/src/modules/registrypreview/RegistryPreviewUI/FileName.cs b/src/modules/registrypreview/RegistryPreviewUI/FileName.cs
new file mode 100644
index 00000000000..7464fff039e
--- /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 949bb3b7b0c..90eb9f2c24e 100644
--- a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs
+++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs
@@ -134,18 +134,18 @@ private async void OpenButton_Click(object sender, RoutedEventArgs e)
}
}
- // 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 @@ private void SaveButton_Click(object sender, RoutedEventArgs e)
///
/// 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");
-
- // 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)
+ // 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"));
+
+ 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/MainWindow.xaml b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml
index e8c8e4b4ae3..b3125844bbf 100644
--- a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml
+++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml
@@ -53,6 +53,7 @@
+
+
+
@@ -75,11 +78,13 @@
x:Uid="SaveButton"
HorizontalAlignment="Left"
Click="SaveButton_Click"
- Icon="Save"
IsEnabled="False"
IsTabStop="False">
+
+
+
-
+
+
+
+
-
+
+
+
+
@@ -110,8 +119,10 @@
x:Uid="RefreshButton"
HorizontalAlignment="Left"
Click="RefreshButton_Click"
- Icon="Refresh"
IsTabStop="False">
+
+
+
@@ -121,8 +132,10 @@
x:Uid="WriteButton"
HorizontalAlignment="Left"
Click="WriteButton_Click"
- Icon="Share"
IsTabStop="False">
+
+
+
@@ -132,8 +145,10 @@
x:Uid="RegistryButton"
HorizontalAlignment="Left"
Click="RegistryButton_Click"
- Icon="Go"
IsTabStop="False">
+
+
+
@@ -161,7 +176,9 @@
ScrollViewer.IsVerticalRailEnabled="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
TabIndex="0"
- TextWrapping="NoWrap" />
+ TextWrapping="NoWrap"
+ CornerRadius="{StaticResource OverlayCornerRadius}"
+ />
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 00000000000..0e0f2d58a79
--- /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 6108bf8eec5..d64bc8f063c 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
diff --git a/src/settings-ui/Settings.UI.Library/AwakeProperties.cs b/src/settings-ui/Settings.UI.Library/AwakeProperties.cs
index e83161f8e64..6e9735d7b60 100644
--- a/src/settings-ui/Settings.UI.Library/AwakeProperties.cs
+++ b/src/settings-ui/Settings.UI.Library/AwakeProperties.cs
@@ -16,7 +16,7 @@ public AwakeProperties()
Mode = AwakeMode.PASSIVE;
IntervalHours = 0;
IntervalMinutes = 0;
- ExpirationDateTime = DateTimeOffset.MinValue;
+ ExpirationDateTime = DateTimeOffset.Now;
CustomTrayTimes = new Dictionary();
}
diff --git a/src/settings-ui/Settings.UI.Library/AwakeSettings.cs b/src/settings-ui/Settings.UI.Library/AwakeSettings.cs
index e47cf82bc02..f811dbca375 100644
--- a/src/settings-ui/Settings.UI.Library/AwakeSettings.cs
+++ b/src/settings-ui/Settings.UI.Library/AwakeSettings.cs
@@ -37,7 +37,9 @@ public object Clone()
KeepDisplayOn = Properties.KeepDisplayOn,
IntervalMinutes = Properties.IntervalMinutes,
IntervalHours = Properties.IntervalHours,
- ExpirationDateTime = Properties.ExpirationDateTime,
+
+ // Fix old buggy default value that might be saved in Settings. Some components don't deal well with negative time zones and minimum time offsets.
+ ExpirationDateTime = Properties.ExpirationDateTime.Year < 2 ? DateTimeOffset.Now : Properties.ExpirationDateTime,
},
};
}
diff --git a/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutControl.xaml.cs b/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutControl.xaml.cs
index 38ae3adb595..c9b9035049d 100644
--- a/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutControl.xaml.cs
+++ b/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutControl.xaml.cs
@@ -89,7 +89,10 @@ public ShortcutControl()
hook = new HotkeySettingsControlHook(Hotkey_KeyDown, Hotkey_KeyUp, Hotkey_IsActive, FilterAccessibleKeyboardEvents);
ResourceLoader resourceLoader = ResourceLoader.GetForViewIndependentUse();
- App.GetSettingsWindow().Activated += ShortcutDialog_SettingsWindow_Activated;
+ if (App.GetSettingsWindow() != null)
+ {
+ App.GetSettingsWindow().Activated += ShortcutDialog_SettingsWindow_Activated;
+ }
// We create the Dialog in C# because doing it in XAML is giving WinUI/XAML Island bugs when using dark theme.
shortcutDialog = new ContentDialog
@@ -113,10 +116,18 @@ private void ShortcutControl_Unloaded(object sender, RoutedEventArgs e)
shortcutDialog.Opened -= ShortcutDialog_Opened;
shortcutDialog.Closing -= ShortcutDialog_Closing;
- App.GetSettingsWindow().Activated -= ShortcutDialog_SettingsWindow_Activated;
+ if (App.GetSettingsWindow() != null)
+ {
+ App.GetSettingsWindow().Activated -= ShortcutDialog_SettingsWindow_Activated;
+ }
// Dispose the HotkeySettingsControlHook object to terminate the hook threads when the textbox is unloaded
- hook.Dispose();
+ if (hook != null)
+ {
+ hook.Dispose();
+ }
+
+ hook = null;
}
private void KeyEventHandler(int key, bool matchValue, int matchValueCode)
@@ -371,15 +382,16 @@ private static bool ComboIsValid(HotkeySettings settings)
private void ShortcutDialog_SettingsWindow_Activated(object sender, WindowActivatedEventArgs args)
{
args.Handled = true;
- if (args.WindowActivationState != WindowActivationState.Deactivated && hook.GetDisposedState() == true)
+ if (args.WindowActivationState != WindowActivationState.Deactivated && (hook == null || hook.GetDisposedState() == true))
{
// If the PT settings window gets focussed/activated again, we enable the keyboard hook to catch the keyboard input.
hook = new HotkeySettingsControlHook(Hotkey_KeyDown, Hotkey_KeyUp, Hotkey_IsActive, FilterAccessibleKeyboardEvents);
}
- else if (args.WindowActivationState == WindowActivationState.Deactivated && hook.GetDisposedState() == false)
+ else if (args.WindowActivationState == WindowActivationState.Deactivated && hook != null && hook.GetDisposedState() == false)
{
// If the PT settings window lost focus/activation, we disable the keyboard hook to allow keyboard input on other windows.
hook.Dispose();
+ hook = null;
}
}
@@ -394,7 +406,12 @@ private void Dispose(bool disposing)
{
if (disposing)
{
- hook.Dispose();
+ if (hook != null)
+ {
+ hook.Dispose();
+ }
+
+ hook = null;
}
disposedValue = true;
diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeWhatsNew.xaml.cs b/src/settings-ui/Settings.UI/OOBE/Views/OobeWhatsNew.xaml.cs
index fd35eafc395..d5cece086ba 100644
--- a/src/settings-ui/Settings.UI/OOBE/Views/OobeWhatsNew.xaml.cs
+++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeWhatsNew.xaml.cs
@@ -55,8 +55,9 @@ public OobeWhatsNew()
///
/// Regex to remove installer hash sections from the release notes.
///
- private const string RemoveInstallerHashesRegex = @"((\r\n)+#+ installer hashes)?((\r\n)+#+( x64)?( arm64)? installer( SHA256)? hash(\r\n)+[0-9A-F]{64})+";
- private const RegexOptions RemoveInstallerHashesRegexOptions = RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
+ private const string RemoveInstallerHashesRegex = @"(\r\n)+## Installer Hashes(\r\n.*)+## Highlights";
+ private const string RemoveHotFixInstallerHashesRegex = @"(\r\n)+## Installer Hashes(\r\n.*)+$";
+ private const RegexOptions RemoveInstallerHashesRegexOptions = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
private static async Task GetReleaseNotesMarkdown()
{
@@ -86,10 +87,14 @@ private static async Task GetReleaseNotesMarkdown()
// Regex to remove installer hash sections from the release notes.
Regex removeHashRegex = new Regex(RemoveInstallerHashesRegex, RemoveInstallerHashesRegexOptions);
+ // Regex to remove installer hash sections from the release notes, since there'll be no Highlights section for hotfix releases.
+ Regex removeHotfixHashRegex = new Regex(RemoveHotFixInstallerHashesRegex, RemoveInstallerHashesRegexOptions);
+
foreach (var release in latestReleases)
{
releaseNotesHtmlBuilder.AppendLine("# " + release.Name);
- var notes = removeHashRegex.Replace(release.ReleaseNotes, string.Empty);
+ var notes = removeHashRegex.Replace(release.ReleaseNotes, "\r\n## Highlights");
+ notes = removeHotfixHashRegex.Replace(notes, string.Empty);
releaseNotesHtmlBuilder.AppendLine(notes);
releaseNotesHtmlBuilder.AppendLine(" ");
}