Compare commits

...

6 Commits

Author SHA1 Message Date
Peiyao Zhao (from Dev Box)
5a284c89a3 test 2025-06-12 17:28:52 +08:00
Dustin L. Howett
6e0f1819d5 build: adjust for changes in the Az.Accounts module (#40001)
They made secure strings the default. Doing it this way maintains
compatibility with the version before and after the default changed.

This fixes symbol publication.
2025-06-12 09:53:40 +08:00
Kai Tao
3f834d7a6b Build: Change CLR projects to portable type pdb (#39992)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Since now githubcopilot is easier to used in vscode, would like to set
up debug in vscode. Full type pdb can't be loaded in visual studio code
debugging
Change the debugging type to make it debuggable from vscode.
<img width="800" alt="image"
src="https://github.com/user-attachments/assets/86e46188-ad42-4fbf-b8af-8e0353b1c615"
/>
from:
e6049bb603/Documentation/diagnostics/portable_pdb.md

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Can debug settings in vscode.
2025-06-12 09:44:32 +08:00
Gordon Lam
e9a79f5d6f Add the detail section (#39991)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
The `verifyNoticeMdAgainstNugetPackages.ps1` script previously only
reported "Notice.md does not match NuGet list." without providing any
details about which packages were different, making it difficult to
debug discrepancies.

## Changes Made

This enhancement adds detailed difference reporting when NuGet packages
don't match:

- **Extracts current package list from NOTICE.md** using regex pattern
matching
- **Shows packages missing from NOTICE.md** (highlighted in red)
- **Shows packages in NOTICE.md but not in generated list** (highlighted
in yellow)
- **Provides summary statistics** with package counts
- **Maintains backward compatibility** - all existing functionality
preserved

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
If there is no difference, the same behaviour.

If there is difference, here is the example:

![image](https://github.com/user-attachments/assets/63dad21a-9db5-4d20-8a2c-ddd44ce1ee80)

## Edge Cases Handled

- **Missing NuGet section**: Shows warning and treats as empty package
list
- **Empty package lists**: Handles gracefully with appropriate counts
- **Matching lists**: No additional output (preserves existing behavior)

This enhancement significantly improves the debugging experience when
NuGet package verification fails by providing specific, actionable
information about which packages need attention.
2025-06-12 09:15:25 +08:00
Dave Rayment
a9c5117f61 Command Palette: fix sample links in README.md (#39983)
Fix links to sample folders.

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Two links were broken in the Command Palette readme because of a folder
name being "Exts" instead of "ext".

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end user facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
N/A

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Manually confirmed the following links are correct:


https://github.com/microsoft/PowerToys/tree/main/src/modules/cmdpal/ext/SamplePagesExtension

https://github.com/microsoft/PowerToys/tree/main/src/modules/cmdpal/ext/ProcessMonitorExtension
2025-06-12 07:08:23 +08:00
rovercoder
b9aac70de5 Spell Checking Fixes (#39985)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Fixes spelling issues and spell checker warnings
2025-06-11 16:32:38 +00:00
11 changed files with 329 additions and 123 deletions

View File

@@ -92,6 +92,7 @@
^\.github/actions/spell-check/
^\.gitmodules$
^\Q.github/workflows/spelling2.yml\E$
^\Q.pipelines/272MSSharedLibSN2048.snk\E$
^\Q.pipelines/ESRPSigning_core.json\E$
^\Qdoc/devdocs/localization.md\E$
^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$
@@ -120,6 +121,7 @@
^src/modules/MouseWithoutBorders/App/Form/.*\.resx$
^src/modules/MouseWithoutBorders/App/Helper/.*\.resx$
^src/modules/previewpane/UnitTests-MarkdownPreviewHandler/HelperFiles/MarkdownWithHTMLImageTag\.txt$
^src/modules/ZoomIt/ZoomIt/ZoomIt\.idc$
^src/Monaco/
^src/common/sysinternals/Eula/
^tools/Verification scripts/Check preview handler registration\.ps1$

View File

@@ -328,7 +328,6 @@ DEFAULTTONULL
DEFAULTTOPRIMARY
DEFERERASE
DEFPUSHBUTTON
DEFT
deinitialization
DELA
DELETEDKEYIMAGE
@@ -670,7 +669,6 @@ IBeam
icf
ICONERROR
ICONLOCATION
idc
IDCANCEL
IDD
idk
@@ -681,7 +679,6 @@ IDR
IDXGI
ietf
IEXPLORE
iextn
IFACEMETHOD
IFACEMETHODIMP
IFile
@@ -720,7 +717,6 @@ INPUTMOUSE
INPUTSINK
INPUTTYPE
INSTALLDESKTOPSHORTCUT
INSTALLDIR
installdir
INSTALLFOLDER
INSTALLFOLDERTOBOOTSTRAPPERINSTALLFOLDER
@@ -812,7 +808,6 @@ LMENU
lnks
LOADFROMFILE
LOBYTE
localappdata
LOCALDISPLAY
localpackage
LOCALSYSTEM
@@ -977,7 +972,6 @@ msrc
msstore
mst
msvcp
msvsmon
MTND
MULTIPLEUSE
multizone
@@ -1284,7 +1278,6 @@ pstm
PStr
pstream
pstrm
pswd
PSYSTEM
psz
ptb
@@ -1476,16 +1469,12 @@ SHELLDLL
shellex
SHELLEXECUTEINFO
SHELLEXECUTEINFOW
SHELLEXTENSION
SHELLICONSIZE
SHELLNEWVALUE
SHFILEINFO
SHFILEOPSTRUCT
SHGDN
SHGDNF
SHGFI
SHGFIICON
SHGFILARGEICON
SHIL
shinfo
shlwapi
@@ -1519,7 +1508,6 @@ siex
sigdn
SIGNINGSCENARIO
signtool
Signtool
SINGLEKEY
sipolicy
SIZEBOX
@@ -1695,7 +1683,6 @@ TLayout
tlb
tlbimp
tlc
TGM
TNP
Toolhelp
toolkitconverters
@@ -1783,7 +1770,6 @@ uxtheme
vabdq
validmodulename
valuegenerator
VARENUM
variantassignment
vcamp
VCENTER
@@ -2003,11 +1989,6 @@ CLSCTXINPROCALL
IIDI
irow
lcid
OTHERUNZOOM
OTHERZOOM
PARENTCLOSING
PARENTOPENING
ppwsz
rguid
SCROLLCHILDREN
VARTYPE
VARTYPE

View File

@@ -243,4 +243,4 @@ Process Process
# ZoomIt menu items with accelerator keys
E&xit
St&yle
St&yle

View File

@@ -68,7 +68,7 @@ jobs:
pwsh: true
ScriptType: InlineScript
Inline: |-
$AzToken = (Get-AzAccessToken -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token
$AzToken = (Get-AzAccessToken -AsSecureString -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token | ConvertFrom-SecureString -AsPlainText
Write-Host "##vso[task.setvariable variable=SymbolAccessToken;issecret=true]$AzToken"

View File

@@ -72,9 +72,57 @@ $returnList = [System.Collections.Generic.HashSet[string]]($totalList) -join "`r
Write-Host $returnList
# Extract the current package list from NOTICE.md
$noticePattern = "## NuGet Packages used by PowerToys\s*((?:\r?\n- .+)+)"
$noticeMatch = [regex]::Match($noticeFile, $noticePattern)
if ($noticeMatch.Success) {
$currentNoticePackageList = $noticeMatch.Groups[1].Value.Trim()
} else {
Write-Warning "Warning: Could not find 'NuGet Packages used by PowerToys' section in NOTICE.md"
$currentNoticePackageList = ""
}
if (!$noticeFile.Trim().EndsWith($returnList.Trim()))
{
Write-Host -ForegroundColor Red "Notice.md does not match NuGet list."
# Show detailed differences
$generatedPackages = $returnList -split "`r`n|`n" | Where-Object { $_.Trim() -ne "" } | Sort-Object
$noticePackages = $currentNoticePackageList -split "`r`n|`n" | Where-Object { $_.Trim() -ne "" } | ForEach-Object { $_.Trim() } | Sort-Object
Write-Host ""
Write-Host -ForegroundColor Cyan "=== DETAILED DIFFERENCE ANALYSIS ==="
Write-Host ""
# Find packages in proj file list but not in NOTICE.md
$missingFromNotice = $generatedPackages | Where-Object { $noticePackages -notcontains $_ }
if ($missingFromNotice.Count -gt 0) {
Write-Host -ForegroundColor Red "MissingFromNotice:"
foreach ($pkg in $missingFromNotice) {
Write-Host -ForegroundColor Red " $pkg"
}
Write-Host ""
}
# Find packages in NOTICE.md but not in proj file list
$extraInNotice = $noticePackages | Where-Object { $generatedPackages -notcontains $_ }
if ($extraInNotice.Count -gt 0) {
Write-Host -ForegroundColor Yellow "ExtraInNotice:"
foreach ($pkg in $extraInNotice) {
Write-Host -ForegroundColor Yellow " $pkg"
}
Write-Host ""
}
# Show counts for summary
Write-Host -ForegroundColor Cyan "Summary:"
Write-Host " Proj file list has $($generatedPackages.Count) packages"
Write-Host " NOTICE.md has $($noticePackages.Count) packages"
Write-Host " MissingFromNotice: $($missingFromNotice.Count) packages"
Write-Host " ExtraInNotice: $($extraInNotice.Count) packages"
Write-Host ""
exit 1
}

View File

@@ -22,7 +22,7 @@
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DebugSymbols>true</DebugSymbols>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
</PropertyGroup>
@@ -43,4 +43,4 @@
<Analyzer Remove="@(Analyzer)" Condition="%(Analyzer.NuGetPackageId) == 'Microsoft.Windows.CsWinRT'" />
</ItemGroup>
</Target>
</Project>
</Project>

View File

@@ -18,7 +18,7 @@ public sealed partial class ContentFormControl : UserControl
// LOAD-BEARING: if you don't hang onto a reference to the RenderedAdaptiveCard
// then the GC might clean it up sometime, even while the card is in the UI
// tree. If this gets GC'd, then it'll revoke our Action handler, and the
// tree. If this gets GC'ed, then it'll revoke our Action handler, and the
// form will do seemingly nothing.
private RenderedAdaptiveCard? _renderedCard;

View File

@@ -39,8 +39,8 @@ Projects of interest are:
[Initial SDK Spec]: ./doc/initial-sdk-spec/initial-sdk-spec.md
[generic samples]: ./Exts/SamplePagesExtension
[real samples]: ./Exts/ProcessMonitorExtension
[generic samples]: ./ext/SamplePagesExtension
[real samples]: ./ext/ProcessMonitorExtension
[real extensions that we've "shipped" already]: https://github.com/zadjii/CmdPalExtensions/blob/main/src/extensions

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// 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.
@@ -23,6 +23,10 @@ namespace RegistryPreviewUILib
{
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
// Indicator if we loaded/reloaded/saved a file and need to skip TextChanged event one time.
// (Solves the problem that enabling the event handler fires it one time.)
private static bool editorContentChangedScripted;
/// <summary>
/// Event that is will prevent the app from closing if the "save file" flag is active
/// </summary>
@@ -77,6 +81,67 @@ namespace RegistryPreviewUILib
MonacoEditor.Focus(FocusState.Programmatic);
}
/// <summary>
/// New button action: Ask to save last changes and reset editor content to reg header only
/// </summary>
private async void NewButton_Click(object sender, RoutedEventArgs e)
{
// Check to see if the current file has been saved
if (saveButton.IsEnabled)
{
ContentDialog contentDialog = new ContentDialog()
{
Title = resourceLoader.GetString("YesNoCancelDialogTitle"),
Content = resourceLoader.GetString("YesNoCancelDialogContent"),
PrimaryButtonText = resourceLoader.GetString("YesNoCancelDialogPrimaryButtonText"),
SecondaryButtonText = resourceLoader.GetString("YesNoCancelDialogSecondaryButtonText"),
CloseButtonText = resourceLoader.GetString("YesNoCancelDialogCloseButtonText"),
DefaultButton = ContentDialogButton.Primary,
};
// Use this code to associate the dialog to the appropriate AppWindow by setting
// the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
contentDialog.XamlRoot = this.Content.XamlRoot;
}
ContentDialogResult contentDialogResult = await contentDialog.ShowAsync();
switch (contentDialogResult)
{
case ContentDialogResult.Primary:
// Save, then continue the new action
if (!AskFileName(string.Empty) ||
!SaveFile())
{
return;
}
break;
case ContentDialogResult.Secondary:
// Don't save and continue the new action!
break;
default:
// Don't open the new action!
return;
}
}
// mute the TextChanged handler to make for clean UI
MonacoEditor.TextChanged -= MonacoEditor_TextChanged;
// reset editor, file info and ui.
_appFileName = string.Empty;
ResetEditorAndFile();
// disable buttons that do not make sense
UpdateUnsavedFileState(false);
refreshButton.IsEnabled = false;
// restore the TextChanged handler
ButtonAction_RestoreTextChangedEvent();
}
/// <summary>
/// Uses a picker to select a new file to open
/// </summary>
@@ -107,11 +172,15 @@ namespace RegistryPreviewUILib
{
case ContentDialogResult.Primary:
// Save, then continue the file open
SaveFile();
if (!AskFileName(string.Empty) ||
!SaveFile())
{
return;
}
break;
case ContentDialogResult.Secondary:
// Don't save and continue the file open!
saveButton.IsEnabled = false;
break;
default:
// Don't open the new file!
@@ -138,14 +207,16 @@ namespace RegistryPreviewUILib
{
// mute the TextChanged handler to make for clean UI
MonacoEditor.TextChanged -= MonacoEditor_TextChanged;
// update file name
_appFileName = storageFile.Path;
UpdateToolBarAndUI(await OpenRegistryFile(_appFileName));
// disable the Save button as it's a new file
saveButton.IsEnabled = false;
UpdateUnsavedFileState(false);
// Restore the event handler as we're loaded
MonacoEditor.TextChanged += MonacoEditor_TextChanged;
ButtonAction_RestoreTextChangedEvent();
}
}
@@ -154,7 +225,14 @@ namespace RegistryPreviewUILib
/// </summary>
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
SaveFile();
if (!AskFileName(string.Empty))
{
return;
}
// save and update window title
// error handling and ui update happens in SaveFile() method
_ = SaveFile();
}
/// <summary>
@@ -162,47 +240,24 @@ namespace RegistryPreviewUILib
/// </summary>
private async void SaveAsButton_Click(object sender, RoutedEventArgs e)
{
// 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
IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(_mainWindow);
string filename = SaveFilePicker.ShowDialog(
windowHandle,
resourceLoader.GetString("SuggestFileName"),
resourceLoader.GetString("FilterRegistryName") + '\0' + "*.reg" + '\0' + resourceLoader.GetString("FilterAllFiles") + '\0' + "*.*" + '\0' + '\0',
resourceLoader.GetString("SaveDialogTitle"));
// mute the TextChanged handler to make for clean UI
MonacoEditor.TextChanged -= MonacoEditor_TextChanged;
if (filename == string.Empty)
if (!AskFileName(_appFileName) || !SaveFile())
{
return;
}
_appFileName = filename;
SaveFile();
UpdateToolBarAndUI(await OpenRegistryFile(_appFileName));
// restore the TextChanged handler
ButtonAction_RestoreTextChangedEvent();
}
/// <summary>
/// Reloads the current REG file from storage
/// </summary>
private async void RefreshButton_Click(object sender, RoutedEventArgs e)
{
// mute the TextChanged handler to make for clean UI
MonacoEditor.TextChanged -= MonacoEditor_TextChanged;
// reload the current Registry file and update the toolbar accordingly.
UpdateToolBarAndUI(await OpenRegistryFile(_appFileName), true, true);
// disable the Save button as it's a new file
saveButton.IsEnabled = false;
// restore the TextChanged handler
MonacoEditor.TextChanged += MonacoEditor_TextChanged;
}
/// <summary>
/// Resets the editor content
/// </summary>
private async void NewButton_Click(object sender, RoutedEventArgs e)
{
// Check to see if the current file has been saved
if (saveButton.IsEnabled)
@@ -210,10 +265,9 @@ namespace RegistryPreviewUILib
ContentDialog contentDialog = new ContentDialog()
{
Title = resourceLoader.GetString("YesNoCancelDialogTitle"),
Content = resourceLoader.GetString("YesNoCancelDialogContent"),
PrimaryButtonText = resourceLoader.GetString("YesNoCancelDialogPrimaryButtonText"),
SecondaryButtonText = resourceLoader.GetString("YesNoCancelDialogSecondaryButtonText"),
CloseButtonText = resourceLoader.GetString("YesNoCancelDialogCloseButtonText"),
Content = resourceLoader.GetString("ReloadDialogContent"),
PrimaryButtonText = resourceLoader.GetString("ReloadDialogPrimaryButtonText"),
CloseButtonText = resourceLoader.GetString("ReloadDialogCloseButtonText"),
DefaultButton = ContentDialogButton.Primary,
};
@@ -228,15 +282,10 @@ namespace RegistryPreviewUILib
switch (contentDialogResult)
{
case ContentDialogResult.Primary:
// Save, then continue the file open
SaveFile();
break;
case ContentDialogResult.Secondary:
// Don't save and continue the file open!
saveButton.IsEnabled = false;
// Don't save and continue the reload action!
break;
default:
// Don't open the new file!
// Don't continue the reload action!
return;
}
}
@@ -244,16 +293,14 @@ namespace RegistryPreviewUILib
// mute the TextChanged handler to make for clean UI
MonacoEditor.TextChanged -= MonacoEditor_TextChanged;
// reset editor, file info and ui.
_appFileName = string.Empty;
ResetEditorAndFile();
// reload the current Registry file and update the toolbar accordingly.
UpdateToolBarAndUI(await OpenRegistryFile(_appFileName), true, true);
// disable the Save button as it's a new file
UpdateUnsavedFileState(false);
// restore the TextChanged handler
MonacoEditor.TextChanged += MonacoEditor_TextChanged;
// disable buttons that do not make sense
saveButton.IsEnabled = false;
refreshButton.IsEnabled = false;
ButtonAction_RestoreTextChangedEvent();
}
/// <summary>
@@ -314,15 +361,20 @@ namespace RegistryPreviewUILib
switch (contentDialogResult)
{
case ContentDialogResult.Primary:
// Save, then continue the file open
SaveFile();
// Save, then continue the merge action
if (!AskFileName(string.Empty) ||
!SaveFile())
{
return;
}
break;
case ContentDialogResult.Secondary:
// Don't save and continue the file open!
saveButton.IsEnabled = false;
// Don't save and continue the merge action!
UpdateUnsavedFileState(false);
break;
default:
// Don't open the new file!
// Don't merge the file!
return;
}
}
@@ -412,8 +464,27 @@ namespace RegistryPreviewUILib
_dispatcherQueue.TryEnqueue(() =>
{
RefreshRegistryFile();
saveButton.IsEnabled = true;
if (!editorContentChangedScripted)
{
UpdateUnsavedFileState(true);
}
editorContentChangedScripted = false;
});
}
/// <summary>
/// Sets indicator for programatic text change and adds text changed handler
/// </summary>
/// <remarks>
/// Use this always, if button actions temporary disable the text changed event
/// </remarks>
private void ButtonAction_RestoreTextChangedEvent()
{
// Solves the problem that enabling the event handler fires it one time.
// These one time fired event would causes wrong unsaved changes state.
editorContentChangedScripted = true;
MonacoEditor.TextChanged += MonacoEditor_TextChanged;
}
}
}
}

View File

@@ -11,6 +11,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.UI.Input;
@@ -22,6 +23,9 @@ namespace RegistryPreviewUILib
{
public sealed partial class RegistryPreviewMainPage : Page
{
private static readonly string _unsavedFileIndicator = "* ";
private static readonly char[] _unsavedFileIndicatorChars = [' ', '*'];
private const string NEWFILEHEADER = "Windows Registry Editor Version 5.00\r\n\r\n";
private static SemaphoreSlim _dialogSemaphore = new(1);
@@ -831,42 +835,66 @@ namespace RegistryPreviewUILib
/// </summary>
private async void HandleDirtyClosing(string title, string content, string primaryButtonText, string secondaryButtonText, string closeButtonText)
{
ContentDialog contentDialog = new ContentDialog()
if (_dialogSemaphore.CurrentCount == 0)
{
Title = title,
Content = content,
PrimaryButtonText = primaryButtonText,
SecondaryButtonText = secondaryButtonText,
CloseButtonText = closeButtonText,
DefaultButton = ContentDialogButton.Primary,
};
// Use this code to associate the dialog to the appropriate AppWindow by setting
// the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
contentDialog.XamlRoot = this.Content.XamlRoot;
return;
}
ContentDialogResult contentDialogResult = await contentDialog.ShowAsync();
switch (contentDialogResult)
try
{
case ContentDialogResult.Primary:
// Save, then close
SaveFile();
break;
case ContentDialogResult.Secondary:
// Don't save, and then close!
saveButton.IsEnabled = false;
break;
default:
// Cancel closing!
return;
}
await _dialogSemaphore.WaitAsync();
// if we got here, we should try to close again
Application.Current.Exit();
ContentDialog contentDialog = new ContentDialog()
{
Title = title,
Content = content,
PrimaryButtonText = primaryButtonText,
SecondaryButtonText = secondaryButtonText,
CloseButtonText = closeButtonText,
DefaultButton = ContentDialogButton.Primary,
};
// Use this code to associate the dialog to the appropriate AppWindow by setting
// the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
contentDialog.XamlRoot = this.Content.XamlRoot;
}
ContentDialogResult contentDialogResult = await contentDialog.ShowAsync();
switch (contentDialogResult)
{
case ContentDialogResult.Primary:
// Save, then close
if (!AskFileName(string.Empty) ||
!SaveFile())
{
return;
}
break;
case ContentDialogResult.Secondary:
// Don't save, and then close!
UpdateUnsavedFileState(false);
break;
default:
// Cancel closing!
return;
}
// if we got here, we should try to close again
Application.Current.Exit();
}
catch
{
// Normally nothing to catch here.
// But for safety the try-catch ensures that we always release the content dialog lock and exit correctly.
}
finally
{
_dialogSemaphore.Release();
}
}
/// <summary>
@@ -926,11 +954,71 @@ namespace RegistryPreviewUILib
type.InvokeMember("ProtectedCursor", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, null, uiElement, new object[] { cursor }, CultureInfo.InvariantCulture);
}
public void UpdateUnsavedFileState(bool unsavedChanges)
{
// get, cut and analyze the current title
string currentTitle = Regex.Replace(_mainWindow.Title, APPNAME + @"$|\s-\s" + APPNAME + @"$", string.Empty);
bool titleContainsIndicator = currentTitle.StartsWith(_unsavedFileIndicator, StringComparison.CurrentCultureIgnoreCase);
// update window title and save button state
if (unsavedChanges)
{
saveButton.IsEnabled = true;
if (!titleContainsIndicator)
{
_updateWindowTitleFunction(_unsavedFileIndicator + currentTitle);
}
}
else
{
saveButton.IsEnabled = false;
if (titleContainsIndicator)
{
_updateWindowTitleFunction(currentTitle.TrimStart(_unsavedFileIndicatorChars));
}
}
}
/// <summary>
/// Ask the user for the file path if it is unknown because of an unsaved file
/// </summary>
/// <param name="fileName">If not empty always ask for a file path and use the value as name.</param>
/// <returns>Returns true if user selected a path, otherwise false</returns>
public bool AskFileName(string fileName)
{
if (string.IsNullOrEmpty(_appFileName) || !string.IsNullOrEmpty(fileName) )
{
string fName = string.IsNullOrEmpty(fileName) ? resourceLoader.GetString("SuggestFileName") : fileName;
// 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
IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(_mainWindow);
string filename = SaveFilePicker.ShowDialog(
windowHandle,
fName,
resourceLoader.GetString("FilterRegistryName") + '\0' + "*.reg" + '\0' + resourceLoader.GetString("FilterAllFiles") + '\0' + "*.*" + '\0' + '\0',
resourceLoader.GetString("SaveDialogTitle"));
if (filename == string.Empty)
{
return false;
}
_appFileName = filename;
}
return true;
}
/// <summary>
/// Wrapper method that saves the current file in place, using the current text in editor.
/// </summary>
private void SaveFile()
private bool SaveFile()
{
bool saveSuccess = true;
ChangeCursor(gridPreview, true);
// set up the FileStream for all writing
@@ -954,10 +1042,13 @@ namespace RegistryPreviewUILib
streamWriter.Close();
// only change when the save is successful
saveButton.IsEnabled = false;
UpdateUnsavedFileState(false);
_updateWindowTitleFunction(_appFileName);
}
catch (UnauthorizedAccessException ex)
{
saveSuccess = false;
// this exception is thrown if the file is there but marked as read only
ShowMessageBox(
resourceLoader.GetString("ErrorDialogTitle"),
@@ -966,6 +1057,8 @@ namespace RegistryPreviewUILib
}
catch
{
saveSuccess = false;
// this catch handles all other exceptions thrown when trying to write the file out
ShowMessageBox(
resourceLoader.GetString("ErrorDialogTitle"),
@@ -983,6 +1076,8 @@ namespace RegistryPreviewUILib
// restore the cursor
ChangeCursor(gridPreview, false);
return saveSuccess;
}
/// <summary>
@@ -1098,4 +1193,4 @@ namespace RegistryPreviewUILib
return false;
}
}
}
}

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@@ -258,12 +258,18 @@
<data name="YesNoCancelDialogCloseButtonText" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="ReloadDialogCloseButtonText" xml:space="preserve">
<value>No</value>
</data>
<data name="YesNoCancelDialogContent" xml:space="preserve">
<value>Save changes?</value>
</data>
<data name="YesNoCancelDialogPrimaryButtonText" xml:space="preserve">
<value>Save</value>
</data>
<data name="ReloadDialogPrimaryButtonText" xml:space="preserve">
<value>Yes</value>
</data>
<data name="YesNoCancelDialogSecondaryButtonText" xml:space="preserve">
<value>Don't save</value>
</data>
@@ -300,4 +306,7 @@
<data name="NewButton.Label" xml:space="preserve">
<value>New</value>
</data>
<data name="ReloadDialogContent" xml:space="preserve">
<value>You lose any unsaved changes. Reload anyway?</value>
</data>
</root>