diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt
index 7b3543e06f56..0ebacd1b41bb 100644
--- a/.github/actions/spell-check/allow/names.txt
+++ b/.github/actions/spell-check/allow/names.txt
@@ -116,6 +116,7 @@ talynone
TBM
tilovell
Triet
+waaverecords
ycv
Yuniardi
yuyoyuppe
@@ -140,6 +141,7 @@ onenote
Quickime
regedit
roslyn
+Spotify
Vanara
WEX
windowwalker
diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
index c4915c60f8cc..1ff3718721ca 100644
--- a/.github/actions/spell-check/expect.txt
+++ b/.github/actions/spell-check/expect.txt
@@ -8,6 +8,7 @@ accctrl
Acceleratorkeys
ACCEPTFILES
ACCESSDENIED
+ACCESSTOKEN
aclapi
AClient
AColumn
@@ -34,6 +35,7 @@ ALPHATYPE
AModifier
AMPROPERTY
AMPROPSETID
+animatedvisuals
ANDSCANS
ansicolor
ANull
@@ -160,7 +162,6 @@ CHANGECBCHAIN
changecursor
CHILDACTIVATE
CHILDWINDOW
-CHT
cidl
cim
CImage
@@ -181,6 +182,7 @@ clrcall
CLSCTX
Clusion
cmder
+CMDNOTFOUNDMODULEINTERFACE
Cmds
CMIC
CMINVOKECOMMANDINFO
@@ -218,7 +220,6 @@ CONFIGW
CONFLICTINGMODIFIERKEY
CONFLICTINGMODIFIERSHORTCUT
CONOUT
-constexpr
consts
contentdialog
contentfiles
@@ -318,7 +319,6 @@ DESKTOPABSOLUTEEDITING
DESKTOPABSOLUTEPARSING
desktopshorcutinstalled
desktopwindowxamlsource
-DEU
devblogs
devdocs
devenum
@@ -507,6 +507,7 @@ GETCLIENTAREAANIMATION
GETDESKWALLPAPER
GETDLGCODE
GETDPISCALEDSIZE
+getfilesiginforedist
GETICON
GETMINMAXINFO
GETPROPERTYSTOREFLAGS
@@ -537,7 +538,6 @@ Hanzi
Hardlines
hardlinks
HARDWAREINPUT
-hashcode
Hashset
hashtag
HASHVAL
@@ -556,7 +556,6 @@ hcwhite
hdc
hdrop
hdwwiz
-HEB
Helpline
helptext
HGFE
@@ -719,7 +718,6 @@ jif
jjw
jobject
jpe
-JPN
jpnime
Jsons
jsonval
@@ -745,12 +743,11 @@ killrunner
Knownfolders
KSPROPERTY
Kybd
-LAlt
-Lambson
languagesjson
-langword
+lastbuildstate
lastcodeanalysissucceeded
Lastdevice
+LASTEXITCODE
LAYOUTRTL
LCIDTo
lcl
@@ -906,6 +903,8 @@ MOUSEHWHEEL
MOUSEINPUT
MOVESIZEEND
MOVESIZESTART
+MOZILLAPL
+MOZPL
mpmc
MRM
MRT
@@ -921,6 +920,7 @@ MSIFASTINSTALL
MSIHANDLE
msiquery
MSIRESTARTMANAGERCONTROL
+msixbundle
MSIXCA
MSLLHOOKSTRUCT
Mso
@@ -971,6 +971,7 @@ newdev
newitem
newpath
newrow
+newsgroups
NIF
NLD
NLog
@@ -983,7 +984,6 @@ NOCLOSEPROCESS
NOCOALESCE
NOCOPYBITS
nodeca
-nodiscard
nodoc
NODRAWCAPTION
NODRAWICON
@@ -1092,7 +1092,6 @@ pcch
pcelt
pch
PCIDLIST
-pcs
PCWSTR
pdisp
pdo
@@ -1276,6 +1275,7 @@ reparented
reparenting
reparse
reportbug
+reportfileaccesses
requery
requerying
rescap
@@ -1303,7 +1303,6 @@ RKey
RNumber
roadmap
rop
-roundf
ROUNDSMALL
rpcrt
RRF
@@ -1319,12 +1318,11 @@ rundll
rungameid
RUNLEVEL
runsettings
+runspace
runtimeclass
runtimeobject
runtimepack
runtimes
-RUS
-RValue
rvm
rwin
rwl
@@ -1505,7 +1503,6 @@ subquery
Superbar
sut
svchost
-SVE
SVGIn
SVGIO
svgz
@@ -1571,6 +1568,8 @@ timediff
timeunion
timeutil
Titlecase
+tkcontrols
+tkconverters
TKey
TLayout
tlb
@@ -1578,7 +1577,6 @@ tlbimp
TMPVAR
TNP
toggleswitch
-tonos
toolkitcontrols
toolkitconverters
Toolset
@@ -1609,6 +1607,7 @@ UAL
uap
udit
uefi
+uesc
UHash
UIA
UIEx
@@ -1617,6 +1616,7 @@ ums
uncompilable
UNCPRIORITY
UNDNAME
+unescape
UNICODETEXT
uninstantiated
uniquifier
@@ -1635,6 +1635,7 @@ UOI
Updatelayout
UPGRADINGPRODUCTCODE
Uptool
+urld
Usb
USEDEFAULT
USEFILEATTRIBUTES
@@ -1714,7 +1715,6 @@ wcsnicmp
WDA
wdp
wdupenv
-weakme
webbrowsers
webcam
webpage
@@ -1723,7 +1723,6 @@ wekyb
Wevtapi
wgpocpl
WIC
-wifi
wil
winapi
winappdriver
@@ -1851,9 +1850,6 @@ zonable
zoneset
Zoneszonabletester
zzz
-newsgroups
-MOZILLAPL
-MOZPL
# FALSE POSITIVES
diff --git a/.github/actions/spell-check/patterns.txt b/.github/actions/spell-check/patterns.txt
index daf0220efe64..d48cec9abd85 100644
--- a/.github/actions/spell-check/patterns.txt
+++ b/.github/actions/spell-check/patterns.txt
@@ -118,6 +118,9 @@ aka\.ms/[a-zA-Z0-9]+
# YouTube url
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
+# power shell gallery website
+\bpowershellgallery.com/[-_a-zA-Z0-9()=./%]*
+
# uuid: (or CompGUIDPrefix)
L?(["']|[-<({>]|\b)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{10,12}(?:\g{-1}|[<})>])
diff --git a/.gitignore b/.gitignore
index 14c1e397376b..f8390b9d56cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -348,3 +348,6 @@ src/common/Telemetry/*.etl
# Generated installer file for Monaco source files.
/installer/PowerToysSetup/MonacoSRC.wxs
+
+# MSBuildCache
+/MSBuildCacheLogs/
diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json
index 73bc45ed035c..a6350ec76434 100644
--- a/.pipelines/ESRPSigning_core.json
+++ b/.pipelines/ESRPSigning_core.json
@@ -28,7 +28,10 @@
"PowerToys.AlwaysOnTop.exe",
"PowerToys.AlwaysOnTopModuleInterface.dll",
-
+
+ "PowerToys.CmdNotFoundModuleInterface.dll",
+ "PowerToys.CmdNotFound.dll",
+
"PowerToys.ColorPicker.dll",
"PowerToys.ColorPickerUI.dll",
"PowerToys.ColorPickerUI.exe",
@@ -251,6 +254,7 @@
"Mages.Core.dll",
"JetBrains.Annotations.dll",
"NLog.Extensions.Logging.dll",
+ "getfilesiginforedist.dll",
"concrt140_app.dll",
"msvcp140_1_app.dll",
"msvcp140_2_app.dll",
diff --git a/.pipelines/ci/caching.yml b/.pipelines/ci/caching.yml
new file mode 100644
index 000000000000..b802c9efae46
--- /dev/null
+++ b/.pipelines/ci/caching.yml
@@ -0,0 +1,41 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/azure-pipelines-vscode/main/service-schema.json
+trigger:
+ batch: true
+ branches:
+ include:
+ - main
+ - stable
+ paths:
+ exclude:
+ - doc/*
+ - temp/*
+ - tools/*
+ - '**.md'
+
+pr:
+ branches:
+ include:
+ - main
+ - stable
+ paths:
+ exclude:
+ - '**.md'
+ - doc
+
+# 0.0.yyMM.dd##
+# 0.0.1904.0900
+name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr)
+
+variables:
+ EnablePipelineCache: true
+
+jobs:
+ - template: ./templates/build-powertoys-precheck.yml
+ - template: ./templates/build-powertoys-ci.yml
+ parameters:
+ platform: x64
+ enableCaching: true
+ - template: ./templates/build-powertoys-ci.yml
+ parameters:
+ platform: arm64
+ enableCaching: true
\ No newline at end of file
diff --git a/.pipelines/ci/templates/build-powertoys-ci.yml b/.pipelines/ci/templates/build-powertoys-ci.yml
index c5f4882cd832..2d39da65b991 100644
--- a/.pipelines/ci/templates/build-powertoys-ci.yml
+++ b/.pipelines/ci/templates/build-powertoys-ci.yml
@@ -1,7 +1,16 @@
parameters:
- configuration: 'Release'
- platform: ''
- additionalBuildArguments: '/p:RestorePackagesConfig=true -m'
+ - name: configuration
+ type: string
+ default: 'Release'
+ - name: platform
+ type: string
+ default: '/p:RestorePackagesConfig=true -m'
+ - name: additionalBuildArguments
+ type: string
+ default: '/p:RestorePackagesConfig=true -m'
+ - name: enableCaching
+ type: boolean
+ default: false
jobs:
- job: Build${{ parameters.platform }}${{ parameters.configuration }}
@@ -26,6 +35,7 @@ jobs:
- template: build-powertoys-steps.yml
parameters:
additionalBuildArguments: ${{ parameters.additionalBuildArguments }}
+ enableCaching: ${{ parameters.enableCaching }}
# It appears that the Component Governance build task that gets automatically injected stopped working
# when we renamed our main branch.
diff --git a/.pipelines/ci/templates/build-powertoys-steps.yml b/.pipelines/ci/templates/build-powertoys-steps.yml
index d151a3925756..815292711191 100644
--- a/.pipelines/ci/templates/build-powertoys-steps.yml
+++ b/.pipelines/ci/templates/build-powertoys-steps.yml
@@ -1,5 +1,10 @@
parameters:
- additionalBuildArguments: ''
+ - name: additionalBuildArguments
+ type: string
+ default: ''
+ - name: enableCaching
+ type: boolean
+ default: false
steps:
- checkout: self
@@ -78,6 +83,13 @@ steps:
- task: VisualStudioTestPlatformInstaller@1
displayName: Ensure VSTest Platform
+- ${{ if eq(parameters.enableCaching, true) }}:
+ - task: NuGetToolInstaller@1
+ displayName: Install NuGet
+
+ - script: nuget restore packages.config -SolutionDirectory .
+ displayName: 'nuget restore packages.config'
+
- task: VSBuild@1
displayName: 'Build PowerToys.sln'
inputs:
@@ -85,8 +97,15 @@ steps:
vsVersion: 17.0
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
- msbuildArgs: -restore ${{ parameters.additionalBuildArguments }}
+ ${{ if eq(parameters.enableCaching, true) }}:
+ msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -graph -reportfileaccesses -p:MSBuildCacheEnabled=true -p:MSBuildCacheLogDirectory=$(Build.ArtifactStagingDirectory)\logs\MSBuildCache -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToys.binlog -ds:false
+ ${{ else }}:
+ msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToys.binlog -ds:false
+ msbuildArchitecture: x64
maximumCpuCount: true
+ ${{ if eq(parameters.enableCaching, true) }}:
+ env:
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: VSBuild@1
displayName: 'Build BugReportTool.sln'
@@ -95,7 +114,8 @@ steps:
vsVersion: 17.0
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
- msbuildArgs: -restore ${{ parameters.additionalBuildArguments }}
+ msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\BugReportTool.binlog -ds:false
+ msbuildArchitecture: x64
maximumCpuCount: true
- task: VSBuild@1
@@ -105,7 +125,8 @@ steps:
vsVersion: 17.0
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
- msbuildArgs: -restore ${{ parameters.additionalBuildArguments }}
+ msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\WebcamReportTool.binlog -ds:false
+ msbuildArchitecture: x64
maximumCpuCount: true
- task: VSBuild@1
@@ -115,7 +136,8 @@ steps:
vsVersion: 17.0
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
- msbuildArgs: -restore ${{ parameters.additionalBuildArguments }}
+ msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\StylesReportTool.binlog -ds:false
+ msbuildArchitecture: x64
maximumCpuCount: true
- task: PowerShell@2
@@ -131,7 +153,8 @@ steps:
vsVersion: 17.0
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
- msbuildArgs: /t:PowerToysInstaller -restore ${{ parameters.additionalBuildArguments }}
+ msbuildArgs: /t:PowerToysInstaller -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToysSetup-PowerToysInstaller.binlog -ds:false
+ msbuildArchitecture: x64
maximumCpuCount: true
- task: VSBuild@1
@@ -141,8 +164,9 @@ steps:
vsVersion: 17.0
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
- msbuildArgs: /t:PowerToysBootstrapper ${{ parameters.additionalBuildArguments }}
+ msbuildArgs: /t:PowerToysBootstrapper ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToysSetup-PowerToysBootstrapper.binlog -ds:false
clean: false
+ msbuildArchitecture: x64
maximumCpuCount: true
- task: PowerShell@2
@@ -159,7 +183,8 @@ steps:
vsVersion: 17.0
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
- msbuildArgs: /t:PowerToysInstaller -restore ${{ parameters.additionalBuildArguments }} /p:PerUser=true
+ msbuildArgs: /t:PowerToysInstaller -restore ${{ parameters.additionalBuildArguments }} /p:PerUser=true -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToysSetup-PowerToysInstaller-PerUser.binlog -ds:false
+ msbuildArchitecture: x64
maximumCpuCount: true
- task: VSBuild@1
@@ -169,8 +194,9 @@ steps:
vsVersion: 17.0
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
- msbuildArgs: /t:PowerToysBootstrapper ${{ parameters.additionalBuildArguments }} /p:PerUser=true
+ msbuildArgs: /t:PowerToysBootstrapper ${{ parameters.additionalBuildArguments }} /p:PerUser=true -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToysSetup-PowerToysBootstrapper-PerUser.binlog -ds:false
clean: false
+ msbuildArchitecture: x64
maximumCpuCount: true
# Check if deps.json files don't reference different dll versions.
@@ -268,3 +294,8 @@ steps:
filePath: '$(build.sourcesdirectory)\.pipelines\verifyNoticeMdAgainstNugetPackages.ps1'
arguments: -path '$(build.sourcesdirectory)\'
pwsh: true
+
+- publish: $(Build.ArtifactStagingDirectory)\logs
+ displayName: Publish Logs
+ artifact: '$(System.JobDisplayName) logs'
+ condition: always()
diff --git a/.pipelines/release.yml b/.pipelines/release.yml
index 1993c2b3ce61..d28596e0f62e 100644
--- a/.pipelines/release.yml
+++ b/.pipelines/release.yml
@@ -8,7 +8,7 @@ resources:
- repository: 1ESPipelineTemplates
type: git
name: 1ESPipelineTemplates/1ESPipelineTemplates
- ref: refs/tags/release-2023-11-13-4
+ ref: refs/tags/release
parameters:
- name: buildConfigurations
diff --git a/.pipelines/versionAndSignCheck.ps1 b/.pipelines/versionAndSignCheck.ps1
index 42b66ed8bc82..d5960efb642d 100644
--- a/.pipelines/versionAndSignCheck.ps1
+++ b/.pipelines/versionAndSignCheck.ps1
@@ -24,6 +24,7 @@ $versionExceptions = @(
$nullVersionExceptions = @(
"codicon.ttf",
"e_sqlite3.dll",
+ "getfilesiginforedist.dll",
"vcamp140_app.dll",
"vcruntime140_app.dll",
"vcruntime140_1_app.dll",
diff --git a/Directory.Build.props b/Directory.Build.props
index 9f8159a6d9da..14a45385d4d1 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,9 +1,9 @@
- Copyright (C) 2023 Microsoft Corporation
+ Copyright (C) 2024 Microsoft Corporation
Microsoft Corp.
- Copyright (C) 2023 Microsoft Corporation
+ Copyright (C) 2024 Microsoft Corporation
PowerToys
Microsoft Corporation
en-US
@@ -44,4 +44,46 @@
+
+
+
+ false
+
+
+ Microsoft.MSBuildCache.AzurePipelines
+ Microsoft.MSBuildCache.Local
+
+
+
+
+ 202310210737
+
+
+ $(MSBuildCacheAllowFileAccessAfterProjectFinishFilePatterns);packages\Microsoft.WindowsAppSDK.*\tools\net472\ApplicationInsights.config
+
+
+ $(MSBuildCacheIdenticalDuplicateOutputPatterns);**
+
+
+ $(MSBuildThisFileDirectory)packages.config
+ $(MSBuildCacheIgnoredInputPatterns);$(PackagesConfigFile)
+
+
+
+ $([System.IO.File]::ReadAllText("$(PackagesConfigFile)"))
+ $([System.Text.RegularExpressions.Regex]::Match($(PackagesConfigContents), 'Microsoft.MSBuildCache.*?version="(.*?)"').Groups[1].Value)
+ $(MSBuildThisFileDirectory)packages\$(MSBuildCachePackageName).$(MSBuildCachePackageVersion)
+ $(MSBuildThisFileDirectory)packages\Microsoft.MSBuildCache.SharedCompilation.$(MSBuildCachePackageVersion)
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Directory.Build.targets b/Directory.Build.targets
index 66a8733ce6fd..cba7762d5f48 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -1,3 +1,6 @@
+
+
+
\ No newline at end of file
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 93f16ce64a7e..4439822c4e12 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -4,15 +4,15 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -24,7 +24,7 @@
-
+
@@ -33,16 +33,18 @@
-
+
+
+
-
+
-
+
@@ -52,22 +54,27 @@
-
-
+
+
-
+
+
-
+
+
+
+
+
@@ -75,8 +82,8 @@
-
-
+
+
diff --git a/NOTICE.md b/NOTICE.md
index 59adbebaed5b..e0aeb9061b2c 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -1296,14 +1296,15 @@ EXHIBIT A -Mozilla Public License.
## NuGet Packages used by PowerToys
-- CommunityToolkit.Mvvm 8.2.0
-- CommunityToolkit.WinUI.Animations 8.0.230907
-- CommunityToolkit.WinUI.Controls.Primitives 8.0.230907
-- CommunityToolkit.WinUI.Controls.Segmented 8.0.230907
-- CommunityToolkit.WinUI.Controls.SettingsControls 8.0.230907
-- CommunityToolkit.WinUI.Controls.Sizers 8.0.230907
-- CommunityToolkit.WinUI.Converters 8.0.230907
-- CommunityToolkit.WinUI.Extensions 8.0.230907
+- CommunityToolkit.Mvvm 8.2.2
+- CommunityToolkit.WinUI.Animations 8.0.240109
+- CommunityToolkit.WinUI.Collections 8.0.240109
+- CommunityToolkit.WinUI.Controls.Primitives 8.0.240109
+- CommunityToolkit.WinUI.Controls.Segmented 8.0.240109
+- CommunityToolkit.WinUI.Controls.SettingsControls 8.0.240109
+- CommunityToolkit.WinUI.Controls.Sizers 8.0.240109
+- CommunityToolkit.WinUI.Converters 8.0.240109
+- CommunityToolkit.WinUI.Extensions 8.0.240109
- CommunityToolkit.WinUI.UI.Controls.DataGrid 7.1.2
- CommunityToolkit.WinUI.UI.Controls.Markdown 7.1.2
- ControlzEx 6.0.0
@@ -1313,7 +1314,7 @@ EXHIBIT A -Mozilla Public License.
- Interop.Microsoft.Office.Interop.OneNote 1.1.0.2
- LazyCache 2.4.0
- Mages 2.0.1
-- Markdig.Signed 0.27.0
+- Markdig.Signed 0.34.0
- Microsoft.CodeAnalysis.NetAnalyzers 8.0.0
- Microsoft.Data.Sqlite 8.0.0
- Microsoft.Extensions.DependencyInjection 8.0.0
@@ -1321,37 +1322,44 @@ EXHIBIT A -Mozilla Public License.
- Microsoft.Extensions.Hosting.WindowsServices 8.0.0
- Microsoft.Extensions.Logging 8.0.0
- Microsoft.Extensions.Logging.Abstractions 8.0.0
-- Microsoft.NET.Test.Sdk 17.6.3
+- Microsoft.Extensions.ObjectPool 8.0.0
+- Microsoft.NET.Test.Sdk 17.8.0
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
- Microsoft.Web.WebView2 1.0.2088.41
+- Microsoft.Win32.SystemEvents 8.0.0
- Microsoft.Windows.Compatibility 8.0.0
- Microsoft.Windows.CsWin32 0.2.46-beta
- Microsoft.Windows.CsWinRT 2.0.4
-- Microsoft.Windows.SDK.BuildTools 10.0.22621.756
+- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
- Microsoft.Windows.SDK.Contracts 10.0.19041.1
-- Microsoft.WindowsAppSDK 1.4.231115000
+- Microsoft.WindowsAppSDK 1.4.231219000
- Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9
- Microsoft.Xaml.Behaviors.Wpf 1.1.39
- ModernWpfUI 0.9.4
- Moq 4.18.4
- MSTest.TestAdapter 3.1.1
- MSTest.TestFramework 3.1.1
-- NLog.Extensions.Logging 5.0.4
-- NLog.Schema 5.0.4
+- NLog.Extensions.Logging 5.3.8
+- NLog.Schema 5.2.8
- ScipBe.Common.Office.OneNote 3.0.1
- SharpCompress 0.33.0
- StreamJsonRpc 2.14.24
-- StyleCop.Analyzers 1.2.0-beta.507
+- StyleCop.Analyzers 1.2.0-beta.556
+- System.CodeDom 8.0.0
- System.CommandLine 2.0.0-beta4.22272.1
- System.ComponentModel.Composition 8.0.0
- System.Configuration.ConfigurationManager 8.0.0
- System.Data.OleDb 8.0.0
-- System.Drawing.Common 8.0.0
+- System.Diagnostics.EventLog 8.0.0
+- System.Diagnostics.PerformanceCounter 8.0.0
+- System.Drawing.Common 8.0.1
- System.IO.Abstractions 17.2.3
- System.IO.Abstractions.TestingHelpers 17.2.3
- System.Management 8.0.0
+- System.Management.Automation 7.4.0
- System.Reactive 6.0.0-preview.9
- System.Runtime.Caching 8.0.0
+- System.Security.Cryptography.ProtectedData 8.0.0
- System.ServiceProcess.ServiceController 8.0.0
- System.Text.Encoding.CodePages 8.0.0
- UnicodeInformation 2.6.0
@@ -1360,5 +1368,4 @@ EXHIBIT A -Mozilla Public License.
- Vanara.PInvoke.Shell32 3.4.11
- Vanara.PInvoke.User32 3.4.11
- WinUIEx 2.2.0
-- WPF-UI 3.0.0-preview.12
-
+- WPF-UI 3.0.0-preview.13
diff --git a/PowerToys.sln b/PowerToys.sln
index 7ae29e869a6e..cd14624e27a5 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -538,6 +538,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CropAndLock", "src\modules\
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CropAndLockModuleInterface", "src\modules\CropAndLock\CropAndLockModuleInterface\CropAndLockModuleInterface.vcxproj", "{3157FA75-86CF-4EE2-8F62-C43F776493C6}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CmdNotFound", "src\modules\cmdNotFound\CmdNotFound\CmdNotFound.csproj", "{A37865FE-2881-449F-8ADB-B8CD373D6D79}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cmdNotFound", "cmdNotFound", "{4C0D0746-BE5B-49EE-BD5D-A7811628AE8B}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-FancyZonesEditor", "src\modules\fancyzones\UnitTests-FancyZonesEditor\UnitTests-FancyZonesEditor.csproj", "{FC8EB78F-F061-4BD9-A3F6-507BEA965E2B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EnvironmentVariables", "EnvironmentVariables", "{538ED0BB-B863-4B20-98CC-BCDF7FA0B68A}"
@@ -558,6 +562,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-QoiPreviewHandler
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-QoiThumbnailProvider", "src\modules\previewpane\UnitTests-QoiThumbnailProvider\UnitTests-QoiThumbnailProvider.csproj", "{F8FFFC12-A31A-4AFA-B3DF-14DCF42B5E38}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdNotFoundModuleInterface", "src\modules\cmdNotFound\CmdNotFoundModuleInterface\CmdNotFoundModuleInterface.vcxproj", "{0014D652-901F-4456-8D65-06FC5F997FB0}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileActionsMenu.Ui", "src\modules\previewpane\FileActionsMenu\FileActionsMenu.Ui\FileActionsMenu.Ui.csproj", "{2ED5F735-787A-4A86-9B0D-4A2DD06FAAF0}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileActionsMenu", "src\modules\previewpane\FileActionsMenu\FileActionsMenu\FileActionsMenu.vcxproj", "{3A2398AB-D8F5-49C5-9EA7-483BED398BEC}"
@@ -2334,6 +2340,18 @@ Global
{3157FA75-86CF-4EE2-8F62-C43F776493C6}.Release|x64.Build.0 = Release|x64
{3157FA75-86CF-4EE2-8F62-C43F776493C6}.Release|x86.ActiveCfg = Release|x64
{3157FA75-86CF-4EE2-8F62-C43F776493C6}.Release|x86.Build.0 = Release|x64
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Debug|ARM64.Build.0 = Debug|ARM64
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Debug|x64.ActiveCfg = Debug|x64
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Debug|x64.Build.0 = Debug|x64
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Debug|x86.ActiveCfg = Debug|x64
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Debug|x86.Build.0 = Debug|x64
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Release|ARM64.ActiveCfg = Release|ARM64
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Release|ARM64.Build.0 = Release|ARM64
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Release|x64.ActiveCfg = Release|x64
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Release|x64.Build.0 = Release|x64
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Release|x86.ActiveCfg = Release|x64
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Release|x86.Build.0 = Release|x64
{FC8EB78F-F061-4BD9-A3F6-507BEA965E2B}.Debug|ARM64.ActiveCfg = Debug|ARM64
{FC8EB78F-F061-4BD9-A3F6-507BEA965E2B}.Debug|ARM64.Build.0 = Debug|ARM64
{FC8EB78F-F061-4BD9-A3F6-507BEA965E2B}.Debug|x64.ActiveCfg = Debug|x64
@@ -2442,6 +2460,18 @@ Global
{F8FFFC12-A31A-4AFA-B3DF-14DCF42B5E38}.Release|x64.Build.0 = Release|x64
{F8FFFC12-A31A-4AFA-B3DF-14DCF42B5E38}.Release|x86.ActiveCfg = Release|x64
{F8FFFC12-A31A-4AFA-B3DF-14DCF42B5E38}.Release|x86.Build.0 = Release|x64
+ {0014D652-901F-4456-8D65-06FC5F997FB0}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {0014D652-901F-4456-8D65-06FC5F997FB0}.Debug|ARM64.Build.0 = Debug|ARM64
+ {0014D652-901F-4456-8D65-06FC5F997FB0}.Debug|x64.ActiveCfg = Debug|x64
+ {0014D652-901F-4456-8D65-06FC5F997FB0}.Debug|x64.Build.0 = Debug|x64
+ {0014D652-901F-4456-8D65-06FC5F997FB0}.Debug|x86.ActiveCfg = Debug|Win32
+ {0014D652-901F-4456-8D65-06FC5F997FB0}.Debug|x86.Build.0 = Debug|Win32
+ {0014D652-901F-4456-8D65-06FC5F997FB0}.Release|ARM64.ActiveCfg = Release|ARM64
+ {0014D652-901F-4456-8D65-06FC5F997FB0}.Release|ARM64.Build.0 = Release|ARM64
+ {0014D652-901F-4456-8D65-06FC5F997FB0}.Release|x64.ActiveCfg = Release|x64
+ {0014D652-901F-4456-8D65-06FC5F997FB0}.Release|x64.Build.0 = Release|x64
+ {0014D652-901F-4456-8D65-06FC5F997FB0}.Release|x86.ActiveCfg = Release|Win32
+ {0014D652-901F-4456-8D65-06FC5F997FB0}.Release|x86.Build.0 = Release|Win32
{2ED5F735-787A-4A86-9B0D-4A2DD06FAAF0}.Debug|ARM64.ActiveCfg = Debug|ARM64
{2ED5F735-787A-4A86-9B0D-4A2DD06FAAF0}.Debug|ARM64.Build.0 = Debug|ARM64
{2ED5F735-787A-4A86-9B0D-4A2DD06FAAF0}.Debug|x64.ActiveCfg = Debug|x64
@@ -2660,6 +2690,8 @@ Global
{3B227528-4BA6-4CAF-B44A-A10C78A64849} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{F5E1146E-B7B3-4E11-85FD-270A500BD78C} = {3B227528-4BA6-4CAF-B44A-A10C78A64849}
{3157FA75-86CF-4EE2-8F62-C43F776493C6} = {3B227528-4BA6-4CAF-B44A-A10C78A64849}
+ {A37865FE-2881-449F-8ADB-B8CD373D6D79} = {4C0D0746-BE5B-49EE-BD5D-A7811628AE8B}
+ {4C0D0746-BE5B-49EE-BD5D-A7811628AE8B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{FC8EB78F-F061-4BD9-A3F6-507BEA965E2B} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{538ED0BB-B863-4B20-98CC-BCDF7FA0B68A} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{51465DA1-C18B-4B99-93E1-ECF8E0FA0CBA} = {538ED0BB-B863-4B20-98CC-BCDF7FA0B68A}
@@ -2670,6 +2702,7 @@ Global
{6B04803D-B418-4833-A67E-B0FC966636A5} = {2F305555-C296-497E-AC20-5FA1B237996A}
{3940AD4D-F748-4BE4-9083-85769CD553EF} = {2F305555-C296-497E-AC20-5FA1B237996A}
{F8FFFC12-A31A-4AFA-B3DF-14DCF42B5E38} = {2F305555-C296-497E-AC20-5FA1B237996A}
+ {0014D652-901F-4456-8D65-06FC5F997FB0} = {4C0D0746-BE5B-49EE-BD5D-A7811628AE8B}
{2ED5F735-787A-4A86-9B0D-4A2DD06FAAF0} = {89A38AD7-DA2F-46B9-AA9B-C213AA172350}
{3A2398AB-D8F5-49C5-9EA7-483BED398BEC} = {89A38AD7-DA2F-46B9-AA9B-C213AA172350}
{89A38AD7-DA2F-46B9-AA9B-C213AA172350} = {2F305555-C296-497E-AC20-5FA1B237996A}
diff --git a/README.md b/README.md
index 575abe4c542a..4d8e8276d7b2 100644
--- a/README.md
+++ b/README.md
@@ -17,14 +17,14 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
| | Current utilities: | |
|--------------|--------------------|--------------|
-| [Always on Top](https://aka.ms/PowerToysOverview_AoT) | [PowerToys Awake](https://aka.ms/PowerToysOverview_Awake) | [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) |
-| [Crop And Lock](https://aka.ms/PowerToysOverview_CropAndLock) | [Environment Variables](https://aka.ms/PowerToysOverview_EnvironmentVariables) | [FancyZones](https://aka.ms/PowerToysOverview_FancyZones) |
-| [File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) | [File Locksmith](https://aka.ms/PowerToysOverview_FileLocksmith) | [Hosts File Editor](https://aka.ms/PowerToysOverview_HostsFileEditor) |
-| [Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | [Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) | [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) |
-| [Mouse Without Borders](https://aka.ms/PowerToysOverview_MouseWithoutBorders) | [Peek](https://aka.ms/PowerToysOverview_Peek) | [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) |
-| [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) |
-| [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) |
-| [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) |
+| [Always on Top](https://aka.ms/PowerToysOverview_AoT) | [PowerToys Awake](https://aka.ms/PowerToysOverview_Awake) | [Command Not Found](https://aka.ms/PowerToysOverview_CmdNotFound) |
+| [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | [Crop And Lock](https://aka.ms/PowerToysOverview_CropAndLock) | [Environment Variables](https://aka.ms/PowerToysOverview_EnvironmentVariables) |
+| [FancyZones](https://aka.ms/PowerToysOverview_FancyZones) | [File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) | [File Locksmith](https://aka.ms/PowerToysOverview_FileLocksmith) |
+| [Hosts File Editor](https://aka.ms/PowerToysOverview_HostsFileEditor) | [Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | [Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) |
+| [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | [Mouse Without Borders](https://aka.ms/PowerToysOverview_MouseWithoutBorders) | [Peek](https://aka.ms/PowerToysOverview_Peek) |
+| [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) | [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) |
+| [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) |
+| [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) |
## Installing and running Microsoft PowerToys
@@ -40,19 +40,19 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and 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.
-[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F50
-[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F49
-[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.76.2/PowerToysUserSetup-0.76.2-x64.exe
-[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.76.2/PowerToysUserSetup-0.76.2-arm64.exe
-[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.76.2/PowerToysSetup-0.76.2-x64.exe
-[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.76.2/PowerToysSetup-0.76.2-arm64.exe
+[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F51
+[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F50
+[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.77.0/PowerToysUserSetup-0.77.0-x64.exe
+[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.77.0/PowerToysUserSetup-0.77.0-arm64.exe
+[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.77.0/PowerToysSetup-0.77.0-x64.exe
+[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.77.0/PowerToysSetup-0.77.0-arm64.exe
| Description | Filename | sha256 hash |
|----------------|----------|-------------|
-| Per user - x64 | [PowerToysUserSetup-0.76.2-x64.exe][ptUserX64] | 73D734FC34B3F9D7484081EC0F0B6ACD4789A55203A185904CC5C62ABD02AF16 |
-| Per user - ARM64 | [PowerToysUserSetup-0.76.2-arm64.exe][ptUserArm64] | 5284CC5DA399DC37858A2FD260C30F20C484BA1B5616D0EB67CD90A8A286CB8B |
-| Machine wide - x64 | [PowerToysSetup-0.76.2-x64.exe][ptMachineX64] | 72B87381C9E5C7447FB59D7CE478B3102C9CEE3C6EB3A6BC7EC7EC7D9EFAB2A0 |
-| Machine wide - ARM64 | [PowerToysSetup-0.76.2-arm64.exe][ptMachineArm64] | F28C7DA377C25309775AB052B2B19A299C26C41582D05B95C3492A4A8C952BFE |
+| Per user - x64 | [PowerToysUserSetup-0.77.0-x64.exe][ptUserX64] | 3485D3F45A3DE6ED7FA151A4CE9D6F941491C30E83AB51FD59B4ADCD20611F1A |
+| Per user - ARM64 | [PowerToysUserSetup-0.77.0-arm64.exe][ptUserArm64] | 762DF383A01006A20C0BAB2D321667E855236EBA7108CDD475E4E2A8AB752E0E |
+| Machine wide - x64 | [PowerToysSetup-0.77.0-x64.exe][ptMachineX64] | 1B6D4247313C289B07A3BF3531E215B3F9BEDBE9254919637F2AC502B4773C31 |
+| Machine wide - ARM64 | [PowerToysSetup-0.77.0-arm64.exe][ptMachineArm64] | CF740B3AC0EB5C23E18B07ACC2D0C6EC5F4CE4B3A2EDC67C2C9FDF6EF78F0352 |
This is our preferred method.
@@ -98,154 +98,112 @@ For guidance on developing for PowerToys, please read the [developer docs](/doc/
Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on.
-### 0.76 - November 2023 Update
+### 0.77 - December 2023 Update
In this release, we focused on new features, stability and improvements.
**Highlights**
- - Upgrade to .NET 8. Thanks [@snickler](https://github.com/snickler)!
- - Keyboard Manager can now remap keys and shortcuts to send sequences of unicode text.
- - Modernized the Keyboard Manager Editor UI. Thanks [@dillydylann](https://github.com/dillydylann)!
- - Modernized the PowerToys Run, Quick Accent and Text Extractor UIs. Thanks [@niels9001](https://github.com/niels9001)!
- - New File Explorer Add-ons: QOI image Preview Handler and Thumbnail Provider. Thanks [@pedrolamas](https://github.com/pedrolamas)!
+ - New utility: Command Not Found PowerShell 7.4 module - adds the ability to detect failed commands in PowerShell 7.4 and suggest a package to install using winget. Thanks [@carlos-zamora](https://github.com/carlos-zamora)!
+ - Keyboard manager does not register low level hook if there are no remappings anymore.
+ - Added support for QOI file type in Peek. Thanks [@pedrolamas](https://github.com/pedrolamas)!
+ - Added support for loading 3rd-party plugins with additional dependencies in PowerToys Run. Thanks [@coreyH](https://github.com/CoreyHayward)!
### General
- - Updated the WebView 2 dependency to 1.0.2088.41. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- - Fixed unreadable color brushes used across WinUI3 applications for improved accessibility. Thanks [@niels9001](https://github.com/niels9001)!
- - Flyouts used across WinUI3 applications are no longer constrained to the application's bounds. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)!
- - Upgraded the WPF-UI dependency to preview.9 and then preview.11. Thanks [@niels9001](https://github.com/niels9001) and [@pomianowski](https://github.com/pomianowski)!
- - Upgraded to .NET 8. Thanks [@snickler](https://github.com/snickler)!
- - Updated the WinAppSDK dependency to 1.4.3.
-### Awake
+ - Bump WPF-UI package version to fix crashes related to theme changes. (This was a hotfix for 0.76)
+ - Fixed typo in version change notification. Thanks [@PesBandi](https://github.com/PesBandi)!
+ - Code improvements and fixed silenced warnings introduced by upgrade to .NET 8.
+ - Update copyright year for 2024.
+ - Added setting to disable warning notifications about detecting an application running as Administrator.
- - Added localization to the tray icon context menu.
+### AlwaysOnTop
-### Crop And Lock
+ - Show notification when elevated app is in the foreground but AlwaysOnTop is running non-elevated.
- - Fixed restoring windows that were reparented while maximized.
+### Command Not Found
+
+ - Added a new utility: A Command Not Found PowerShell 7.4 module. It adds the ability to detect failed commands in PowerShell 7.4 and suggest a package to install using winget. Thanks [@carlos-zamora](https://github.com/carlos-zamora)!
### Environment Variables
- - Fixed crash caused by WinAppSDK version bump by replacing ListView elements with ItemsControl.
+ - Fixed issue causing Environment Variables window not to appear as a foreground window.
### FancyZones
- - Reverted a change that caused some applications, like the Windows Calculator, to not snap correctly. (This was a hotfix for 0.75)
- - FancyZones Editor will no longer apply a layout to the current monitor after editing it.
- - Fixed and refactored the code that detected if a window can be snapped. Added tests to it with known application window styles to avoid regressions in the future.
+ - Fixed snapping specific apps (e.g. Facebook messenger). (This was a hotfix for 0.76)
+ - Fixed behavior of Move newly created windows to current active monitor setting to keep maximize state on moving. Thanks [@quyenvsp](https://github.com/quyenvsp)!
+ - Fixed issue causing FancyZones Editor layout window to be zoned.
### File Explorer add-ons
- - Solved an issue incorrectly detecting encoding when previewing code files preview.
- - Fixed the background color for Gcode preview handler on dark theme. Thanks [@pedrolamas](https://github.com/pedrolamas)!
- - New utilities: Preview Handler and Thumbnail Provider for QOI image files. Thanks [@pedrolamas](https://github.com/pedrolamas)!
- - GCode Thumbnails are now in the 32 bit ARGB format. Thanks [@pedrolamas](https://github.com/pedrolamas)!
- - Added the perceived type to SVG and QOI file thumbnails. Thanks [@pedrolamas](https://github.com/pedrolamas)!
-
-### GPO
-
- - Added the missing Environment Variables utility policy to the .admx and .adml files. (This was a hotfix for 0.75)
- - Fixed some typos and text improvements in the .adml file. Thanks [@htcfreek](https://github.com/htcfreek)!
+ - Fixed WebView2 based previewers issue caused by the latest WebView update. (This was a hotfix for 0.76)
### Hosts File Editor
- - Added a proper warning when the hosts file is read-only and a button to make it writable. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
+ - Fixed issue causing settings not to be preserved on update.
### Image Resizer
-
- - Fixed a WPF-UI issue regarding the application's background brushes. Thanks [@niels9001](https://github.com/niels9001)!
-
-### Installer
- - Included the Text Extractor and Awake localization files in the install process.
+ - Fixed crash caused by WpfUI ThemeWatcher. (This was a hotfix for 0.76)
### Keyboard Manager
- - Modernized the UI with the Fluent design. Thanks [@dillydylann](https://github.com/dillydylann)!
- - Added the feature to remap keys and shortcuts to arbitrary unicode text sequences.
-
-### Mouse Without Borders
-
- - Removed Thread.Suspend calls when exiting the utility. That call is deprecated, unneeded and was causing a silent crash.
+ - Do not register low level hook if there are no remappings.
### Peek
- - Added the possibility to pause/resume videos with the space bar. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- - Fixed high CPU usage when idle before initializing the main window. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- - Implemented Ctrl+W as a shortcut to close Peek. Thanks [@Physalis2](https://github.com/Physalis2)!
- - Solved an issue incorrectly detecting encoding when previewing code files.
- - Fixed background issues when peeking into HTML files after the WebView 2 upgrade.
+ - Improved icon and title showing for previewed files. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
+ - Added QOI file type support. Thanks [@pedrolamas](https://github.com/pedrolamas)!
### PowerToys Run
- - Moved to WPF-UI and redesigned according to Fluent UX principles. Thanks [@niels9001](https://github.com/niels9001)!
- - Fixed an issue causing 3rd party plugins to not have their custom settings correctly initialized with default values. (This was a hotfix for 0.75) Thanks [@waaverecords](https://github.com/waaverecords)!
- - Fixed a crash in the VSCode plugin when the VSCode path had trailing backspaces. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- - Fixed a crash when trying to load invalid image icons.
- - Fixed a crash in the Programs plugin when getting images for some .lnk files.
- - Fixed a rare startup initialization error and removed cold start operations that were no longer needed. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- - Improved calculations for Windows File Time and Unix Epoch Time in the DateTime plugin. Thanks [@htcfreek](https://github.com/htcfreek)!
- - Fixed a crash when trying to get the icon for a link that pointed to no file.
- - Cleaned up code in the WindowWalker plugin improving the logic. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
+ - Fixed results list UI element height for different maximum number of results value. (This was a hotfix for 0.76)
+ - Fixed icon extraction for .lnk files. (This was a hotfix for 0.76)
+ - Fixed search box UI glitch when FlowDirection is RightToLeft. (This was a hotfix for 0.76)
+ - Fixed theme setting. (This was a hotfix for 0.76)
+ - Fixed error reporting window UI issue. Thanks [@niels9001](https://github.com/niels9001)!
+ - UI improvements and ability to show/hide plugins overview panel. Thanks [@niels9001](https://github.com/niels9001)!
+ - Allow interaction with plugin hints. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
+ - Switch to WPF-UI theme manager. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
+ - Fixed issue causing 3rd party plugin's dependencies dll not being loaded properly. Thanks [@coreyH](https://github.com/CoreyHayward)!
+ - Added configurable font sizes. Thanks [@niels9001](https://github.com/niels9001)!
+ - Changed the text color of plugin hints to improve the contrast when light theme is used. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
+ - Fix scientific notation errors in Calculator plugin. Thanks [@viggyd](https://github.com/viggyd)!
+ - Add URI/URL features to Value generator plugin. Thanks [@htcfreek](https://github.com/htcfreek)!
### Quick Accent
- - Moved from ModernWPF to WPF-UI. Thanks [@niels9001](https://github.com/niels9001)!
- - Added support to the Finnish language character set. Thanks [@davidtlascelles](https://github.com/davidtlascelles)!
- - Added currency symbols for Croatian, Gaeilge, Gàidhlig and Welsh. Thanks [@PesBandi](https://github.com/PesBandi)!
- - Added a missing Latin letter ꝡ. Thanks [@cubedhuang](https://github.com/cubedhuang)!
- - Added fraction characters. Thanks [@PesBandi](https://github.com/PesBandi)!
- - Added support to the Danish language character set. Thanks [@PesBandi](https://github.com/PesBandi)!
- - Added the Kazakhstani Tenge character to the Currencies characters set. Thanks [@PesBandi](https://github.com/PesBandi)!
- - Renamed Slovakian to Slovak, which is the correct term. Thanks [@PesBandi](https://github.com/PesBandi)!
- - Added the Greek language character set. Thanks [@mcbabo](https://github.com/mcbabo)!
+ - Moved Greek specific characters from All language set to Greek. Thanks [@PesBandi](https://github.com/PesBandi)!
+ - Add more mathematical symbols. Thanks [@kevinfu2](https://github.com/kevinfu2)!
### Settings
- - When clicking a module's name on the Dashboard, it will navigate to that module's page.
- - Fixed the clipping of information in the Backup and Restore section of the General Settings page. Thanks [@niels9001](https://github.com/niels9001)!
- - Updated the File Explorer Add-ons fluent icon. Thanks [@niels9001](https://github.com/niels9001)!
- - Added a warning when trying to set a shortcut that might conflict with "Alt Gr" key combinations.
- - Added a direct link to the OOBE's "What's New page" from the main Settings window. Thanks [@iakrayna](https://github.com/iakrayna)!
- - Changed mentions from Microsoft Docs to Microsoft Learn.
- - Fixed the slow reaction to system theme changes.
-
-### Text Extractor
-
- - Move to WPF-UI, localization and light theme support. Thanks [@niels9001](https://github.com/niels9001)!
- - Disabled by default on Windows 11, with a information box on Settings to prefer using the Windows Snipping Tool, which now supports OCR.
+ - Fixed exception occurring on theme change.
+ - Fix "What's new" icon. Thanks [@niels9001](https://github.com/niels9001)!
+ - Remove obsolete UI Font icon properties. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)!
+ - OOBE UI improvements. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)!
+ - XAML Binding improvements. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)!
+ - Fixed crash caused by ThemeListener constructor exceptions.
### Documentation
- - Fixed some typos in the README. Thanks [@Asymtode712](https://github.com/Asymtode712)!
- - Reworked the gpo docs on learn.microsoft.com, adding .admx, registry and Intune information. Thanks [@htcfreek](https://github.com/htcfreek)!
+ - Improved docs for adding new languages to monaco. Thanks [@PesBandi](https://github.com/PesBandi)!
+ - Update README.md to directly state x64 & ARM processor in requirements.
+ - Added Scoop plugin to PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@Quriz](https://github.com/Quriz)!
### Development
- - Updated the check-spelling ci action to 0.22. Thanks [@jsoref](https://github.com/jsoref)!
- - Refactored the modules data model used between the Settings Dashboard and Flyout.
- - Fixed a flaky interop test that was causing automated CI to hang occasionally.
- - Increased the WebView 2 loading timeout to reduce flakiness in those tests. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- - Added support for building with the Dev Drive CopyOnWrite feature, increasing build speed. Thanks [@pedrolamas](https://github.com/pedrolamas)!
- - Addressed the C# static analyzers suggestions. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- - Addressed the C++ static analyzers suggestions.
- - PRs that only contain Markdown or text files changes no longer trigger the full CI. Thanks [@snickler](https://github.com/snickler)!
- - Updated the Microsoft.Windows.CsWinRT to 2.0.4 to fix building with the official Visual Studio 17.8 release.
- - Fixed new code quality issues caught by the official Visual Studio 17.8 release.
- - Added a bot trigger to point contributors to the main new contribution issue on GitHub. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
- - Removed unneeded entries from expect.txt.
- - Turned off a new feature from Visual Studio that was adding the commit hash to the binary files Product Version.
- - Refactored and reviewed the spellcheck entries into different files. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)!
- - Added Spectre mitigation and SHA256 hash creation for some DLLs.
- - Reverted the release pipeline template to a previous release that's stable for shipping PowerToys.
-
-#### What is being planned for version 0.77
-
-For [v0.77][github-next-release-work], we'll work on the items below:
-
- - New utility: Command Not Found
+ - Adopted XamlStyler for PowerToys Run source code. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
+ - Consolidate Microsoft.Windows.SDK.BuildTools across solution.
+ - Upgraded Boost's lib to v1.84.
+ - Upgraded HelixToolkit packages to the latest versions.
+ - Updated sdl baselines.
+
+#### What is being planned for version 0.78
+
+For [v0.78][github-next-release-work], we'll work on the items below:
+
- Language selection
- Automated UI testing through WinAppDriver
- Develop support for Desired State Configuration
diff --git a/doc/devdocs/modules/launcher/new-plugin-checklist.md b/doc/devdocs/modules/launcher/new-plugin-checklist.md
index 3554e3af47c1..1d76e85b7596 100644
--- a/doc/devdocs/modules/launcher/new-plugin-checklist.md
+++ b/doc/devdocs/modules/launcher/new-plugin-checklist.md
@@ -1,11 +1,14 @@
# New plugin checklist
+
- [ ] The plugin is a project under `modules\launcher\Plugins`
- [ ] Microsoft plugin project name pattern: `Microsoft.PowerToys.Run.Plugin.{PluginName}`
- [ ] Community plugin project name pattern: `Community.PowerToys.Run.Plugin.{PluginName}`
+- [ ] The plugin target framework should be `net8.0-windows`
- [ ] The project file should import `Version.props` and specify `$(Version).0`
-- [ ] Make sure `*.csproj` specify only x64 platform target
-- [ ] The plugin has to contain a `plugin.json` file of the following format in its root folder
-```
+- [ ] If the plugin uses any 3rd party dependencies the project file should import `DynamicPlugin.props`
+- [ ] The plugin has to contain a `plugin.json` file of the following format in its root folder:
+
+```json
{
"ID": string, // GUID string
"ActionKeyword": string, // Direct activation phrase
@@ -17,23 +20,26 @@
"Website": "https://aka.ms/powertoys",
"ExecuteFileName": string, // Should be {Type}.PowerToys.Run.Plugin.{PluginName}.dll
"IcoPathDark": string, // Path to dark theme icon. The path is relative to the root plugin folder
- "IcoPathLight": string // Path to light theme icon. The path is relative to the root plugin folder
+ "IcoPathLight": string // Path to light theme icon. The path is relative to the root plugin folder
+ "DynamicLoading": bool // Sets whether the plugin should dynamically load any dependencies isolated from the core application.
}
```
+
- [ ] Make sure your `Main` class contains a public, static string property for the `PluginID`. The plugin id has to be the same as the one in the `plugin.json`file.
+
```csharp
public static string PluginID => "xxxxxxx"; // The part xxxxxxx stands for the plugin ID.
```
+
- [ ] Do not use plugin name or PowerToys as prefixes for entities inside of the plugin project
- [ ] The plugin has to have Unit tests. Use MSTest framework
- [ ] Plugin's output code and assets have to be included in the installer [`Product.wxs`](/installer/PowerToysSetup/Product.wxs)
- [ ] Test the plugin with a local build. Build the installer, install, check that the plugin works as expected
- [ ] All plugin's binaries have to be included in the signed build [`pipeline.user.windows.yml`](/.pipelines/pipeline.user.windows.yml)
-- [ ] The plugin target framework has to be .NET Core 3.1. All dependencies have to have .NET 5 version
+- [ ] The plugin target framework has to be net8.0-windows. All dependencies should be compatible with .NET 8.
Some localization steps can only be done after the first pass by the localization team to provide the localized resources.
In the PR that adds a new plugin, reference a new issue to track the work for fully enabling localization for the new plugin.
- - [ ] Add the resource folder to https://github.com/microsoft/PowerToys/blob/21247c0bb09a1bee3d14d6efa53d0c247f7236af/installer/PowerToysSetup/Product.wxs#L825
- - [ ] Add the resource files under the section https://github.com/microsoft/PowerToys/blob/21247c0bb09a1bee3d14d6efa53d0c247f7236af/installer/PowerToysSetup/Product.wxs#L882
-
+- [ ] Add the resource folder to https://github.com/microsoft/PowerToys/blob/21247c0bb09a1bee3d14d6efa53d0c247f7236af/installer/PowerToysSetup/Product.wxs#L825
+- [ ] Add the resource files under the section https://github.com/microsoft/PowerToys/blob/21247c0bb09a1bee3d14d6efa53d0c247f7236af/installer/PowerToysSetup/Product.wxs#L882
diff --git a/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md b/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md
index 379e7d489491..4a580f090d0e 100644
--- a/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md
+++ b/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md
@@ -1,6 +1,6 @@
# Value Generator Plugin
-The Value Generator plugin is used to generate hashes for strings, to calculate base64 encodings and to generate GUIDs versions 1, 3, 4 and 5.
+The Value Generator plugin is used to generate hashes for strings, to calculate base64 encodings, escape and encode URLs/URIs and to generate GUIDs versions 1, 3, 4 and 5.
![Image of Value Generator plugin](/doc/images/launcher/plugin/community.valuegenerator.png)
@@ -10,7 +10,7 @@ The Value Generator plugin is used to generate hashes for strings, to calculate
- The result of `string ResultToString()` will be used for the Result's title
- The `Description` field will be used for the Result's subtitle
-### [`HashRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Hashing/HashRequest.cs)
+### [`HashRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Hashing/HashRequest.cs)
- Implements IComputeRequest
- Supports the hashing algorithms from System.Security.Cryptography:
- MD5
@@ -20,19 +20,19 @@ The Value Generator plugin is used to generate hashes for strings, to calculate
- SHA512
- If other algorithms are added to System.Security.Cryptography, they can be added to the `_algorithms` dictionary. [`InputParser.ParseInput()`](#inputparser) will need to return a `HashRequest` for the algorithm in the query
-### [`Base64Request`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64Request.cs)
+### [`Base64Request`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64Request.cs)
- Implements IComputeRequest
- `Compute()` will populate `Result` with the base64 encoding of the byte array passed in the constructor
-### [`Base64DecodeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64DecodeRequest.cs)
+### [`Base64DecodeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64DecodeRequest.cs)
- Implements IComputeRequest
- `Compute()` will populate `Result` with the decoded byte array of the base64 string passed in the constructor
-### [`GUIDRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDRequest.cs)
+### [`GUIDRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDRequest.cs)
- Implements IComputeRequest
- Uses the [`GUIDGenerator`](#guidgenerator) class to generate or compute the requested GUID
-### [`GUIDGenerator`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDGenerator.cs)
+### [`GUIDGenerator`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs)
- Utility class for generating or calculating GUIDs
- Generating GUID versions 1 and 4 is done using builtin APIs. [`UuidCreateSequential`](https://learn.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreatesequential) for version 1 and `System.Guid.NewGuid()` for version 4
- Versions 3 and 5 take two parameters, a namespace and a name
@@ -40,6 +40,32 @@ The Value Generator plugin is used to generate hashes for strings, to calculate
- The `PredefinedNamespaces` dictionary contains aliases for the predefined namespaces
- The name can be any string
+### [`UrlEncodeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlEncodeRequest.cs)
+- Implements IComputeRequest
+- `Compute()` will populate `Result` with the encoded url converted using `HttpUtility.UrlEncode()`.
+
+### [`UrlDecodeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlDecodeRequest.cs)
+- Implements IComputeRequest
+- `Compute()` will populate `Result` with the decoded url converted using `HttpUtility.UrlDecode()`.
+
+### [`DataEscapeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataEscapeRequest.cs)
+- Implements IComputeRequest
+- `Compute()` will populate `Result` with the escaped data string converted using `System.Uri.EscapeDataString()`.
+
+### [`DataUnescapeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataUnescapeRequest.cs)
+- Implements IComputeRequest
+- `Compute()` will populate `Result` with the unescaped data string converted using `System.Uri.UnescapeDataString()`.
+
+### [`HexEscapeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexEscapeRequest.cs)
+- Implements IComputeRequest
+- `Compute()` will populate `Result` with the escaped data string converted using `System.Uri.HexEscape()`.
+- Only single characters are supported as input.
+
+### [`HexUnescapeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexUnescapeRequest.cs)
+- Implements IComputeRequest
+- `Compute()` will populate `Result` with the unescaped data string converted using `System.Uri.HexUnescape()`.
+- Only the first hexadecimal character in the string gets unescaped. The rest of the user input is ignored.
+
### [`InputParser`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs)
- It is responsible only for parsing the query from the user
- Based on the user query, the `ParseInput()` method must return an object that implements the `IComputeRequest` interface or it must throw one of `FormatException` or `ArgumentException`
@@ -51,6 +77,6 @@ The Value Generator plugin is used to generate hashes for strings, to calculate
> The error message will not be shown to the user but a log message will be created
### Adding a new value generator
-1. To add a new value generator, create a folder under `/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/` and inside it add a class that implements `IComputeRequest`.
+1. To add a new value generator, create a folder under `/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/` and inside it add a class that implements `IComputeRequest`.
2. Add any utility classes that are specific to the new generator inside the same folder to keep them separated from the other generators.
3. Modify the `InputParser.ParseInput()` to handle a request for the new generator and return an instance of the class you created in step 1
\ No newline at end of file
diff --git a/doc/images/icons/Command Not Found.png b/doc/images/icons/Command Not Found.png
new file mode 100644
index 000000000000..8243b9f8c8eb
Binary files /dev/null and b/doc/images/icons/Command Not Found.png differ
diff --git a/doc/images/overview/Original/PowerLauncher.png b/doc/images/overview/Original/PowerLauncher.png
index 701969e2c77a..597e64067fdf 100644
Binary files a/doc/images/overview/Original/PowerLauncher.png and b/doc/images/overview/Original/PowerLauncher.png differ
diff --git a/doc/images/overview/PowerLauncher_large.png b/doc/images/overview/PowerLauncher_large.png
index aecb74c9fb65..063e51be4c31 100644
Binary files a/doc/images/overview/PowerLauncher_large.png and b/doc/images/overview/PowerLauncher_large.png differ
diff --git a/doc/images/overview/PowerLauncher_small.png b/doc/images/overview/PowerLauncher_small.png
index cdf5776b37db..0ef995180f54 100644
Binary files a/doc/images/overview/PowerLauncher_small.png and b/doc/images/overview/PowerLauncher_small.png differ
diff --git a/doc/thirdPartyRunPlugins.md b/doc/thirdPartyRunPlugins.md
index bf85dd421c93..0e9f4a62ab37 100644
--- a/doc/thirdPartyRunPlugins.md
+++ b/doc/thirdPartyRunPlugins.md
@@ -34,3 +34,6 @@ Contact the developers of a plugin directly for assistance with a specific plugi
| [Visual Studio](https://github.com/davidegiacometti/PowerToys-Run-VisualStudio) | [davidegiacometti](https://github.com/davidegiacometti) | Open Visual Studio recents |
| [WinGet](https://github.com/bostrot/PowerToysRunPluginWinget) | [bostrot](https://github.com/bostrot) | Search and install packages from WinGet |
| [Scoop](https://github.com/Quriz/PowerToysRunScoop) | [Quriz](https://github.com/Quriz) | Search and install packages from Scoop |
+| [Spotify](https://github.com/waaverecords/PowerToys-Run-Spotify) | [waaverecords](https://github.com/waaverecords) | Search Spotify and control its player |
+| [Input Typer](https://github.com/CoreyHayward/PowerToys-Run-InputTyper) | [CoreyHayward](https://github.com/CoreyHayward) | Type the input as if sent from a keyboard |
+| [Clipboard Manager](https://github.com/CoreyHayward/PowerToys-Run-ClipboardManager) | [CoreyHayward](https://github.com/CoreyHayward) | Search and paste text from your clipboard history |
\ No newline at end of file
diff --git a/installer/PowerToysSetup/CmdNotFound.wxs b/installer/PowerToysSetup/CmdNotFound.wxs
new file mode 100644
index 000000000000..3ac8445d887d
--- /dev/null
+++ b/installer/PowerToysSetup/CmdNotFound.wxs
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetup/Common.wxi b/installer/PowerToysSetup/Common.wxi
index 4a1c93fc8ea3..6e68ce19861d 100644
--- a/installer/PowerToysSetup/Common.wxi
+++ b/installer/PowerToysSetup/Common.wxi
@@ -18,6 +18,7 @@
+
diff --git a/installer/PowerToysSetup/PowerToysInstaller.wixproj b/installer/PowerToysSetup/PowerToysInstaller.wixproj
index 6a4f3b80cb79..8fb186ebb644 100644
--- a/installer/PowerToysSetup/PowerToysInstaller.wixproj
+++ b/installer/PowerToysSetup/PowerToysInstaller.wixproj
@@ -1,5 +1,6 @@
-
+
@@ -14,7 +15,7 @@ SET PTRoot=$(SolutionDir)\..
call "..\..\..\publish.cmd" x64
)
call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuildThisFileDirectory)\generateMonacoWxs.ps1 -monacoWxsFile "$(MSBuildThisFileDirectory)\MonacoSRC.wxs"
-
+
@@ -25,7 +26,7 @@ SET PTRoot=$(SolutionDir)\..
call "..\..\..\publish.cmd" arm64
)
call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuildThisFileDirectory)\generateMonacoWxs.ps1 -monacoWxsFile "$(MSBuildThisFileDirectory)\MonacoSRC.wxs"
-
+
Always
@@ -67,7 +68,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
- Release
+ Release
$(Platform)
3.10
022a9d30-7c4f-416d-a9df-5ff2661cc0ad
@@ -103,6 +104,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
+
@@ -180,17 +182,6 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
-->
-
+
\ No newline at end of file
diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs
index 2dea1fab5570..ee15ec090a59 100644
--- a/installer/PowerToysSetup/Product.wxs
+++ b/installer/PowerToysSetup/Product.wxs
@@ -72,6 +72,7 @@
+
@@ -133,6 +134,7 @@
+
@@ -159,6 +161,9 @@
Installed AND (REMOVE="ALL")
+
+ Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
+
Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
@@ -200,6 +205,10 @@
Property="UnApplyModulesRegistryChangeSets"
Value="[INSTALLFOLDER]" />
+
+
@@ -252,7 +261,15 @@
BinaryKey="PTCustomActions"
DllEntry="UninstallServicesCA"
/>
-
+
+
+
+
+
@@ -45,6 +47,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -55,7 +70,9 @@
+
+
diff --git a/installer/PowerToysSetupCustomActions/CustomAction.cpp b/installer/PowerToysSetupCustomActions/CustomAction.cpp
index c76e2203f885..290df09898f4 100644
--- a/installer/PowerToysSetupCustomActions/CustomAction.cpp
+++ b/installer/PowerToysSetupCustomActions/CustomAction.cpp
@@ -433,7 +433,29 @@ UINT __stdcall RemoveWindowsServiceByName(std::wstring serviceName)
return ERROR_SUCCESS;
}
+UINT __stdcall UninstallCommandNotFoundModuleCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ std::wstring installationFolder;
+ std::string command;
+ hr = WcaInitialize(hInstall, "UninstallCommandNotFoundModule");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ hr = getInstallFolder(hInstall, installationFolder);
+ ExitOnFailure(hr, "Failed to get installFolder.");
+
+ command = "pwsh.exe";
+ command += " ";
+ command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(installationFolder) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "\"";
+
+ system(command.c_str());
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
UINT __stdcall UninstallServicesCA(MSIHANDLE hInstall)
{
diff --git a/installer/PowerToysSetupCustomActions/CustomAction.def b/installer/PowerToysSetupCustomActions/CustomAction.def
index 7c3c058d9cd1..6a503da797c0 100644
--- a/installer/PowerToysSetupCustomActions/CustomAction.def
+++ b/installer/PowerToysSetupCustomActions/CustomAction.def
@@ -22,4 +22,5 @@ EXPORTS
UninstallVirtualCameraDriverCA
UnRegisterContextMenuPackagesCA
UninstallEmbeddedMSIXCA
- UninstallServicesCA
\ No newline at end of file
+ UninstallServicesCA
+ UninstallCommandNotFoundModuleCA
\ No newline at end of file
diff --git a/nuget.config b/nuget.config
index 81243afb3c86..0643a02af3c0 100644
--- a/nuget.config
+++ b/nuget.config
@@ -6,8 +6,5 @@
-
-
-
-
\ No newline at end of file
+
diff --git a/packages.config b/packages.config
new file mode 100644
index 000000000000..c99cd91fd719
--- /dev/null
+++ b/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.editorconfig b/src/.editorconfig
index 68812c52b97c..28245c30fc47 100644
--- a/src/.editorconfig
+++ b/src/.editorconfig
@@ -96,49 +96,4 @@ end_of_line = crlf
dotnet_diagnostic.IDE0065.severity = none
# IDE0009: Add this or Me qualification
-dotnet_diagnostic.IDE0009.severity = none
-
-# CA1859: Change type for improved performance
-dotnet_diagnostic.CA1859.severity = none
-
-# CA1716: Identifiers should not match keywords
-dotnet_diagnostic.CA1716.severity = none
-
-# SYSLIB1096: Convert to 'GeneratedComInterface'
-dotnet_diagnostic.SYSLIB1096.severity = silent
-
-# CA1309: Use ordinal StringComparison
-dotnet_diagnostic.CA1309.severity = suggestion
-
-# CS1615: Argument may not be passed with the ref keyword
-dotnet_diagnostic.CS1615.severity = none
-
-# CA1854: Prefer a 'TryGetValue' call over a Dictionary indexer access guarded by a 'ContainsKey' check to avoid double lookup
-dotnet_diagnostic.CA1854.severity = suggestion
-
-# CA1860: Avoid using 'Enumerable.Any()' extension method
-dotnet_diagnostic.CA1860.severity = suggestion
-
-# CA1861: Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array
-dotnet_diagnostic.CA1861.severity = suggestion
-
-# CA1862: Prefer using 'StringComparer'/'StringComparison' to perform case-insensitive string comparisons
-dotnet_diagnostic.CA1862.severity = suggestion
-
-# CA1863: Cache a CompositeFormat for repeated use in this formatting operation
-dotnet_diagnostic.CA1863.severity = none
-
-# CA1864: Prefer the 'IDictionary.TryAdd(TKey, TValue)' method
-dotnet_diagnostic.CA1864.severity = suggestion
-
-# CA1865: Use 'string.Method(char)' instead of 'string.Method(string)' for string with single char
-dotnet_diagnostic.CA1865.severity = suggestion
-
-# CA1869: Cache and reuse 'JsonSerializerOptions' instances
-dotnet_diagnostic.CA1869.severity = none
-
-# CA2208: Instantiate argument exceptions correctly
-dotnet_diagnostic.CA2208.severity = none
-
-# CS9191: The 'ref' modifier for argument corresponding to 'in' is equivalent to 'in'. Consider using 'in' instead.
-dotnet_diagnostic.CS9191.severity = none
+dotnet_diagnostic.IDE0009.severity = none
\ No newline at end of file
diff --git a/src/ActionRunner/actionRunner.vcxproj b/src/ActionRunner/actionRunner.vcxproj
index ebe3fc8fc8af..8809611842f1 100644
--- a/src/ActionRunner/actionRunner.vcxproj
+++ b/src/ActionRunner/actionRunner.vcxproj
@@ -62,7 +62,7 @@
-
+
@@ -70,6 +70,6 @@
-
+
\ No newline at end of file
diff --git a/src/ActionRunner/packages.config b/src/ActionRunner/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/ActionRunner/packages.config
+++ b/src/ActionRunner/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/Update/PowerToys.Update.vcxproj b/src/Update/PowerToys.Update.vcxproj
index e04037475406..914732926ceb 100644
--- a/src/Update/PowerToys.Update.vcxproj
+++ b/src/Update/PowerToys.Update.vcxproj
@@ -68,7 +68,7 @@
-
+
@@ -76,6 +76,6 @@
-
+
\ No newline at end of file
diff --git a/src/Update/packages.config b/src/Update/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/Update/packages.config
+++ b/src/Update/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/common/AllExperiments/Experiments.cs b/src/common/AllExperiments/Experiments.cs
index 426e7af1d1d9..2fdcaa0482a9 100644
--- a/src/common/AllExperiments/Experiments.cs
+++ b/src/common/AllExperiments/Experiments.cs
@@ -145,7 +145,7 @@ private async Task VariantAssignmentProvider_Initialize()
private string? AssignmentUnit { get; set; }
- private IVariantAssignmentRequest GetVariantAssignmentRequest()
+ private VariantAssignmentRequest GetVariantAssignmentRequest()
{
var jsonFilePath = CreateFilePath();
try
diff --git a/src/common/COMUtils/COMUtils.vcxproj b/src/common/COMUtils/COMUtils.vcxproj
index 6a49eb786067..f582df593bf9 100644
--- a/src/common/COMUtils/COMUtils.vcxproj
+++ b/src/common/COMUtils/COMUtils.vcxproj
@@ -36,12 +36,12 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
\ No newline at end of file
diff --git a/src/common/COMUtils/packages.config b/src/common/COMUtils/packages.config
index 3d9798ef5e72..6199e2345ccd 100644
--- a/src/common/COMUtils/packages.config
+++ b/src/common/COMUtils/packages.config
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/src/common/FilePreviewCommon/Formatters/JsonFormatter.cs b/src/common/FilePreviewCommon/Formatters/JsonFormatter.cs
index 4f366fbff831..91b7013c6fdd 100644
--- a/src/common/FilePreviewCommon/Formatters/JsonFormatter.cs
+++ b/src/common/FilePreviewCommon/Formatters/JsonFormatter.cs
@@ -12,6 +12,12 @@ public class JsonFormatter : IFormatter
///
public string LangSet => "json";
+ private static readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions
+ {
+ WriteIndented = true,
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ };
+
///
public string Format(string value)
{
@@ -22,11 +28,7 @@ public string Format(string value)
using (var jDocument = JsonDocument.Parse(value, new JsonDocumentOptions { CommentHandling = JsonCommentHandling.Skip }))
{
- return JsonSerializer.Serialize(jDocument, new JsonSerializerOptions
- {
- WriteIndented = true,
- Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
- });
+ return JsonSerializer.Serialize(jDocument, _serializerOptions);
}
}
}
diff --git a/src/common/GPOWrapper/GPOWrapper.cpp b/src/common/GPOWrapper/GPOWrapper.cpp
index 77d81e85160c..c3177d60dbee 100644
--- a/src/common/GPOWrapper/GPOWrapper.cpp
+++ b/src/common/GPOWrapper/GPOWrapper.cpp
@@ -12,6 +12,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{
return static_cast(powertoys_gpo::getConfiguredAwakeEnabledValue());
}
+ GpoRuleConfigured GPOWrapper::GetConfiguredCmdNotFoundEnabledValue()
+ {
+ return static_cast(powertoys_gpo::getConfiguredCmdNotFoundEnabledValue());
+ }
GpoRuleConfigured GPOWrapper::GetConfiguredColorPickerEnabledValue()
{
return static_cast(powertoys_gpo::getConfiguredColorPickerEnabledValue());
diff --git a/src/common/GPOWrapper/GPOWrapper.h b/src/common/GPOWrapper/GPOWrapper.h
index c29fac95b6a9..bd153ed25833 100644
--- a/src/common/GPOWrapper/GPOWrapper.h
+++ b/src/common/GPOWrapper/GPOWrapper.h
@@ -9,6 +9,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation
GPOWrapper() = default;
static GpoRuleConfigured GetConfiguredAlwaysOnTopEnabledValue();
static GpoRuleConfigured GetConfiguredAwakeEnabledValue();
+ static GpoRuleConfigured GetConfiguredCmdNotFoundEnabledValue();
static GpoRuleConfigured GetConfiguredColorPickerEnabledValue();
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
diff --git a/src/common/GPOWrapper/GPOWrapper.idl b/src/common/GPOWrapper/GPOWrapper.idl
index d0e3ebbd4fb6..3b4c0eca2800 100644
--- a/src/common/GPOWrapper/GPOWrapper.idl
+++ b/src/common/GPOWrapper/GPOWrapper.idl
@@ -13,6 +13,7 @@ namespace PowerToys
[default_interface] static runtimeclass GPOWrapper {
static GpoRuleConfigured GetConfiguredAlwaysOnTopEnabledValue();
static GpoRuleConfigured GetConfiguredAwakeEnabledValue();
+ static GpoRuleConfigured GetConfiguredCmdNotFoundEnabledValue();
static GpoRuleConfigured GetConfiguredColorPickerEnabledValue();
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
diff --git a/src/common/GPOWrapperProjection/GPOWrapper.cs b/src/common/GPOWrapperProjection/GPOWrapper.cs
index 92196323e5e4..5e1ef147484b 100644
--- a/src/common/GPOWrapperProjection/GPOWrapper.cs
+++ b/src/common/GPOWrapperProjection/GPOWrapper.cs
@@ -27,6 +27,11 @@ public static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue()
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredFancyZonesEnabledValue();
}
+ public static GpoRuleConfigured GetConfiguredCmdNotFoundEnabledValue()
+ {
+ return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredCmdNotFoundEnabledValue();
+ }
+
public static GpoRuleConfigured GetConfiguredColorPickerEnabledValue()
{
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredColorPickerEnabledValue();
diff --git a/src/common/ManagedCommon/ManagedCommon.csproj b/src/common/ManagedCommon/ManagedCommon.csproj
index 53ca704109c8..dbe6eca3f7ba 100644
--- a/src/common/ManagedCommon/ManagedCommon.csproj
+++ b/src/common/ManagedCommon/ManagedCommon.csproj
@@ -4,7 +4,7 @@
net8.0-windows
- win-x64;win-arm64
+ win-x64;win-arm64
$(Version).0
Microsoft Corporation
PowerToys
diff --git a/src/common/ManagedCommon/ModuleType.cs b/src/common/ManagedCommon/ModuleType.cs
index 3a489711b08a..0bbfaeeda357 100644
--- a/src/common/ManagedCommon/ModuleType.cs
+++ b/src/common/ManagedCommon/ModuleType.cs
@@ -9,6 +9,7 @@ public enum ModuleType
AlwaysOnTop,
Awake,
ColorPicker,
+ CmdNotFound,
CropAndLock,
EnvironmentVariables,
FancyZones,
diff --git a/src/common/SettingsAPI/FileWatcher.cpp b/src/common/SettingsAPI/FileWatcher.cpp
index 7a0c778cf627..dc25e15c7a92 100644
--- a/src/common/SettingsAPI/FileWatcher.cpp
+++ b/src/common/SettingsAPI/FileWatcher.cpp
@@ -1,5 +1,6 @@
#include "pch.h"
#include "FileWatcher.h"
+#include
std::optional FileWatcher::MyFileTime()
{
@@ -19,50 +20,47 @@ std::optional FileWatcher::MyFileTime()
return result;
}
-void FileWatcher::Run()
+FileWatcher::FileWatcher(const std::wstring& path, std::function callback) :
+ m_path(path),
+ m_callback(callback)
{
- while (1)
- {
- auto lastWrite = MyFileTime();
- if (!m_lastWrite.has_value())
- {
- m_lastWrite = lastWrite;
- }
- else if (lastWrite.has_value())
- {
- if (m_lastWrite->dwHighDateTime != lastWrite->dwHighDateTime ||
- m_lastWrite->dwLowDateTime != lastWrite->dwLowDateTime)
+ std::filesystem::path fsPath(path);
+ m_file_name = fsPath.filename();
+ std::transform(m_file_name.begin(), m_file_name.end(), m_file_name.begin(), ::towlower);
+ m_folder_change_reader = wil::make_folder_change_reader_nothrow(
+ fsPath.parent_path().c_str(),
+ false,
+ wil::FolderChangeEvents::LastWriteTime,
+ [this](wil::FolderChangeEvent, PCWSTR fileName) {
+ std::wstring lowerFileName(fileName);
+ std::transform(lowerFileName.begin(), lowerFileName.end(), lowerFileName.begin(), ::towlower);
+
+ if (m_file_name.compare(fileName) == 0)
{
- m_lastWrite = lastWrite;
- m_callback();
+ auto lastWrite = MyFileTime();
+ if (!m_lastWrite.has_value())
+ {
+ m_lastWrite = lastWrite;
+ }
+ else if (lastWrite.has_value())
+ {
+ if (m_lastWrite->dwHighDateTime != lastWrite->dwHighDateTime ||
+ m_lastWrite->dwLowDateTime != lastWrite->dwLowDateTime)
+ {
+ m_lastWrite = lastWrite;
+ m_callback();
+ }
+ }
}
- }
-
- if (WaitForSingleObject(m_abortEvent, m_refreshPeriod) == WAIT_OBJECT_0)
- {
- return;
- }
- }
-}
+ });
-FileWatcher::FileWatcher(const std::wstring& path, std::function callback, DWORD refreshPeriod) :
- m_refreshPeriod(refreshPeriod),
- m_path(path),
- m_callback(callback)
-{
- m_abortEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
- if (m_abortEvent)
+ if (!m_folder_change_reader)
{
- m_thread = std::thread([this]() { Run(); });
+ Logger::error(L"Failed to start folder change reader for path {}. {}", path, get_last_error_or_default(GetLastError()));
}
}
FileWatcher::~FileWatcher()
{
- if (m_abortEvent)
- {
- SetEvent(m_abortEvent);
- m_thread.join();
- CloseHandle(m_abortEvent);
- }
+ m_folder_change_reader.reset();
}
diff --git a/src/common/SettingsAPI/FileWatcher.h b/src/common/SettingsAPI/FileWatcher.h
index 206c1cd74778..073a33a0c81c 100644
--- a/src/common/SettingsAPI/FileWatcher.h
+++ b/src/common/SettingsAPI/FileWatcher.h
@@ -11,16 +11,14 @@
class FileWatcher
{
- DWORD m_refreshPeriod;
std::wstring m_path;
+ std::wstring m_file_name;
std::optional m_lastWrite;
std::function m_callback;
- HANDLE m_abortEvent;
- std::thread m_thread;
-
+ wil::unique_folder_change_reader_nothrow m_folder_change_reader;
+
std::optional MyFileTime();
- void Run();
public:
- FileWatcher(const std::wstring& path, std::function callback, DWORD refreshPeriod = 1000);
+ FileWatcher(const std::wstring& path, std::function callback);
~FileWatcher();
};
diff --git a/src/common/SettingsAPI/SettingsAPI.vcxproj b/src/common/SettingsAPI/SettingsAPI.vcxproj
index 306b4a8c3be5..c8a06a1a9e56 100644
--- a/src/common/SettingsAPI/SettingsAPI.vcxproj
+++ b/src/common/SettingsAPI/SettingsAPI.vcxproj
@@ -47,10 +47,15 @@
{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}
+
+ {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}
+
+
+
@@ -58,5 +63,6 @@
+
\ No newline at end of file
diff --git a/src/common/SettingsAPI/pch.h b/src/common/SettingsAPI/pch.h
index 9526a2f07203..86f6eff28321 100644
--- a/src/common/SettingsAPI/pch.h
+++ b/src/common/SettingsAPI/pch.h
@@ -9,3 +9,5 @@
#include
#include
+#include
+#include
diff --git a/src/common/interop/PowerToys.Interop.vcxproj b/src/common/interop/PowerToys.Interop.vcxproj
index 3f792f85ce21..75382dabfb6c 100644
--- a/src/common/interop/PowerToys.Interop.vcxproj
+++ b/src/common/interop/PowerToys.Interop.vcxproj
@@ -136,12 +136,12 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
\ No newline at end of file
diff --git a/src/common/interop/PowerToys.Interop.vcxproj.filters b/src/common/interop/PowerToys.Interop.vcxproj.filters
index b54ad522d3d9..cf0425666e52 100644
--- a/src/common/interop/PowerToys.Interop.vcxproj.filters
+++ b/src/common/interop/PowerToys.Interop.vcxproj.filters
@@ -68,6 +68,9 @@
Resource Files
+
+
+
diff --git a/src/common/interop/packages.config b/src/common/interop/packages.config
index 3d9798ef5e72..6199e2345ccd 100644
--- a/src/common/interop/packages.config
+++ b/src/common/interop/packages.config
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/src/common/logger/logger_settings.h b/src/common/logger/logger_settings.h
index 91dd5faae8ab..cc1b3825cecc 100644
--- a/src/common/logger/logger_settings.h
+++ b/src/common/logger/logger_settings.h
@@ -67,6 +67,8 @@ struct LogSettings
inline const static std::string cropAndLockLoggerName = "crop-and-lock";
inline const static std::wstring registryPreviewLogPath = L"Logs\\registryPreview-log.txt";
inline const static std::string environmentVariablesLoggerName = "environment-variables";
+ inline const static std::wstring cmdNotFoundLogPath = L"Logs\\cmd-not-found-log.txt";
+ inline const static std::string cmdNotFoundLoggerName = "cmd-not-found";
inline const static int retention = 30;
std::wstring logLevel;
LogSettings();
diff --git a/src/common/notifications/NotificationUtil.h b/src/common/notifications/NotificationUtil.h
new file mode 100644
index 000000000000..8fcf3d91d8c3
--- /dev/null
+++ b/src/common/notifications/NotificationUtil.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include "Generated Files/resource.h"
+
+namespace notifications
+{
+ // Non-Localizable strings
+ namespace NonLocalizable
+ {
+ const wchar_t RunAsAdminInfoPage[] = L"https://aka.ms/powertoysDetectedElevatedHelp";
+ const wchar_t ToastNotificationButtonUrl[] = L"powertoys://cant_drag_elevated_disable/";
+ }
+
+ inline void WarnIfElevationIsRequired(std::wstring title, std::wstring message, std::wstring button1, std::wstring button2)
+ {
+ using namespace NonLocalizable;
+
+ auto settings = PTSettingsHelper::load_general_settings();
+ auto enableWarningsElevatedApps = settings.GetNamedBoolean(L"enable_warnings_elevated_apps", true);
+
+ static bool warning_shown = false;
+ if (enableWarningsElevatedApps && !warning_shown && !is_toast_disabled(ElevatedDontShowAgainRegistryPath, ElevatedDisableIntervalInDays))
+ {
+ std::vector actions = {
+ link_button{ button1, RunAsAdminInfoPage },
+ link_button{ button2, ToastNotificationButtonUrl }
+ };
+ show_toast_with_activations(message,
+ title,
+ {},
+ std::move(actions));
+ warning_shown = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/common/notifications/dont_show_again.h b/src/common/notifications/dont_show_again.h
index 98657b1e7e0c..f1cdd0f32242 100644
--- a/src/common/notifications/dont_show_again.h
+++ b/src/common/notifications/dont_show_again.h
@@ -4,8 +4,8 @@
namespace notifications
{
- const inline wchar_t CantDragElevatedDontShowAgainRegistryPath[] = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain\{e16ea82f-6d94-4f30-bb02-d6d911588afd})";
- const inline int64_t CantDragElevatedDisableIntervalInDays = 30;
+ const inline wchar_t ElevatedDontShowAgainRegistryPath[] = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain\{e16ea82f-6d94-4f30-bb02-d6d911588afd})";
+ const inline int64_t ElevatedDisableIntervalInDays = 30;
const inline wchar_t PreviewModulesDontShowAgainRegistryPath[] = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain\{7e29e2b2-b31c-4dcd-b7b0-79c078b02430})";
const inline int64_t PreviewModulesDisableIntervalInDays = 30;
diff --git a/src/common/notifications/notifications.vcxproj b/src/common/notifications/notifications.vcxproj
index c66752318802..c0438616ad57 100644
--- a/src/common/notifications/notifications.vcxproj
+++ b/src/common/notifications/notifications.vcxproj
@@ -27,6 +27,7 @@
+
@@ -43,7 +44,7 @@
-
+
@@ -51,6 +52,6 @@
-
+
\ No newline at end of file
diff --git a/src/common/notifications/packages.config b/src/common/notifications/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/common/notifications/packages.config
+++ b/src/common/notifications/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/common/updating/packages.config b/src/common/updating/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/common/updating/packages.config
+++ b/src/common/updating/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/common/updating/updating.vcxproj b/src/common/updating/updating.vcxproj
index 97089b03a1f1..dfd2817d8376 100644
--- a/src/common/updating/updating.vcxproj
+++ b/src/common/updating/updating.vcxproj
@@ -58,7 +58,7 @@
-
+
@@ -66,6 +66,6 @@
-
+
\ No newline at end of file
diff --git a/src/common/updating/updating.vcxproj.filters b/src/common/updating/updating.vcxproj.filters
index 5952e2f3bd1c..4eb124fc0c65 100644
--- a/src/common/updating/updating.vcxproj.filters
+++ b/src/common/updating/updating.vcxproj.filters
@@ -45,4 +45,7 @@
+
+
+
\ No newline at end of file
diff --git a/src/common/utils/elevation.h b/src/common/utils/elevation.h
index 177a4b24e29a..62ab1c75d5f2 100644
--- a/src/common/utils/elevation.h
+++ b/src/common/utils/elevation.h
@@ -494,3 +494,30 @@ inline bool check_user_is_admin()
freeMemory(pSID, pGroupInfo);
return false;
}
+
+inline bool IsProcessOfWindowElevated(HWND window)
+{
+ DWORD pid = 0;
+ GetWindowThreadProcessId(window, &pid);
+ if (!pid)
+ {
+ return false;
+ }
+
+ wil::unique_handle hProcess{ OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
+ FALSE,
+ pid) };
+
+ wil::unique_handle token;
+
+ if (OpenProcessToken(hProcess.get(), TOKEN_QUERY, &token))
+ {
+ TOKEN_ELEVATION elevation;
+ DWORD size;
+ if (GetTokenInformation(token.get(), TokenElevation, &elevation, sizeof(elevation), &size))
+ {
+ return elevation.TokenIsElevated != 0;
+ }
+ }
+ return false;
+}
diff --git a/src/common/utils/gpo.h b/src/common/utils/gpo.h
index be88e0d22d77..016bd744cc28 100644
--- a/src/common/utils/gpo.h
+++ b/src/common/utils/gpo.h
@@ -24,6 +24,7 @@ namespace powertoys_gpo {
const std::wstring POLICY_CONFIGURE_ENABLED_GLOBAL_ALL_UTILITIES = L"ConfigureGlobalUtilityEnabledState";
const std::wstring POLICY_CONFIGURE_ENABLED_ALWAYS_ON_TOP = L"ConfigureEnabledUtilityAlwaysOnTop";
const std::wstring POLICY_CONFIGURE_ENABLED_AWAKE = L"ConfigureEnabledUtilityAwake";
+ const std::wstring POLICY_CONFIGURE_ENABLED_CMD_NOT_FOUND = L"ConfigureEnabledUtilityCmdNotFound";
const std::wstring POLICY_CONFIGURE_ENABLED_COLOR_PICKER = L"ConfigureEnabledUtilityColorPicker";
const std::wstring POLICY_CONFIGURE_ENABLED_CROP_AND_LOCK = L"ConfigureEnabledUtilityCropAndLock";
const std::wstring POLICY_CONFIGURE_ENABLED_FANCYZONES = L"ConfigureEnabledUtilityFancyZones";
@@ -224,6 +225,11 @@ namespace powertoys_gpo {
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_AWAKE);
}
+ inline gpo_rule_configured_t getConfiguredCmdNotFoundEnabledValue()
+ {
+ return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_CMD_NOT_FOUND);
+ }
+
inline gpo_rule_configured_t getConfiguredColorPickerEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_COLOR_PICKER);
diff --git a/src/common/utils/window.h b/src/common/utils/window.h
index cfcf20534ac0..dbce3a95ee03 100644
--- a/src/common/utils/window.h
+++ b/src/common/utils/window.h
@@ -6,9 +6,13 @@
#include
#include
+#include
+#include
// Initializes and runs windows message loop
-inline int run_message_loop(const bool until_idle = false, const std::optional timeout_ms = {})
+inline int run_message_loop(const bool until_idle = false,
+ const std::optional timeout_ms = {},
+ std::unordered_map> wm_app_msg_callbacks = {})
{
MSG msg{};
bool stop = false;
@@ -24,11 +28,16 @@ inline int run_message_loop(const bool until_idle = false, const std::optionalsecond();
}
+
if (timeout_ms.has_value())
{
KillTimer(nullptr, timerId);
}
+
return static_cast(msg.wParam);
}
@@ -60,7 +69,7 @@ template
inline T GetWindowCreateParam(LPARAM lparam)
{
static_assert(sizeof(T) <= sizeof(void*));
- T data{ static_cast (reinterpret_cast(lparam)->lpCreateParams) };
+ T data{ static_cast(reinterpret_cast(lparam)->lpCreateParams) };
return data;
}
@@ -74,5 +83,5 @@ inline void StoreWindowParam(HWND window, T data)
template
inline T GetWindowParam(HWND window)
{
- return reinterpret_cast (GetWindowLongPtrW(window, GWLP_USERDATA));
+ return reinterpret_cast(GetWindowLongPtrW(window, GWLP_USERDATA));
}
diff --git a/src/common/version/version.h b/src/common/version/version.h
index 8c3ffd302d16..b30061956599 100644
--- a/src/common/version/version.h
+++ b/src/common/version/version.h
@@ -13,7 +13,7 @@
#define PRODUCT_VERSION_STRING FILE_VERSION_STRING
#define COMPANY_NAME "Microsoft Corporation"
-#define COPYRIGHT_NOTE "Copyright (C) 2023 Microsoft Corporation"
+#define COPYRIGHT_NOTE "Copyright (C) 2024 Microsoft Corporation"
#define PRODUCT_NAME "PowerToys"
#include
diff --git a/src/gpo/assets/PowerToys.admx b/src/gpo/assets/PowerToys.admx
index 0b135f665e5d..42ebfe334996 100644
--- a/src/gpo/assets/PowerToys.admx
+++ b/src/gpo/assets/PowerToys.admx
@@ -1,11 +1,11 @@
-
+
-
+
@@ -15,6 +15,7 @@
+
@@ -59,6 +60,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/gpo/assets/en-US/PowerToys.adml b/src/gpo/assets/en-US/PowerToys.adml
index de04d77c8bd4..2eb3c700b3e6 100644
--- a/src/gpo/assets/en-US/PowerToys.adml
+++ b/src/gpo/assets/en-US/PowerToys.adml
@@ -1,7 +1,7 @@
-
+
PowerToys
PowerToys
@@ -17,6 +17,7 @@
PowerToys version 0.73.0 or later
PowerToys version 0.75.0 or later
PowerToys version 0.76.0 or later
+ PowerToys version 0.77.0 or later
This policy configures the enabled state for all PowerToys utilities.
@@ -112,6 +113,7 @@ Note: Changes require a restart of PowerToys Run.
Always On Top: Configure enabled state
Awake: Configure enabled state
Color Picker: Configure enabled state
+ Command Not Found: Configure enabled state
Crop And Lock: Configure enabled state
Environment Variables: Configure enabled state
FancyZones: Configure enabled state
diff --git a/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj b/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj
index 04833d956711..fabdbc9c2531 100644
--- a/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj
+++ b/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj
@@ -155,8 +155,8 @@
-
+
@@ -164,7 +164,7 @@
-
+
\ No newline at end of file
diff --git a/src/modules/CropAndLock/CropAndLock/packages.config b/src/modules/CropAndLock/CropAndLock/packages.config
index 1a0b177ac118..fe9625b6da89 100644
--- a/src/modules/CropAndLock/CropAndLock/packages.config
+++ b/src/modules/CropAndLock/CropAndLock/packages.config
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/src/modules/CropAndLock/CropAndLockModuleInterface/CropAndLockModuleInterface.vcxproj b/src/modules/CropAndLock/CropAndLockModuleInterface/CropAndLockModuleInterface.vcxproj
index 7c26becd5eab..6c44320941b7 100644
--- a/src/modules/CropAndLock/CropAndLockModuleInterface/CropAndLockModuleInterface.vcxproj
+++ b/src/modules/CropAndLock/CropAndLockModuleInterface/CropAndLockModuleInterface.vcxproj
@@ -102,8 +102,8 @@
-
+
@@ -112,7 +112,7 @@
-
+
\ No newline at end of file
diff --git a/src/modules/CropAndLock/CropAndLockModuleInterface/packages.config b/src/modules/CropAndLock/CropAndLockModuleInterface/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/modules/CropAndLock/CropAndLockModuleInterface/packages.config
+++ b/src/modules/CropAndLock/CropAndLockModuleInterface/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj
index 4d544d3b658b..2c7035035d17 100644
--- a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj
+++ b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj
@@ -58,11 +58,6 @@
-
-
- https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json
-
-
diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/Views/MainPage.xaml b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/Views/MainPage.xaml
index 37ee7095fe12..5be8dc688db6 100644
--- a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/Views/MainPage.xaml
+++ b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/Views/MainPage.xaml
@@ -2,14 +2,14 @@
x:Class="EnvironmentVariables.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:converters="using:EnvironmentVariables.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:ic="using:Microsoft.Xaml.Interactions.Core"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:EnvironmentVariables.Models"
- xmlns:toolkitConverters="using:CommunityToolkit.WinUI.Converters"
+ xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
+ xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
xmlns:ui="using:CommunityToolkit.WinUI"
x:Name="RootPage"
mc:Ignorable="d">
@@ -22,12 +22,12 @@
-
-
+
-
-
+
+
-
+
-
+
@@ -98,12 +98,12 @@
-
-
-
+
+
@@ -194,22 +194,22 @@
-
-
+
-
+
-
+
@@ -253,17 +253,17 @@
-
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
+
-
+
-
-
-
-
-
+
-
-
+
@@ -676,8 +676,8 @@
x:Uid="AddNewVariableValue"
Margin="0,16,0,0" />
-
-
+
+
-
-
+
+
diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/Microsoft.WindowsAppSdk.manifest b/src/modules/EnvironmentVariables/EnvironmentVariables/Microsoft.WindowsAppSdk.manifest
new file mode 100644
index 000000000000..045e30ad152b
--- /dev/null
+++ b/src/modules/EnvironmentVariables/EnvironmentVariables/Microsoft.WindowsAppSdk.manifest
@@ -0,0 +1,2 @@
+
+PerMonitorV2
\ No newline at end of file
diff --git a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj
index 6d5ada2cef19..77c2b3bdd73f 100644
--- a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj
+++ b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj
@@ -64,11 +64,6 @@
false
-
-
- https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json
-
-
diff --git a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/Views/MainPage.xaml b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/Views/MainPage.xaml
index 848974dce0ef..72519afe7221 100644
--- a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/Views/MainPage.xaml
+++ b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/Views/MainPage.xaml
@@ -8,17 +8,17 @@
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:interop="using:FileLocksmith.Interop"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:tkControls="using:CommunityToolkit.WinUI.Controls"
- xmlns:tkConverters="using:CommunityToolkit.WinUI.Converters"
+ xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
+ xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
-
-
-
-
+
+
0
-
-
+
+
-
-
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
+
+
+
-
+
@@ -162,9 +162,9 @@
-
-
-
+
+
+
diff --git a/src/modules/FileLocksmith/FileLocksmithUI/Microsoft.WindowsAppSdk.manifest b/src/modules/FileLocksmith/FileLocksmithUI/Microsoft.WindowsAppSdk.manifest
new file mode 100644
index 000000000000..4f4d25cd7e44
--- /dev/null
+++ b/src/modules/FileLocksmith/FileLocksmithUI/Microsoft.WindowsAppSdk.manifest
@@ -0,0 +1,2 @@
+
+true/PMPerMonitorV2, PerMonitor
\ No newline at end of file
diff --git a/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj b/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj
index c4ee03077a1a..3e789295e5f4 100644
--- a/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj
+++ b/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj
@@ -22,6 +22,14 @@
+
+
+ runtime
+
+
+
+ runtime
+
diff --git a/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml b/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml
index 16a6ec5c61ca..700c9da14463 100644
--- a/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml
+++ b/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml
@@ -2,7 +2,6 @@
x:Class="Hosts.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:converters="using:CommunityToolkit.WinUI.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:helpers="using:Hosts.Helpers"
xmlns:i="using:Microsoft.Xaml.Interactivity"
@@ -10,6 +9,7 @@
xmlns:local="using:Hosts.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:Hosts.Models"
+ xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
xmlns:ui="using:CommunityToolkit.WinUI"
x:Name="Page"
Loaded="Page_Loaded"
@@ -21,17 +21,17 @@
-
-
-
-
+
+
-
+true/PMPerMonitorV2, PerMonitor
\ No newline at end of file
diff --git a/src/modules/Hosts/Hosts/Settings/UserSettings.cs b/src/modules/Hosts/Hosts/Settings/UserSettings.cs
index 237dcc1bbba7..5c73dd3a172b 100644
--- a/src/modules/Hosts/Hosts/Settings/UserSettings.cs
+++ b/src/modules/Hosts/Hosts/Settings/UserSettings.cs
@@ -17,7 +17,7 @@ public class UserSettings : IUserSettings
private const string HostsModuleName = "Hosts";
private const int MaxNumberOfRetry = 5;
- private readonly ISettingsUtils _settingsUtils;
+ private readonly SettingsUtils _settingsUtils;
private readonly IFileSystemWatcher _watcher;
private readonly object _loadingSettingsLock = new object();
diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj
index b9a95730b592..840bb868a5de 100644
--- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj
+++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj
@@ -1,7 +1,7 @@
-
-
+
+
true
@@ -144,21 +144,21 @@
-
-
-
+
+
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/MeasureTool/MeasureToolCore/packages.config b/src/modules/MeasureTool/MeasureToolCore/packages.config
index abb0cf1bdb60..627512b77879 100644
--- a/src/modules/MeasureTool/MeasureToolCore/packages.config
+++ b/src/modules/MeasureTool/MeasureToolCore/packages.config
@@ -1,7 +1,7 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/src/modules/MeasureTool/MeasureToolUI/Microsoft.WindowsAppSdk.manifest b/src/modules/MeasureTool/MeasureToolUI/Microsoft.WindowsAppSdk.manifest
new file mode 100644
index 000000000000..5cb144b58d49
--- /dev/null
+++ b/src/modules/MeasureTool/MeasureToolUI/Microsoft.WindowsAppSdk.manifest
@@ -0,0 +1,2 @@
+
+true/PMPerMonitorV2, PerMonitor
\ No newline at end of file
diff --git a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp
index 29ab5419ec50..51b93c37a66b 100644
--- a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp
+++ b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp
@@ -75,6 +75,11 @@ struct SuperSonar
static constexpr int FinalAlphaDenominator = 100;
winrt::DispatcherQueueController m_dispatcherQueueController{ nullptr };
+ // Don't consider movements started past these milliseconds to detect shaking.
+ int m_shakeIntervalMs = FIND_MY_MOUSE_DEFAULT_SHAKE_INTERVAL_MS;
+ // By which factor must travelled distance be than the diagonal of the rectangle containing the movements. (value in percent)
+ int m_shakeFactor = FIND_MY_MOUSE_DEFAULT_SHAKE_FACTOR;
+
private:
// Save the mouse movement that occurred in any direction.
@@ -87,10 +92,6 @@ struct SuperSonar
// Raw Input may give relative or absolute values. Need to take each case into account.
bool m_seenAnAbsoluteMousePosition = false;
POINT m_lastAbsolutePosition = { 0, 0 };
- // Don't consider movements started past these milliseconds to detect shaking.
- static constexpr LONG ShakeIntervalMs = 1000;
- // By which factor must travelled distance be than the diagonal of the rectangle containing the movements.
- static constexpr float ShakeFactor = 4.0f;
static inline byte GetSign(LONG const& num)
{
@@ -398,7 +399,7 @@ void SuperSonar::OnSonarKeyboardInput(RAWINPUT const& input)
template
void SuperSonar::DetectShake()
{
- ULONGLONG shakeStartTick = GetTickCount64() - ShakeIntervalMs;
+ ULONGLONG shakeStartTick = GetTickCount64() - m_shakeIntervalMs;
// Prune the story of movements for those movements that started too long ago.
std::erase_if(m_movementHistory, [shakeStartTick](const PointerRecentMovement& movement) { return movement.tick < shakeStartTick; });
@@ -429,7 +430,7 @@ void SuperSonar::DetectShake()
double rectangleHeight = static_cast(maxY) - minY;
double diagonal = sqrt(rectangleWidth * rectangleWidth + rectangleHeight * rectangleHeight);
- if (diagonal > 0 && distanceTravelled / diagonal > ShakeFactor)
+ if (diagonal > 0 && distanceTravelled / diagonal > (m_shakeFactor/100.f))
{
m_movementHistory.clear();
StartSonar();
@@ -767,6 +768,8 @@ struct CompositionSpotlight : SuperSonar
m_sonarZoomFactor = settings.spotlightInitialZoom;
m_excludedApps = settings.excludedApps;
m_shakeMinimumDistance = settings.shakeMinimumDistance;
+ m_shakeIntervalMs = settings.shakeIntervalMs;
+ m_shakeFactor = settings.shakeFactor;
}
else
{
@@ -794,6 +797,8 @@ struct CompositionSpotlight : SuperSonar
m_sonarZoomFactor = localSettings.spotlightInitialZoom;
m_excludedApps = localSettings.excludedApps;
m_shakeMinimumDistance = localSettings.shakeMinimumDistance;
+ m_shakeIntervalMs = localSettings.shakeIntervalMs;
+ m_shakeFactor = localSettings.shakeFactor;
UpdateMouseSnooping(); // For the shake mouse activation method
// Apply new settings to runtime composition objects.
diff --git a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.h b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.h
index 149e52842bdd..d55b72a34d02 100644
--- a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.h
+++ b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.h
@@ -19,6 +19,8 @@ constexpr int FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS = 500;
constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM = 9;
constexpr FindMyMouseActivationMethod FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD = FindMyMouseActivationMethod::DoubleLeftControlKey;
constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_MINIMUM_DISTANCE = 1000;
+constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_INTERVAL_MS = 1000;
+constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_FACTOR = 400; // 400 percent
struct FindMyMouseSettings
{
@@ -31,6 +33,8 @@ struct FindMyMouseSettings
int animationDurationMs = FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS;
int spotlightInitialZoom = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM;
int shakeMinimumDistance = FIND_MY_MOUSE_DEFAULT_SHAKE_MINIMUM_DISTANCE;
+ int shakeIntervalMs = FIND_MY_MOUSE_DEFAULT_SHAKE_INTERVAL_MS;
+ int shakeFactor = FIND_MY_MOUSE_DEFAULT_SHAKE_FACTOR;
std::vector excludedApps;
};
diff --git a/src/modules/MouseUtils/FindMyMouse/dllmain.cpp b/src/modules/MouseUtils/FindMyMouse/dllmain.cpp
index 2d1aa55accaf..d0102ba5076a 100644
--- a/src/modules/MouseUtils/FindMyMouse/dllmain.cpp
+++ b/src/modules/MouseUtils/FindMyMouse/dllmain.cpp
@@ -23,6 +23,8 @@ namespace
const wchar_t JSON_KEY_SPOTLIGHT_INITIAL_ZOOM[] = L"spotlight_initial_zoom";
const wchar_t JSON_KEY_EXCLUDED_APPS[] = L"excluded_apps";
const wchar_t JSON_KEY_SHAKING_MINIMUM_DISTANCE[] = L"shaking_minimum_distance";
+ const wchar_t JSON_KEY_SHAKING_INTERVAL_MS[] = L"shaking_interval_ms";
+ const wchar_t JSON_KEY_SHAKING_FACTOR[] = L"shaking_factor";
const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"activation_shortcut";
}
@@ -396,6 +398,42 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings)
{
Logger::warn("Failed to initialize Shaking Minimum Distance from settings. Will use default value");
}
+ try
+ {
+ // Parse Shaking Interval Milliseconds
+ auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SHAKING_INTERVAL_MS);
+ int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE));
+ if (value >= 0)
+ {
+ findMyMouseSettings.shakeIntervalMs = value;
+ }
+ else
+ {
+ throw std::runtime_error("Invalid Shaking Interval Milliseconds value");
+ }
+ }
+ catch (...)
+ {
+ Logger::warn("Failed to initialize Shaking Interval Milliseconds from settings. Will use default value");
+ }
+ try
+ {
+ // Parse Shaking Factor
+ auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SHAKING_FACTOR);
+ int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE));
+ if (value >= 0)
+ {
+ findMyMouseSettings.shakeFactor = value;
+ }
+ else
+ {
+ throw std::runtime_error("Invalid Shaking Factor value");
+ }
+ }
+ catch (...)
+ {
+ Logger::warn("Failed to initialize Shaking Factor from settings. Will use default value");
+ }
try
{
diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj b/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj
index 86100f4a8dfa..66b91f144344 100644
--- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj
+++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj
@@ -22,6 +22,10 @@
+
+
+ runtime
+
diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs
index 348c1f149eaa..0020edb552b0 100644
--- a/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs
+++ b/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs
@@ -45,6 +45,10 @@ internal partial class Common
private static string lastDragDropFile;
private static long clipboardCopiedTime;
+ internal static readonly char[] Comma = new char[] { ',' };
+ internal static readonly char[] Star = new char[] { '*' };
+ internal static readonly char[] NullSeparator = new char[] { '\0' };
+
internal static ID LastIDWithClipboardData { get; set; }
internal static string LastDragDropFile
@@ -406,7 +410,7 @@ private static void ConnectAndGetData(object postAction)
try
{
- remoteMachine = postAct.Contains("mspaint,") ? postAct.Split(new char[] { ',' })[1] : Common.LastMachineWithClipboardData;
+ remoteMachine = postAct.Contains("mspaint,") ? postAct.Split(Comma)[1] : Common.LastMachineWithClipboardData;
remoteMachine = remoteMachine.Trim();
@@ -518,7 +522,7 @@ internal static void ReceiveAndProcessClipboardData(string remoteMachine, Socket
fileName = Common.GetStringU(header).Replace("\0", string.Empty);
Common.LogDebug("Header: " + fileName);
- string[] headers = fileName.Split(new char[] { '*' });
+ string[] headers = fileName.Split(Star);
if (headers.Length < 2 || !long.TryParse(headers[0], out long dataSize))
{
@@ -973,7 +977,7 @@ internal static void SetClipboardData(byte[] data)
foreach (string txt in texts)
{
- if (string.IsNullOrEmpty(txt.Trim(new char[] { '\0' })))
+ if (string.IsNullOrEmpty(txt.Trim(NullSeparator)))
{
continue;
}
diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs
index 33f528941947..d557a8223c78 100644
--- a/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs
+++ b/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs
@@ -22,7 +22,9 @@ namespace MouseWithoutBorders
{
internal partial class Common
{
- private static SymmetricAlgorithm symAl;
+#pragma warning disable SYSLIB0021
+ private static AesCryptoServiceProvider symAl;
+#pragma warning restore SYSLIB0021
private static string myKey;
private static uint magicNumber;
private static Random ran = new(); // Used for non encryption related functionality.
diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs
index 54affce04baf..5202eb6cc9a8 100644
--- a/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs
+++ b/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs
@@ -313,7 +313,8 @@ internal static void RunDDHelper(bool cleanUp = false)
HasSwitchedMachineSinceLastCopy = true;
// Common.CreateLowIntegrityProcess("\"" + Path.GetDirectoryName(Application.ExecutablePath) + "\\MouseWithoutBordersHelper.exe\"", string.Empty, 0, false, 0);
- if (Process.GetProcessesByName(HelperProcessName)?.Any() != true)
+ var processes = Process.GetProcessesByName(HelperProcessName);
+ if (processes?.Length == 0)
{
Log("Unable to start helper process.");
Common.ShowToolTip("Error starting Mouse Without Borders Helper, clipboard sharing will not work!", 5000, ToolTipIcon.Error);
@@ -325,7 +326,8 @@ internal static void RunDDHelper(bool cleanUp = false)
}
else
{
- if (Process.GetProcessesByName(HelperProcessName)?.Any() == true)
+ var processes = Process.GetProcessesByName(HelperProcessName);
+ if (processes?.Length > 0)
{
Log("Helper process found running.");
}
@@ -432,7 +434,7 @@ internal static bool GetUserName()
{
if (string.IsNullOrEmpty(Setting.Values.Username) && !Common.RunOnLogonDesktop)
{
- if (Program.User.ToLower(CultureInfo.CurrentCulture).Contains("system"))
+ if (Program.User.Contains("system", StringComparison.CurrentCultureIgnoreCase))
{
_ = Common.ImpersonateLoggedOnUserAndDoSomething(() =>
{
diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Launch.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Launch.cs
index 02dbfd707b02..80ffe9fd8415 100644
--- a/src/modules/MouseWithoutBorders/App/Class/Common.Launch.cs
+++ b/src/modules/MouseWithoutBorders/App/Class/Common.Launch.cs
@@ -84,12 +84,11 @@ internal static bool ImpersonateLoggedOnUserAndDoSomething(Action targetFunc)
}
}
- [SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.String.ToLower", Justification = "Dotnet port with style preservation")]
internal static int CreateProcessInInputDesktopSession(string commandLine, string arg, string desktop, short wShowWindow, bool lowIntegrity = false)
// As user who runs explorer.exe
{
- if (!Program.User.ToLower(CultureInfo.InvariantCulture).Contains("system"))
+ if (!Program.User.Contains("system", StringComparison.InvariantCultureIgnoreCase))
{
ProcessStartInfo s = new(commandLine, arg);
s.WindowStyle = wShowWindow != 0 ? ProcessWindowStyle.Normal : ProcessWindowStyle.Hidden;
diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs
index 2673d95c48ab..1b65270fd0b4 100644
--- a/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs
+++ b/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs
@@ -45,7 +45,7 @@ internal static void StartMouseWithoutBordersService(string desktopToRunMouseWit
{
Process[] ps = Process.GetProcessesByName("MouseWithoutBordersSvc");
- if (ps.Any())
+ if (ps.Length != 0)
{
if (DateTime.UtcNow - lastStartServiceTime < TimeSpan.FromSeconds(5))
{
diff --git a/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs b/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs
index adb0c90bd0ec..8f6224e3f18a 100644
--- a/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs
+++ b/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs
@@ -353,6 +353,7 @@ internal static void MouseClickDotForm(int x, int y)
private static bool ctrlDown;
private static bool altDown;
private static bool shiftDown;
+ internal static readonly string[] Args = new string[] { "CAD" };
private static void ResetModifiersState(HotkeySettings matchingHotkey)
{
@@ -456,7 +457,7 @@ private static void InputProcessKeyEx(int vkCode, int flags, out bool eatKey)
if (ctrlDown && altDown)
{
ctrlDown = altDown = false;
- new ServiceController("MouseWithoutBordersSvc").Start(new string[] { "CAD" });
+ new ServiceController("MouseWithoutBordersSvc").Start(Args);
}
break;
diff --git a/src/modules/MouseWithoutBorders/App/Class/MachinePool.cs b/src/modules/MouseWithoutBorders/App/Class/MachinePool.cs
index 51bbd88921e6..2420e43b3771 100644
--- a/src/modules/MouseWithoutBorders/App/Class/MachinePool.cs
+++ b/src/modules/MouseWithoutBorders/App/Class/MachinePool.cs
@@ -156,7 +156,7 @@ public void Initialize(IEnumerable machineNames)
}
else if (list.Count >= 4)
{
- throw new ArgumentException("machineNames.Length > Common.MAX_MACHINE");
+ throw new ArgumentException($"The number of machines exceeded the maximum allowed limit of {Common.MAX_MACHINE}. Actual count: {list.Count}.");
}
_ = LearnMachine(name);
@@ -178,7 +178,7 @@ public void Initialize(IEnumerable infos)
}
else if (list.Count >= 4)
{
- throw new ArgumentException("infos.Length > Common.MAX_MACHINE");
+ throw new ArgumentException($"The number of machines exceeded the maximum allowed limit of {Common.MAX_MACHINE}. Actual count: {list.Count}.");
}
_ = LearnMachine(inf.Name);
diff --git a/src/modules/MouseWithoutBorders/App/Class/MachinePoolHelpers.cs b/src/modules/MouseWithoutBorders/App/Class/MachinePoolHelpers.cs
index 66175d8f552c..d2162273fb7f 100644
--- a/src/modules/MouseWithoutBorders/App/Class/MachinePoolHelpers.cs
+++ b/src/modules/MouseWithoutBorders/App/Class/MachinePoolHelpers.cs
@@ -8,6 +8,9 @@ namespace MouseWithoutBorders.Class
{
internal static class MachinePoolHelpers
{
+ internal static readonly char[] Comma = new char[] { ',' };
+ internal static readonly char[] Colon = new char[] { ':' };
+
internal static MachineInf[] LoadMachineInfoFromMachinePoolStringSetting(string s)
{
if (s == null)
@@ -15,7 +18,7 @@ internal static MachineInf[] LoadMachineInfoFromMachinePoolStringSetting(string
throw new ArgumentNullException(s);
}
- string[] st = s.Split(new char[] { ',' });
+ string[] st = s.Split(Comma);
if (st.Length < Common.MAX_MACHINE)
{
@@ -25,7 +28,7 @@ internal static MachineInf[] LoadMachineInfoFromMachinePoolStringSetting(string
MachineInf[] rv = new MachineInf[Common.MAX_MACHINE];
for (int i = 0; i < Common.MAX_MACHINE; i++)
{
- string[] mc = st[i].Split(new char[] { ':' });
+ string[] mc = st[i].Split(Colon);
if (mc.Length == 2)
{
rv[i].Name = mc[0];
diff --git a/src/modules/MouseWithoutBorders/App/Class/Setting.cs b/src/modules/MouseWithoutBorders/App/Class/Setting.cs
index 46f647c15180..edb74665cb27 100644
--- a/src/modules/MouseWithoutBorders/App/Class/Setting.cs
+++ b/src/modules/MouseWithoutBorders/App/Class/Setting.cs
@@ -38,7 +38,7 @@ internal class Settings
{
internal bool Changed;
- private readonly ISettingsUtils _settingsUtils;
+ private readonly SettingsUtils _settingsUtils;
private readonly object _loadingSettingsLock = new object();
private readonly IFileSystemWatcher _watcher;
diff --git a/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs b/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs
index 106ba6941ae2..a3ea7e3c0862 100644
--- a/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs
+++ b/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs
@@ -887,14 +887,14 @@ void ClientThread(object obj)
if (!string.IsNullOrEmpty(Setting.Values.Name2IP))
{
- string[] name2ip = Setting.Values.Name2IP.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
+ string[] name2ip = Setting.Values.Name2IP.Split(Separator, StringSplitOptions.RemoveEmptyEntries);
string[] nameNip;
if (name2ip != null)
{
foreach (string st in name2ip)
{
- nameNip = st.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ nameNip = st.Split(BlankSeparator, StringSplitOptions.RemoveEmptyEntries);
if (nameNip != null && nameNip.Length >= 2 && nameNip[0].Trim().Equals(machineName, StringComparison.OrdinalIgnoreCase)
&& IPAddress.TryParse(nameNip[1].Trim(), out IPAddress ip) && !validAddressesSt.Contains("[" + ip.ToString() + "]")
@@ -1063,7 +1063,7 @@ private bool CheckForSameSubNet(List validatedAddresses, string machi
List localIPv4Addresses = GetMyIPv4Addresses().ToList();
- if (!localIPv4Addresses.Any())
+ if (localIPv4Addresses.Count == 0)
{
Common.Log($"No IPv4 resolved from the local machine: {Common.MachineName}");
return true;
@@ -1234,6 +1234,8 @@ private void FlagReopenSocketIfNeeded(Exception e)
}
private long lastRemoteMachineID;
+ internal static readonly string[] Separator = new string[] { "\r\n" };
+ internal static readonly char[] BlankSeparator = new char[] { ' ' };
private void MainTCPRoutine(TcpSk tcp, string machineName, bool isClient)
{
diff --git a/src/modules/MouseWithoutBorders/App/Class/TcpServer.cs b/src/modules/MouseWithoutBorders/App/Class/TcpServer.cs
index 6e5478d71b96..f7ea7ba09437 100644
--- a/src/modules/MouseWithoutBorders/App/Class/TcpServer.cs
+++ b/src/modules/MouseWithoutBorders/App/Class/TcpServer.cs
@@ -104,6 +104,7 @@ internal void Close()
}
private static bool logged;
+ internal static readonly string[] Separator = new[] { " " };
private void LogError(string log)
{
@@ -146,7 +147,7 @@ private void LogError(string log)
try
{
// Assuming the format of netstat's output is fixed.
- pid = int.Parse(portLogLine.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries).Last(), CultureInfo.CurrentCulture);
+ pid = int.Parse(portLogLine.Split(Separator, StringSplitOptions.RemoveEmptyEntries).Last(), CultureInfo.CurrentCulture);
process = Process.GetProcessById(pid);
}
catch (Exception)
diff --git a/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsForm.cs b/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsForm.cs
index f6cf932811f8..a8195a79ad28 100644
--- a/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsForm.cs
+++ b/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsForm.cs
@@ -123,13 +123,14 @@ private void CloseWindowButtonClick(object sender, EventArgs e)
}
private string lastMessage = string.Empty;
+ private static readonly string[] Separator = new string[] { "\r\n" };
internal void ShowTip(ToolTipIcon icon, string msg, int durationInMilliseconds)
{
int x = 0;
string text = msg + $"\r\n {(lastMessage.Equals(msg, StringComparison.OrdinalIgnoreCase) ? string.Empty : $"\r\nPrevious message/error: {lastMessage}")} ";
lastMessage = msg;
- int y = (-text.Split(new string[] { "\r\n" }, StringSplitOptions.None).Length * 15) - 30;
+ int y = (-text.Split(Separator, StringSplitOptions.None).Length * 15) - 30;
toolTipManual.Hide(this);
diff --git a/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs b/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs
index c786498f971b..cea9daafc95a 100644
--- a/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs
+++ b/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs
@@ -742,11 +742,13 @@ private void LoadSettingsToUI()
LoadMachines();
}
+ internal static readonly string[] Separator = new string[] { "\r\n" };
+
internal void ShowTip(ToolTipIcon icon, string text, int duration)
{
int x = 0;
text += "\r\n ";
- int y = (-text.Split(new string[] { "\r\n" }, StringSplitOptions.None).Length * 15) - 30;
+ int y = (-text.Split(Separator, StringSplitOptions.None).Length * 15) - 30;
toolTipManual.Hide(this);
diff --git a/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj b/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj
index c9e3417ab3cd..276c65385530 100644
--- a/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj
+++ b/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj
@@ -58,20 +58,20 @@
-
+
-
+
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
\ No newline at end of file
diff --git a/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj.filters b/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj.filters
index 27d483b17418..e3dbd34dc7ff 100644
--- a/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj.filters
+++ b/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj.filters
@@ -34,13 +34,13 @@
{8d479404-964b-4eb1-8fe8-554be3e68c9b}
-
-
-
+
+
+
\ No newline at end of file
diff --git a/src/modules/MouseWithoutBorders/ModuleInterface/packages.config b/src/modules/MouseWithoutBorders/ModuleInterface/packages.config
index 3d9798ef5e72..6199e2345ccd 100644
--- a/src/modules/MouseWithoutBorders/ModuleInterface/packages.config
+++ b/src/modules/MouseWithoutBorders/ModuleInterface/packages.config
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/src/modules/PowerOCR/PowerOCR/Helpers/ImageMethods.cs b/src/modules/PowerOCR/PowerOCR/Helpers/ImageMethods.cs
index 1e11a83801f7..241e1f7433db 100644
--- a/src/modules/PowerOCR/PowerOCR/Helpers/ImageMethods.cs
+++ b/src/modules/PowerOCR/PowerOCR/Helpers/ImageMethods.cs
@@ -148,6 +148,8 @@ internal static async Task GetClickedWord(Window passedWindow, System.Wi
return resultText.Trim();
}
+ internal static readonly char[] Separator = new char[] { '\n', '\r' };
+
public static async Task ExtractText(Bitmap bmp, Language? preferredLanguage, System.Windows.Point? singlePoint = null)
{
Language? selectedLanguage = preferredLanguage ?? GetOCRLanguage();
@@ -211,7 +213,7 @@ public static async Task ExtractText(Bitmap bmp, Language? preferredLang
if (culture.TextInfo.IsRightToLeft)
{
- string[] textListLines = text.ToString().Split(new char[] { '\n', '\r' });
+ string[] textListLines = text.ToString().Split(Separator);
_ = text.Clear();
foreach (string textLine in textListLines)
diff --git a/src/modules/PowerOCR/PowerOCR/Models/ResultTable.cs b/src/modules/PowerOCR/PowerOCR/Models/ResultTable.cs
index 06d3afa009ed..812fd985f575 100644
--- a/src/modules/PowerOCR/PowerOCR/Models/ResultTable.cs
+++ b/src/modules/PowerOCR/PowerOCR/Models/ResultTable.cs
@@ -239,7 +239,7 @@ private static List CalculateRowAreas(Rectangle rectCanvasSize, int hitGrid
return rowAreas;
}
- private static void CheckIntersectionsWithWordBorders(int hitGridSpacing, ICollection wordBorders, ICollection rowAreas, int i, Rect horizontalLineRect)
+ private static void CheckIntersectionsWithWordBorders(int hitGridSpacing, ICollection wordBorders, List rowAreas, int i, Rect horizontalLineRect)
{
foreach (WordBorder wb in wordBorders)
{
diff --git a/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs b/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs
index d08bc5b02038..a1a5ba6d726a 100644
--- a/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs
+++ b/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs
@@ -16,7 +16,7 @@ namespace PowerOCR.Settings
[Export(typeof(IUserSettings))]
public class UserSettings : IUserSettings
{
- private readonly ISettingsUtils _settingsUtils;
+ private readonly SettingsUtils _settingsUtils;
private const string PowerOcrModuleName = "TextExtractor";
private const string DefaultActivationShortcut = "Win + Shift + O";
private const int MaxNumberOfRetry = 5;
diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.rc b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.base.rc
similarity index 55%
rename from src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.rc
rename to src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.base.rc
index 5fa3c8b90d58..c4e1b7cfc5e9 100644
--- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.rc
+++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.base.rc
@@ -1,27 +1,27 @@
#include
#include "resource.h"
-#include "../../../common/version/version.h"
+#include "../../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
1 VERSIONINFO
-FILEVERSION FILE_VERSION
-PRODUCTVERSION PRODUCT_VERSION
-FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEVERSION FILE_VERSION
+ PRODUCTVERSION PRODUCT_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
-FILEFLAGS VS_FF_DEBUG
+ FILEFLAGS VS_FF_DEBUG
#else
-FILEFLAGS 0x0L
+ FILEFLAGS 0x0L
#endif
-FILEOS VOS_NT_WINDOWS32
-FILETYPE VFT_DLL
-FILESUBTYPE VFT2_UNKNOWN
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
- BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
+ BLOCK "040904b0"
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
@@ -35,6 +35,15 @@ BEGIN
END
BLOCK "VarFileInfo"
BEGIN
- VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
+ VALUE "Translation", 0x409, 1200
END
END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON1 ICON "../../Assets/AlwaysOnTop.ico"
diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp
index 31c774ea91c8..b9ec910c62f7 100644
--- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp
+++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp
@@ -8,6 +8,10 @@
#include
#include
+#include
+#include
+#include
+
#include
#include
@@ -489,6 +493,10 @@ void AlwaysOnTop::HandleWinHookEvent(WinHookEvent* data) noexcept
break;
case EVENT_SYSTEM_FOREGROUND:
{
+ if (!is_process_elevated() && IsProcessOfWindowElevated(data->hwnd))
+ {
+ notifications::WarnIfElevationIsRequired(GET_RESOURCE_STRING(IDS_ALWAYSONTOP), GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED), GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED_LEARN_MORE), GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED_DIALOG_DONT_SHOW_AGAIN));
+ }
RefreshBorders();
}
break;
diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj
index 169a53e24307..39233d7ed593 100644
--- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj
+++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj
@@ -3,6 +3,9 @@
+
+
+
true
@@ -141,9 +144,12 @@
+
+
+
@@ -168,15 +174,25 @@
{6955446d-23f7-4023-9bb3-8657f904af99}
+
+ {1d5be09d-78c0-4fd7-af00-ae7c1af7c525}
+
+
+
+
+
+
+
+
-
+
-
+
@@ -184,6 +200,6 @@
-
+
\ No newline at end of file
diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj.filters b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj.filters
index 4a12888f4af1..156373674f95 100644
--- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj.filters
+++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj.filters
@@ -13,6 +13,9 @@
{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+ {A74B5AAC-E913-410D-8941-D73346CF47AE}
+
@@ -49,8 +52,22 @@
Source Files
+
+
+ Generated Files
+
+
+
+ Resource Files
+
+
+ Header Files
+
+
+ Resource Files
+
@@ -98,10 +115,19 @@
Header Files
+
+ Generated Files
+
+
+ Header Files
+
-
+
+
+
+
Resource Files
-
+
\ No newline at end of file
diff --git a/src/modules/alwaysontop/AlwaysOnTop/Resources.resx b/src/modules/alwaysontop/AlwaysOnTop/Resources.resx
new file mode 100644
index 000000000000..17ce38ca6922
--- /dev/null
+++ b/src/modules/alwaysontop/AlwaysOnTop/Resources.resx
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ AlwaysOnTop
+ AlwaysOnTop is a product name, keep as is.
+
+
+ We've detected an application running with administrator privileges. This will prevent certain interactions with these applications.
+ administrator is context of user account.
+
+
+ Learn more
+
+
+ Don't show again
+
+
\ No newline at end of file
diff --git a/src/modules/alwaysontop/AlwaysOnTop/WindowBorder.cpp b/src/modules/alwaysontop/AlwaysOnTop/WindowBorder.cpp
index da335bb101ac..763a9770f757 100644
--- a/src/modules/alwaysontop/AlwaysOnTop/WindowBorder.cpp
+++ b/src/modules/alwaysontop/AlwaysOnTop/WindowBorder.cpp
@@ -126,6 +126,11 @@ bool WindowBorder::Init(HINSTANCE hinstance)
return false;
}
+ if (!SetLayeredWindowAttributes(m_window, 0, 255, LWA_ALPHA))
+ {
+ return false;
+ }
+
// set position of the border-window behind the tracking window
// helps to prevent border overlapping (happens after turning borders off and on)
SetWindowPos(m_trackingWindow
diff --git a/src/modules/alwaysontop/AlwaysOnTop/packages.config b/src/modules/alwaysontop/AlwaysOnTop/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/modules/alwaysontop/AlwaysOnTop/packages.config
+++ b/src/modules/alwaysontop/AlwaysOnTop/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/modules/alwaysontop/AlwaysOnTop/pch.h b/src/modules/alwaysontop/AlwaysOnTop/pch.h
index 1a3b9fd1fd2a..848c90fea23d 100644
--- a/src/modules/alwaysontop/AlwaysOnTop/pch.h
+++ b/src/modules/alwaysontop/AlwaysOnTop/pch.h
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include
#include
diff --git a/src/modules/alwaysontop/AlwaysOnTop/resource.base.h b/src/modules/alwaysontop/AlwaysOnTop/resource.base.h
new file mode 100644
index 000000000000..9dd2bfb332b9
--- /dev/null
+++ b/src/modules/alwaysontop/AlwaysOnTop/resource.base.h
@@ -0,0 +1,13 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by AlwaysOnTop.rc
+
+//////////////////////////////
+// Non-localizable
+
+#define FILE_DESCRIPTION "PowerToys.AlwaysOnTop"
+#define INTERNAL_NAME "PowerToys.AlwaysOnTop"
+#define ORIGINAL_FILENAME "PowerToys.AlwaysOnTop.exe"
+
+// Non-localizable
+//////////////////////////////
diff --git a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj
index 298bb2c8c69a..ad8e3e138bb2 100644
--- a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj
+++ b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj
@@ -68,7 +68,7 @@
-
+
@@ -77,6 +77,6 @@
-
+
\ No newline at end of file
diff --git a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj.filters b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj.filters
index 5b442a35d885..66fe083d3a16 100644
--- a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj.filters
+++ b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj.filters
@@ -47,4 +47,7 @@
Resource Files
+
+
+
\ No newline at end of file
diff --git a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/packages.config b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/packages.config
+++ b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/modules/alwaysontop/Assets/AlwaysOnTop.ico b/src/modules/alwaysontop/Assets/AlwaysOnTop.ico
new file mode 100644
index 000000000000..fdfc40a14bc7
Binary files /dev/null and b/src/modules/alwaysontop/Assets/AlwaysOnTop.ico differ
diff --git a/src/modules/awake/Awake/Core/Manager.cs b/src/modules/awake/Awake/Core/Manager.cs
index 6016a4551e51..09b3bbc3e831 100644
--- a/src/modules/awake/Awake/Core/Manager.cs
+++ b/src/modules/awake/Awake/Core/Manager.cs
@@ -28,6 +28,9 @@ namespace Awake.Core
///
public class Manager
{
+ private static readonly CompositeFormat AwakeMinutes = System.Text.CompositeFormat.Parse(Properties.Resources.AWAKE_MINUTES);
+ private static readonly CompositeFormat AwakeHours = System.Text.CompositeFormat.Parse(Properties.Resources.AWAKE_HOURS);
+
private static BlockingCollection _stateQueue;
private static CancellationTokenSource _tokenSource;
@@ -276,9 +279,9 @@ public static Dictionary GetDefaultTrayOptions()
{
Dictionary optionsList = new Dictionary
{
- { string.Format(CultureInfo.InvariantCulture, Resources.AWAKE_MINUTES, 30), 1800 },
+ { string.Format(CultureInfo.InvariantCulture, AwakeMinutes, 30), 1800 },
{ Resources.AWAKE_1_HOUR, 3600 },
- { string.Format(CultureInfo.InvariantCulture, Resources.AWAKE_HOURS, 2), 7200 },
+ { string.Format(CultureInfo.InvariantCulture, AwakeHours, 2), 7200 },
};
return optionsList;
}
diff --git a/src/modules/awake/Awake/Program.cs b/src/modules/awake/Awake/Program.cs
index 7475e9872530..2baab45c2998 100644
--- a/src/modules/awake/Awake/Program.cs
+++ b/src/modules/awake/Awake/Program.cs
@@ -47,6 +47,11 @@ internal sealed class Program
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private static ManualResetEvent _exitSignal = new ManualResetEvent(false);
+ internal static readonly string[] AliasesConfigOption = new[] { "--use-pt-config", "-c" };
+ internal static readonly string[] AliasesDisplayOption = new[] { "--display-on", "-d" };
+ internal static readonly string[] AliasesTimeOption = new[] { "--time-limit", "-t" };
+ internal static readonly string[] AliasesPidOption = new[] { "--pid", "-p" };
+ internal static readonly string[] AliasesExpireAtOption = new[] { "--expire-at", "-e" };
private static int Main(string[] args)
{
@@ -86,7 +91,7 @@ private static int Main(string[] args)
Logger.LogInfo("Parsing parameters...");
Option configOption = new(
- aliases: new[] { "--use-pt-config", "-c" },
+ aliases: AliasesConfigOption,
getDefaultValue: () => false,
description: $"Specifies whether {Core.Constants.AppName} will be using the PowerToys configuration file for managing the state.")
{
@@ -95,7 +100,7 @@ private static int Main(string[] args)
};
Option displayOption = new(
- aliases: new[] { "--display-on", "-d" },
+ aliases: AliasesDisplayOption,
getDefaultValue: () => true,
description: "Determines whether the display should be kept awake.")
{
@@ -104,7 +109,7 @@ private static int Main(string[] args)
};
Option timeOption = new(
- aliases: new[] { "--time-limit", "-t" },
+ aliases: AliasesTimeOption,
getDefaultValue: () => 0,
description: "Determines the interval, in seconds, during which the computer is kept awake.")
{
@@ -113,7 +118,7 @@ private static int Main(string[] args)
};
Option pidOption = new(
- aliases: new[] { "--pid", "-p" },
+ aliases: AliasesPidOption,
getDefaultValue: () => 0,
description: $"Bind the execution of {Core.Constants.AppName} to another process. When the process ends, the system will resume managing the current sleep and display state.")
{
@@ -122,7 +127,7 @@ private static int Main(string[] args)
};
Option expireAtOption = new(
- aliases: new[] { "--expire-at", "-e" },
+ aliases: AliasesExpireAtOption,
getDefaultValue: () => string.Empty,
description: $"Determines the end date/time when {Core.Constants.AppName} will back off and let the system manage the current sleep and display state.")
{
diff --git a/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj b/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj
index 491ab5916014..6e4556e7f627 100644
--- a/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj
+++ b/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj
@@ -67,7 +67,7 @@
-
+
@@ -75,6 +75,6 @@
-
+
\ No newline at end of file
diff --git a/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj.filters b/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj.filters
index eeb4aece126f..1109a2f73cf3 100644
--- a/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj.filters
+++ b/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj.filters
@@ -47,4 +47,7 @@
Resource Files
+
+
+
\ No newline at end of file
diff --git a/src/modules/awake/AwakeModuleInterface/packages.config b/src/modules/awake/AwakeModuleInterface/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/modules/awake/AwakeModuleInterface/packages.config
+++ b/src/modules/awake/AwakeModuleInterface/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/modules/cmdNotFound/CmdNotFound/CmdNotFound.csproj b/src/modules/cmdNotFound/CmdNotFound/CmdNotFound.csproj
new file mode 100644
index 000000000000..65e6a0436e4a
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFound/CmdNotFound.csproj
@@ -0,0 +1,69 @@
+
+
+
+
+ net8.0-windows10.0.22621.0
+ 10.0.19041.0
+ 10.0.19041.0
+ win-x64;win-arm64
+ enable
+ Microsoft Corporation
+ PowerToys
+ enable
+ PowerToys CommandNotFound
+ PowerToys.CmdNotFound
+ false
+ true
+ ..\..\..\..\$(Platform)\$(Configuration)
+ false
+ false
+ true
+ true
+
+
+
+
+ win-x64
+
+
+ win-arm64
+
+
+
+ DEBUG;TRACE
+ full
+ prompt
+ 4
+ false
+
+
+
+ TRACE;RELEASE
+ true
+ pdbonly
+ prompt
+ 4
+
+
+
+
+ contentFiles
+ all
+ runtime; compile; build; native; analyzers; buildtransitive
+
+
+ contentFiles
+ all
+
+
+ PreserveNewest
+ PreserveNewest
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/cmdNotFound/CmdNotFound/Init.cs b/src/modules/cmdNotFound/CmdNotFound/Init.cs
new file mode 100644
index 000000000000..4d4ecb6d60c8
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFound/Init.cs
@@ -0,0 +1,57 @@
+// 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.Management.Automation;
+using System.Management.Automation.Subsystem;
+using System.Management.Automation.Subsystem.Feedback;
+using System.Management.Automation.Subsystem.Prediction;
+
+namespace WinGetCommandNotFound
+{
+ public sealed class Init : IModuleAssemblyInitializer, IModuleAssemblyCleanup
+ {
+ internal const string Id = "e5351aa4-dfde-4d4d-bf0f-1a2f5a37d8d6";
+
+ public void OnImport()
+ {
+ if (!Platform.IsWindows || !IsWinGetInstalled())
+ {
+ return;
+ }
+
+ SubsystemManager.RegisterSubsystem(SubsystemKind.FeedbackProvider, WinGetCommandNotFoundFeedbackPredictor.Singleton);
+ SubsystemManager.RegisterSubsystem(SubsystemKind.CommandPredictor, WinGetCommandNotFoundFeedbackPredictor.Singleton);
+ }
+
+ public void OnRemove(PSModuleInfo psModuleInfo)
+ {
+ if (!IsWinGetInstalled())
+ {
+ return;
+ }
+
+ SubsystemManager.UnregisterSubsystem(new Guid(Id));
+ SubsystemManager.UnregisterSubsystem(new Guid(Id));
+ }
+
+ private bool IsWinGetInstalled()
+ {
+ // Ensure WinGet is installed
+ using (var pwsh = PowerShell.Create(RunspaceMode.CurrentRunspace))
+ {
+ var results = pwsh.AddCommand("Get-Command")
+ .AddParameter("Name", "winget")
+ .AddParameter("CommandType", "Application")
+ .Invoke();
+
+ if (results.Count is 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/modules/cmdNotFound/CmdNotFound/PooledPowerShellObjectPolicy.cs b/src/modules/cmdNotFound/CmdNotFound/PooledPowerShellObjectPolicy.cs
new file mode 100644
index 000000000000..75d2ba8922c3
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFound/PooledPowerShellObjectPolicy.cs
@@ -0,0 +1,34 @@
+// 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.Management.Automation;
+using System.Management.Automation.Runspaces;
+using Microsoft.Extensions.ObjectPool;
+
+namespace WinGetCommandNotFound
+{
+ public sealed class PooledPowerShellObjectPolicy : IPooledObjectPolicy
+ {
+ private static readonly string[] WingetClientModuleName = new[] { "Microsoft.WinGet.Client" };
+
+ public PowerShell Create()
+ {
+ var iss = InitialSessionState.CreateDefault2();
+ iss.ImportPSModule(WingetClientModuleName);
+ return PowerShell.Create(iss);
+ }
+
+ public bool Return(PowerShell obj)
+ {
+ if (obj != null)
+ {
+ obj.Commands.Clear();
+ obj.Streams.ClearStreams();
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/modules/cmdNotFound/CmdNotFound/Telemetry/CmdNotFoundFeedbackProvidedEvent.cs b/src/modules/cmdNotFound/CmdNotFound/Telemetry/CmdNotFoundFeedbackProvidedEvent.cs
new file mode 100644
index 000000000000..83563bbe3515
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFound/Telemetry/CmdNotFoundFeedbackProvidedEvent.cs
@@ -0,0 +1,16 @@
+// 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.Diagnostics.Tracing;
+using Microsoft.PowerToys.Telemetry;
+using Microsoft.PowerToys.Telemetry.Events;
+
+namespace WinGetCommandNotFound.Telemetry
+{
+ [EventData]
+ public class CmdNotFoundFeedbackProvidedEvent : EventBase, IEvent
+ {
+ public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
+ }
+}
diff --git a/src/modules/cmdNotFound/CmdNotFound/Telemetry/CmdNotFoundSuggestionProvidedEvent.cs b/src/modules/cmdNotFound/CmdNotFound/Telemetry/CmdNotFoundSuggestionProvidedEvent.cs
new file mode 100644
index 000000000000..2f55dbbb3905
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFound/Telemetry/CmdNotFoundSuggestionProvidedEvent.cs
@@ -0,0 +1,16 @@
+// 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.Diagnostics.Tracing;
+using Microsoft.PowerToys.Telemetry;
+using Microsoft.PowerToys.Telemetry.Events;
+
+namespace WinGetCommandNotFound.Telemetry
+{
+ [EventData]
+ public class CmdNotFoundSuggestionProvidedEvent : EventBase, IEvent
+ {
+ public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
+ }
+}
diff --git a/src/modules/cmdNotFound/CmdNotFound/WinGetCommandNotFound.psd1 b/src/modules/cmdNotFound/CmdNotFound/WinGetCommandNotFound.psd1
new file mode 100644
index 000000000000..c7a1118eac19
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFound/WinGetCommandNotFound.psd1
@@ -0,0 +1,11 @@
+@{
+ ModuleVersion = '0.1.0'
+ GUID = '28c9afa2-92e5-413e-8e53-44b2d7a83ac6'
+ Author = 'Carlos Zamora'
+ CompanyName = "Microsoft Corporation"
+ Copyright = "Copyright (c) Microsoft Corporation."
+ Description = 'Enable suggestions on how to install missing commands via winget'
+ PowerShellVersion = '7.4'
+ NestedModules = @('PowerToys.CmdNotFound.dll')
+ RequiredModules = @(@{ModuleName = 'Microsoft.WinGet.Client'; ModuleVersion = "0.2.1"; })
+}
diff --git a/src/modules/cmdNotFound/CmdNotFound/WinGetCommandNotFoundFeedbackPredictor.cs b/src/modules/cmdNotFound/CmdNotFound/WinGetCommandNotFoundFeedbackPredictor.cs
new file mode 100644
index 000000000000..405898ef35c5
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFound/WinGetCommandNotFoundFeedbackPredictor.cs
@@ -0,0 +1,217 @@
+// 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.Collections;
+using System.Collections.ObjectModel;
+using System.Globalization;
+using System.Management.Automation;
+using System.Management.Automation.Subsystem.Feedback;
+using System.Management.Automation.Subsystem.Prediction;
+using ManagedCommon;
+using Microsoft.Extensions.ObjectPool;
+using Microsoft.PowerToys.Telemetry;
+
+namespace WinGetCommandNotFound
+{
+ public sealed class WinGetCommandNotFoundFeedbackPredictor : IFeedbackProvider, ICommandPredictor
+ {
+ private readonly Guid _guid;
+
+ private readonly ObjectPool _pool;
+
+ private const int _maxSuggestions = 20;
+
+ private List? _candidates;
+
+ private bool _warmedUp;
+
+ public static WinGetCommandNotFoundFeedbackPredictor Singleton { get; } = new WinGetCommandNotFoundFeedbackPredictor(Init.Id);
+
+ private WinGetCommandNotFoundFeedbackPredictor(string guid)
+ {
+ Logger.InitializeLogger("\\CmdNotFound\\Logs");
+
+ _guid = new Guid(guid);
+
+ var provider = new DefaultObjectPoolProvider();
+ _pool = provider.Create(new PooledPowerShellObjectPolicy());
+ _pool.Return(_pool.Get());
+ Task.Run(() => WarmUp());
+ }
+
+ public Guid Id => _guid;
+
+ public string Name => "Windows Package Manager - WinGet";
+
+ public string Description => "Finds missing commands that can be installed via WinGet.";
+
+ public Dictionary? FunctionsToDefine => null;
+
+ private void WarmUp()
+ {
+ var ps = _pool.Get();
+ try
+ {
+ ps.AddCommand("Find-WinGetPackage")
+ .AddParameter("Count", 1)
+ .Invoke();
+ }
+ finally
+ {
+ _pool.Return(ps);
+ _warmedUp = true;
+ }
+ }
+
+ ///
+ /// Gets feedback based on the given commandline and error record.
+ ///
+ public FeedbackItem? GetFeedback(FeedbackContext context, CancellationToken token)
+ {
+ var target = (string)context.LastError!.TargetObject;
+ if (target is not null)
+ {
+ try
+ {
+ bool tooManySuggestions = false;
+ string packageMatchFilterField = "command";
+ var pkgList = FindPackages(target, ref tooManySuggestions, ref packageMatchFilterField);
+ if (pkgList.Count == 0)
+ {
+ return null;
+ }
+
+ // Build list of suggestions
+ _candidates = new List();
+ foreach (var pkg in pkgList)
+ {
+ _candidates.Add(string.Format(CultureInfo.InvariantCulture, "winget install --id {0}", pkg.Members["Id"].Value.ToString()));
+ }
+
+ // Build footer message
+ var footerMessage = tooManySuggestions ?
+ string.Format(CultureInfo.InvariantCulture, "Additional results can be found using \"winget search --{0} {1}\"", packageMatchFilterField, target) :
+ null;
+
+ PowerToysTelemetry.Log.WriteEvent(new Telemetry.CmdNotFoundFeedbackProvidedEvent());
+
+ return new FeedbackItem(
+ "Try installing this package using winget:",
+ _candidates,
+ footerMessage,
+ FeedbackDisplayLayout.Portrait);
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError("GetFeedback failed to execute", ex);
+ return new FeedbackItem($"Failed to execute PowerToys Command Not Found.{Environment.NewLine}This is a known issue if PowerShell 7 is installed from the Store or MSIX. If that isn't your case, please report an issue.", new List(), FeedbackDisplayLayout.Portrait);
+ }
+ }
+
+ return null;
+ }
+
+ private Collection FindPackages(string query, ref bool tooManySuggestions, ref string packageMatchFilterField)
+ {
+ if (!_warmedUp)
+ {
+ return new Collection();
+ }
+
+ var ps = _pool.Get();
+ try
+ {
+ var common = new Hashtable()
+ {
+ ["Source"] = "winget",
+ };
+
+ // 1) Search by command
+ var pkgList = ps.AddCommand("Find-WinGetPackage")
+ .AddParameter("Command", query)
+ .AddParameter("MatchOption", "StartsWithCaseInsensitive")
+ .AddParameters(common)
+ .Invoke();
+ if (pkgList.Count > 0)
+ {
+ tooManySuggestions = pkgList.Count > _maxSuggestions;
+ packageMatchFilterField = "command";
+ return pkgList;
+ }
+
+ // 2) No matches found,
+ // search by name
+ ps.Commands.Clear();
+ pkgList = ps.AddCommand("Find-WinGetPackage")
+ .AddParameter("Name", query)
+ .AddParameter("MatchOption", "ContainsCaseInsensitive")
+ .AddParameters(common)
+ .Invoke();
+ if (pkgList.Count > 0)
+ {
+ tooManySuggestions = pkgList.Count > _maxSuggestions;
+ packageMatchFilterField = "name";
+ return pkgList;
+ }
+
+ // 3) No matches found,
+ // search by moniker
+ ps.Commands.Clear();
+ pkgList = ps.AddCommand("Find-WinGetPackage")
+ .AddParameter("Moniker", query)
+ .AddParameter("MatchOption", "ContainsCaseInsensitive")
+ .AddParameters(common)
+ .Invoke();
+ tooManySuggestions = pkgList.Count > _maxSuggestions;
+ packageMatchFilterField = "moniker";
+ return pkgList;
+ }
+ finally
+ {
+ _pool.Return(ps);
+ }
+ }
+
+ public bool CanAcceptFeedback(PredictionClient client, PredictorFeedbackKind feedback)
+ {
+ return feedback switch
+ {
+ PredictorFeedbackKind.CommandLineAccepted => true,
+ _ => false,
+ };
+ }
+
+ public SuggestionPackage GetSuggestion(PredictionClient client, PredictionContext context, CancellationToken cancellationToken)
+ {
+ if (_candidates is not null)
+ {
+ string input = context.InputAst.Extent.Text;
+ List? result = null;
+
+ foreach (string c in _candidates)
+ {
+ if (c.StartsWith(input, StringComparison.OrdinalIgnoreCase))
+ {
+ result ??= new List(_candidates.Count);
+ result.Add(new PredictiveSuggestion(c));
+ }
+ }
+
+ if (result is not null)
+ {
+ PowerToysTelemetry.Log.WriteEvent(new Telemetry.CmdNotFoundSuggestionProvidedEvent());
+ return new SuggestionPackage(result);
+ }
+ }
+
+ return default;
+ }
+
+ public void OnCommandLineAccepted(PredictionClient client, IReadOnlyList history)
+ {
+ // Reset the candidate state.
+ _candidates = null;
+ }
+ }
+}
diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.rc b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.rc
new file mode 100644
index 000000000000..e9ac022fc59a
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.rc
@@ -0,0 +1,108 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include
+#include "resource.h"
+#include "../../../common/version/version.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+1 VERSIONINFO
+FILEVERSION FILE_VERSION
+PRODUCTVERSION PRODUCT_VERSION
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+FILEFLAGS VS_FF_DEBUG
+#else
+FILEFLAGS 0x0L
+#endif
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_DLL
+FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
+ BEGIN
+ VALUE "CompanyName", COMPANY_NAME
+ VALUE "FileDescription", FILE_DESCRIPTION
+ VALUE "FileVersion", FILE_VERSION_STRING
+ VALUE "InternalName", INTERNAL_NAME
+ VALUE "LegalCopyright", COPYRIGHT_NOTE
+ VALUE "OriginalFilename", ORIGINAL_FILENAME
+ VALUE "ProductName", PRODUCT_NAME
+ VALUE "ProductVersion", PRODUCT_VERSION_STRING
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_CMD_NOT_FOUND_NAME "Command Not Found"
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj
new file mode 100644
index 000000000000..a7fd427c2af5
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj
@@ -0,0 +1,107 @@
+
+
+
+ 17.0
+ Win32Proj
+ {0014d652-901f-4456-8d65-06fc5f997fb0}
+ CmdNotFoundModuleInterface
+ PowerToys.CmdNotFoundModuleInterface
+ v143
+ CmdNotFoundModuleInterface
+
+
+
+ DynamicLibrary
+ true
+ Unicode
+
+
+ DynamicLibrary
+ false
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\..\..\$(Platform)\$(Configuration)\
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;CMDNOTFOUNDMODULEINTERFACE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+
+
+ Windows
+ true
+ false
+ Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;CMDNOTFOUNDMODULEINTERFACE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+
+
+ Windows
+ true
+ true
+ true
+ false
+ Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)
+
+
+
+
+ ..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)
+
+
+
+
+
+ Header Files
+
+
+
+
+
+ Source Files
+
+
+
+ Create
+
+
+
+
+ {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}
+
+
+ {6955446d-23f7-4023-9bb3-8657f904af99}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj.filters b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj.filters
new file mode 100644
index 000000000000..1834d3ae8849
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj.filters
@@ -0,0 +1,45 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Resource Files
+
+
+
\ No newline at end of file
diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/dllmain.cpp b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/dllmain.cpp
new file mode 100644
index 000000000000..edb8e6455b91
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/dllmain.cpp
@@ -0,0 +1,158 @@
+// dllmain.cpp : Defines the entry point for the DLL application.
+#include "pch.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "resource.h"
+#include "trace.h"
+
+BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ Trace::RegisterProvider();
+ break;
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ case DLL_PROCESS_DETACH:
+ Trace::UnregisterProvider();
+ break;
+ }
+ return TRUE;
+}
+
+const static wchar_t* MODULE_NAME = L"Command Not Found";
+const static wchar_t* MODULE_DESC = L"A module that detects an error thrown by a command in PowerShell and suggests a relevant WinGet package to install, if available.";
+
+inline const std::wstring ModuleKey = L"CmdNotFound";
+
+class CmdNotFound : public PowertoyModuleIface
+{
+ std::wstring app_name;
+ std::wstring app_key;
+
+private:
+ bool m_enabled = false;
+
+ void install_module()
+ {
+ auto module_path = get_module_folderpath();
+
+ std::string command = "pwsh.exe";
+ command += " ";
+ command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(module_path) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\EnableModule.ps1" + "\"" + " -scriptPath \"" + winrt::to_string(module_path) + "\"";
+
+ int ret = system(command.c_str());
+
+ if (ret != 0)
+ {
+ Logger::error("Running EnableModule.ps1 script failed.");
+ }
+ else
+ {
+ Logger::info("Module installed successfully.");
+ Trace::EnableCmdNotFoundGpo(true);
+ }
+ }
+
+ void uninstall_module()
+ {
+ auto module_path = get_module_folderpath();
+
+ std::string command = "pwsh.exe";
+ command += " ";
+ command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(module_path) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "\"";
+
+ int ret = system(command.c_str());
+
+ if (ret != 0)
+ {
+ Logger::error("Running EnableModule.ps1 script failed.");
+ }
+ else
+ {
+ Logger::info("Module uninstalled successfully.");
+ Trace::EnableCmdNotFoundGpo(false);
+ }
+ }
+
+public:
+ CmdNotFound()
+ {
+ app_name = GET_RESOURCE_STRING(IDS_CMD_NOT_FOUND_NAME);
+ app_key = ModuleKey;
+ LoggerHelpers::init_logger(app_key, L"ModuleInterface", LogSettings::cmdNotFoundLoggerName);
+ Logger::info("CmdNotFound object is constructing");
+
+ powertoys_gpo::gpo_rule_configured_t gpo_rule_configured_value = gpo_policy_enabled_configuration();
+ if (gpo_rule_configured_value == powertoys_gpo::gpo_rule_configured_t::gpo_rule_configured_enabled)
+ {
+ install_module();
+ m_enabled = true;
+ }
+ else if (gpo_rule_configured_value == powertoys_gpo::gpo_rule_configured_t::gpo_rule_configured_disabled)
+ {
+ uninstall_module();
+ m_enabled = false;
+ }
+ }
+
+ virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
+ {
+ return powertoys_gpo::getConfiguredCmdNotFoundEnabledValue();
+ }
+
+ virtual void destroy() override
+ {
+ delete this;
+ }
+
+ virtual const wchar_t* get_name() override
+ {
+ return MODULE_NAME;
+ }
+
+ virtual const wchar_t* get_key() override
+ {
+ return app_key.c_str();
+ }
+
+ virtual bool get_config(wchar_t* /*buffer*/, int* /*buffer_size*/) override
+ {
+ return false;
+ }
+
+ virtual void set_config(const wchar_t* config) override
+ {
+ }
+
+ virtual void enable()
+ {
+ }
+
+ virtual void disable()
+ {
+ }
+
+ virtual bool is_enabled() override
+ {
+ return m_enabled;
+ }
+};
+
+extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
+{
+ return new CmdNotFound();
+}
diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.cpp b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.cpp
new file mode 100644
index 000000000000..64b7eef6d6b9
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.cpp
@@ -0,0 +1,5 @@
+// pch.cpp: source file corresponding to the pre-compiled header
+
+#include "pch.h"
+
+// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.h b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.h
new file mode 100644
index 000000000000..96a774ab2ad3
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.h
@@ -0,0 +1,16 @@
+// pch.h: This is a precompiled header file.
+// Files listed below are compiled only once, improving build performance for future builds.
+// This also affects IntelliSense performance, including code completion and many code browsing features.
+// However, files listed here are ALL re-compiled if any one of them is updated between builds.
+// Do not add files here that you will be updating frequently as this negates the performance advantage.
+
+#ifndef PCH_H
+#define PCH_H
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files
+#include
+
+#include
+
+#endif //PCH_H
diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/resource.h b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/resource.h
new file mode 100644
index 000000000000..9b16533a8a48
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/resource.h
@@ -0,0 +1,21 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Awake.rc
+//
+#define IDS_CMD_NOT_FOUND_NAME 101
+
+
+#define FILE_DESCRIPTION "PowerToys Command Not Found"
+#define INTERNAL_NAME "PowerToys.CmdNotFoundModuleInterface"
+#define ORIGINAL_FILENAME "PowerToys.CmdNotFoundModuleInterface.dll"
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.cpp b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.cpp
new file mode 100644
index 000000000000..255c46ea9944
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.cpp
@@ -0,0 +1,30 @@
+#include "pch.h"
+#include "trace.h"
+
+TRACELOGGING_DEFINE_PROVIDER(
+ g_hProvider,
+ "Microsoft.PowerToys",
+ // {38e8889b-9731-53f5-e901-e8a7c1753074}
+ (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
+ TraceLoggingOptionProjectTelemetry());
+
+void Trace::RegisterProvider()
+{
+ TraceLoggingRegister(g_hProvider);
+}
+
+void Trace::UnregisterProvider()
+{
+ TraceLoggingUnregister(g_hProvider);
+}
+
+// Log if the user has CmdNotFound enabled or disabled
+void Trace::EnableCmdNotFoundGpo(const bool enabled) noexcept
+{
+ TraceLoggingWrite(
+ g_hProvider,
+ "CmdNotFound_EnableCmdNotFound",
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
+ TraceLoggingBoolean(enabled, "Enabled"));
+}
diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.h b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.h
new file mode 100644
index 000000000000..4294c510a64d
--- /dev/null
+++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.h
@@ -0,0 +1,11 @@
+#pragma once
+
+class Trace
+{
+public:
+ static void RegisterProvider();
+ static void UnregisterProvider();
+
+ // Log if the user has CmdNotFound enabled or disabled
+ static void EnableCmdNotFoundGpo(const bool enabled) noexcept;
+};
diff --git a/src/modules/colorPicker/ColorPickerUI/Behaviors/ResizeBehavior.cs b/src/modules/colorPicker/ColorPickerUI/Behaviors/ResizeBehavior.cs
index 5424f44acd08..6d020a752b6a 100644
--- a/src/modules/colorPicker/ColorPickerUI/Behaviors/ResizeBehavior.cs
+++ b/src/modules/colorPicker/ColorPickerUI/Behaviors/ResizeBehavior.cs
@@ -20,7 +20,7 @@ public class ResizeBehavior : Behavior
private static readonly TimeSpan _animationTimeSmaller = _animationTime;
private static readonly IEasingFunction _easeFunctionSmaller = new QuadraticEase() { EasingMode = EasingMode.EaseIn };
- private static void CustomAnimation(DependencyProperty prop, IAnimatable sender, double fromValue, double toValue)
+ private static void CustomAnimation(DependencyProperty prop, FrameworkElement sender, double fromValue, double toValue)
{
// if the animation is to/from a value of 0, it will cancel the current animation
DoubleAnimation move = null;
diff --git a/src/modules/colorPicker/ColorPickerUI/Controls/ColorPickerControl.xaml.cs b/src/modules/colorPicker/ColorPickerUI/Controls/ColorPickerControl.xaml.cs
index 7c5fe0e26e45..b7fbdbc67a8b 100644
--- a/src/modules/colorPicker/ColorPickerUI/Controls/ColorPickerControl.xaml.cs
+++ b/src/modules/colorPicker/ColorPickerUI/Controls/ColorPickerControl.xaml.cs
@@ -329,7 +329,7 @@ private static string ColorToHex(Color color, string oldValue = "")
newHexString = newHexString.ToLowerInvariant();
// Return only with hashtag if user typed it before
- bool addHashtag = oldValue.StartsWith("#", StringComparison.InvariantCulture);
+ bool addHashtag = oldValue.StartsWith('#');
return addHashtag ? "#" + newHexString : newHexString;
}
@@ -348,7 +348,7 @@ private static string FormatHexColorString(string hexCodeText)
else
{
// Hex with or without hashtag and six characters
- return hexCodeText.StartsWith("#", StringComparison.InvariantCulture) ? hexCodeText : "#" + hexCodeText;
+ return hexCodeText.StartsWith('#') ? hexCodeText : "#" + hexCodeText;
}
}
diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/SerializationHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/SerializationHelper.cs
index 9df21d04119b..4b31c423849e 100644
--- a/src/modules/colorPicker/ColorPickerUI/Helpers/SerializationHelper.cs
+++ b/src/modules/colorPicker/ColorPickerUI/Helpers/SerializationHelper.cs
@@ -8,6 +8,7 @@
using System.Globalization;
using System.Linq;
using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Windows.Media;
using ColorPicker.Models;
@@ -21,6 +22,9 @@ internal enum GroupExportedColorsBy
internal static class SerializationHelper
{
+ public static readonly JsonSerializerOptions DefaultOptions = new JsonSerializerOptions { WriteIndented = false };
+ public static readonly JsonSerializerOptions IndentedOptions = new JsonSerializerOptions { WriteIndented = true };
+
public static Dictionary> ConvertToDesiredColorFormats(
IList colorsToExport,
IEnumerable colorRepresentations,
@@ -116,10 +120,7 @@ public static string ToTxt(this Dictionary> s
public static string ToJson(this Dictionary> source, bool indented = true)
{
- var options = new JsonSerializerOptions
- {
- WriteIndented = indented,
- };
+ var options = indented ? IndentedOptions : DefaultOptions;
return JsonSerializer.Serialize(source, options);
}
diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/ZoomWindowHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/ZoomWindowHelper.cs
index a67c8b73595b..0b38ebbc42f4 100644
--- a/src/modules/colorPicker/ColorPickerUI/Helpers/ZoomWindowHelper.cs
+++ b/src/modules/colorPicker/ColorPickerUI/Helpers/ZoomWindowHelper.cs
@@ -91,7 +91,7 @@ private void SetZoomImage(System.Windows.Point point)
ShowZoomWindow(point);
}
- private static BitmapSource BitmapToImageSource(Bitmap bitmap)
+ private static BitmapImage BitmapToImageSource(Bitmap bitmap)
{
using (MemoryStream memory = new MemoryStream())
{
diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs
index 4ab33f09a148..477465e84f65 100644
--- a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs
+++ b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs
@@ -22,7 +22,7 @@ namespace ColorPicker.Settings
[Export(typeof(IUserSettings))]
public class UserSettings : IUserSettings
{
- private readonly ISettingsUtils _settingsUtils;
+ private readonly SettingsUtils _settingsUtils;
private const string ColorPickerModuleName = "ColorPicker";
private const string ColorPickerHistoryFilename = "colorHistory.json";
private const string DefaultActivationShortcut = "Ctrl + Break";
@@ -36,6 +36,11 @@ public class UserSettings : IUserSettings
private bool _loadingColorsHistory;
+ private static readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions
+ {
+ WriteIndented = true,
+ };
+
[ImportingConstructor]
public UserSettings(Helpers.IThrottledActionInvoker throttledActionInvoker)
{
@@ -58,7 +63,7 @@ private void ColorHistory_CollectionChanged(object sender, System.Collections.Sp
{
if (!_loadingColorsHistory)
{
- _settingsUtils.SaveSettings(JsonSerializer.Serialize(ColorHistory, new JsonSerializerOptions { WriteIndented = true }), ColorPickerModuleName, ColorPickerHistoryFilename);
+ _settingsUtils.SaveSettings(JsonSerializer.Serialize(ColorHistory, _serializerOptions), ColorPickerModuleName, ColorPickerHistoryFilename);
}
}
diff --git a/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj b/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj
index 009be3601428..8219d398c6e5 100644
--- a/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj
+++ b/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj
@@ -22,6 +22,14 @@
+
+
+ runtime
+
+
+
+ runtime
+
diff --git a/src/modules/fancyzones/FancyZones/FancyZones.vcxproj b/src/modules/fancyzones/FancyZones/FancyZones.vcxproj
index 21cd0a9cb4bc..643aad98cf6b 100644
--- a/src/modules/fancyzones/FancyZones/FancyZones.vcxproj
+++ b/src/modules/fancyzones/FancyZones/FancyZones.vcxproj
@@ -161,7 +161,7 @@
-
+
@@ -169,6 +169,6 @@
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZones/FancyZones.vcxproj.filters b/src/modules/fancyzones/FancyZones/FancyZones.vcxproj.filters
index 3f34fdfd49f5..a099341ce226 100644
--- a/src/modules/fancyzones/FancyZones/FancyZones.vcxproj.filters
+++ b/src/modules/fancyzones/FancyZones/FancyZones.vcxproj.filters
@@ -39,4 +39,7 @@
+
+
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZones/packages.config b/src/modules/fancyzones/FancyZones/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/modules/fancyzones/FancyZones/packages.config
+++ b/src/modules/fancyzones/FancyZones/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj
index faf124b3efdd..d2e5b1ca43db 100644
--- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj
@@ -61,7 +61,6 @@
-
@@ -156,7 +155,7 @@
-
+
@@ -164,6 +163,6 @@
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters
index 220b0cc22f62..c72ebd2892cd 100644
--- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters
@@ -153,9 +153,6 @@
Header Files
-
- Header Files
-
Header Files
@@ -299,4 +296,7 @@
Generated Files
+
+
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesLib/NotificationUtil.h b/src/modules/fancyzones/FancyZonesLib/NotificationUtil.h
deleted file mode 100644
index 7686f409be7c..000000000000
--- a/src/modules/fancyzones/FancyZonesLib/NotificationUtil.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-
-namespace FancyZonesNotifications
-{
- // Non-Localizable strings
- namespace NonLocalizable
- {
- const wchar_t FancyZonesRunAsAdminInfoPage[] = L"https://aka.ms/powertoysDetectedElevatedHelp";
- const wchar_t ToastNotificationButtonUrl[] = L"powertoys://cant_drag_elevated_disable/";
- }
-
- inline void WarnIfElevationIsRequired()
- {
- using namespace notifications;
- using namespace NonLocalizable;
-
- static bool warning_shown = false;
- if (!warning_shown && !is_toast_disabled(CantDragElevatedDontShowAgainRegistryPath, CantDragElevatedDisableIntervalInDays))
- {
- std::vector actions = {
- link_button{ GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_LEARN_MORE), FancyZonesRunAsAdminInfoPage },
- link_button{ GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_DIALOG_DONT_SHOW_AGAIN), ToastNotificationButtonUrl }
- };
- show_toast_with_activations(GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED),
- GET_RESOURCE_STRING(IDS_FANCYZONES),
- {},
- std::move(actions));
- warning_shown = true;
- }
- }
-}
diff --git a/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp b/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp
index edb32685f7dc..0a7251c78cf6 100644
--- a/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp
+++ b/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp
@@ -4,7 +4,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -12,6 +11,7 @@
#include
#include
+#include
WindowMouseSnap::WindowMouseSnap(HWND window, const std::unordered_map>& activeWorkAreas) :
m_window(window),
@@ -34,10 +34,10 @@ std::unique_ptr WindowMouseSnap::Create(HWND window, const std:
return nullptr;
}
- if (!is_process_elevated() && FancyZonesWindowUtils::IsProcessOfWindowElevated(window))
+ if (!is_process_elevated() && IsProcessOfWindowElevated(window))
{
// Notifies user if unable to drag elevated window
- FancyZonesNotifications::WarnIfElevationIsRequired();
+ notifications::WarnIfElevationIsRequired(GET_RESOURCE_STRING(IDS_FANCYZONES), GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED), GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_LEARN_MORE), GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_DIALOG_DONT_SHOW_AGAIN));
return nullptr;
}
diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp
index 77bad34683a7..3de75ce7646a 100644
--- a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp
+++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp
@@ -158,33 +158,6 @@ bool FancyZonesWindowUtils::IsRoot(HWND window) noexcept
return GetAncestor(window, GA_ROOT) == window;
}
-bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window)
-{
- DWORD pid = 0;
- GetWindowThreadProcessId(window, &pid);
- if (!pid)
- {
- return false;
- }
-
- wil::unique_handle hProcess{ OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
- FALSE,
- pid) };
-
- wil::unique_handle token;
-
- if (OpenProcessToken(hProcess.get(), TOKEN_QUERY, &token))
- {
- TOKEN_ELEVATION elevation;
- DWORD size;
- if (GetTokenInformation(token.get(), TokenElevation, &elevation, sizeof(elevation), &size))
- {
- return elevation.TokenIsElevated != 0;
- }
- }
- return false;
-}
-
bool FancyZonesWindowUtils::IsExcluded(HWND window)
{
std::wstring processPath = get_process_path_waiting_uwp(window);
diff --git a/src/modules/fancyzones/FancyZonesLib/packages.config b/src/modules/fancyzones/FancyZonesLib/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/modules/fancyzones/FancyZonesLib/packages.config
+++ b/src/modules/fancyzones/FancyZonesLib/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesLib/pch.h b/src/modules/fancyzones/FancyZonesLib/pch.h
index db90d123e2ad..70a402b0fa03 100644
--- a/src/modules/fancyzones/FancyZonesLib/pch.h
+++ b/src/modules/fancyzones/FancyZonesLib/pch.h
@@ -13,8 +13,9 @@
#include
#include
#include
-#include
-#include
+#include
+#include
+#include
#include
#include
#include
diff --git a/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj b/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj
index a46103c5b7aa..12a82f47e270 100644
--- a/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj
+++ b/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj
@@ -70,7 +70,7 @@
-
+
@@ -79,6 +79,6 @@
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj.filters b/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj.filters
index e27c0a55384f..b49ea1cd1abc 100644
--- a/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj.filters
+++ b/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj.filters
@@ -41,4 +41,7 @@
Resource Files
+
+
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesModuleInterface/packages.config b/src/modules/fancyzones/FancyZonesModuleInterface/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/modules/fancyzones/FancyZonesModuleInterface/packages.config
+++ b/src/modules/fancyzones/FancyZonesModuleInterface/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj
index c0da418a8f58..160abae1fa1a 100644
--- a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj
+++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj
@@ -86,7 +86,7 @@
-
+
@@ -94,6 +94,6 @@
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj.filters b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj.filters
index 0732c34ad4af..fc286dd63eeb 100644
--- a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj.filters
+++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj.filters
@@ -91,4 +91,7 @@
Resource Files
+
+
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/packages.config b/src/modules/fancyzones/FancyZonesTests/UnitTests/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/modules/fancyzones/FancyZonesTests/UnitTests/packages.config
+++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/UnitTests-FancyZonesEditor/UnitTests-FancyZonesEditor.csproj b/src/modules/fancyzones/UnitTests-FancyZonesEditor/UnitTests-FancyZonesEditor.csproj
index 8d1972207fcc..750c3a8c6e6b 100644
--- a/src/modules/fancyzones/UnitTests-FancyZonesEditor/UnitTests-FancyZonesEditor.csproj
+++ b/src/modules/fancyzones/UnitTests-FancyZonesEditor/UnitTests-FancyZonesEditor.csproj
@@ -20,6 +20,10 @@
+
+
+ runtime
+
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs
index ac9c8ac7c38b..1429bda4f822 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs
@@ -237,7 +237,7 @@ public override void Move(int delta)
private SnappyHelperBase snappyX;
private SnappyHelperBase snappyY;
- private SnappyHelperBase NewMagneticSnapper(bool isX, ResizeMode mode)
+ private SnappyHelperMagnetic NewMagneticSnapper(bool isX, ResizeMode mode)
{
Rect workingArea = App.Overlay.WorkArea;
int screenAxisOrigin = (int)(isX ? workingArea.Left : workingArea.Top);
@@ -245,7 +245,7 @@ private SnappyHelperBase NewMagneticSnapper(bool isX, ResizeMode mode)
return new SnappyHelperMagnetic(Model.Zones, ZoneIndex, isX, mode, screenAxisOrigin, screenAxisSize);
}
- private SnappyHelperBase NewNonMagneticSnapper(bool isX, ResizeMode mode)
+ private SnappyHelperNonMagnetic NewNonMagneticSnapper(bool isX, ResizeMode mode)
{
Rect workingArea = App.Overlay.WorkArea;
int screenAxisOrigin = (int)(isX ? workingArea.Left : workingArea.Top);
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs
index 01e17f70b8ce..5971f2c72277 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Globalization;
+using System.Text;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
@@ -10,6 +11,8 @@ namespace FancyZonesEditor.Controls
{
internal sealed class CustomSliderAutomationPeer : SliderAutomationPeer
{
+ private static readonly CompositeFormat CustomSliderAnnounce = System.Text.CompositeFormat.Parse(Properties.Resources.Custom_slider_announce);
+
private string name = string.Empty;
public CustomSliderAutomationPeer(Slider owner)
@@ -29,7 +32,7 @@ protected override string GetNameCore()
string announce = string.Format(
CultureInfo.CurrentCulture,
- Properties.Resources.Custom_slider_announce,
+ CustomSliderAnnounce,
name,
element.Minimum,
element.Maximum,
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs
index 944222b73da7..383b10181a56 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs
@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.InteropServices;
+using System.Text;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Automation.Peers;
@@ -36,6 +37,10 @@ public partial class MainWindow : Window
private bool haveTriedToGetFocusAlready;
+ private static readonly CompositeFormat EditTemplate = System.Text.CompositeFormat.Parse(Properties.Resources.Edit_Template);
+ private static readonly CompositeFormat PixelValue = System.Text.CompositeFormat.Parse(Properties.Resources.Pixel_Value);
+ private static readonly CompositeFormat TemplateZoneCountValue = System.Text.CompositeFormat.Parse(Properties.Resources.Template_Zone_Count_Value);
+
public int WrapPanelItemSize { get; set; } = DefaultWrapPanelItemSize;
public MainWindow(bool spanZonesAcrossMonitors, Rect workArea)
@@ -335,7 +340,7 @@ private async void EditLayout_Click(object sender, RoutedEventArgs e)
App.Overlay.StartEditing(_settings.SelectedModel);
Keyboard.ClearFocus();
- EditLayoutDialogTitle.Text = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Edit_Template, ((LayoutModel)dataContext).Name);
+ EditLayoutDialogTitle.Text = string.Format(CultureInfo.CurrentCulture, EditTemplate, ((LayoutModel)dataContext).Name);
await EditLayoutDialog.ShowAsync();
}
@@ -567,7 +572,7 @@ private void SensitivityInput_ValueChanged(object sender, RoutedPropertyChangedE
FrameworkElementAutomationPeer.FromElement(SensitivityInput) as SliderAutomationPeer;
string activityId = "sliderValueChanged";
- string value = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Pixel_Value, SensitivityInput.Value);
+ string value = string.Format(CultureInfo.CurrentCulture, PixelValue, SensitivityInput.Value);
if (peer != null && value != null)
{
@@ -588,7 +593,7 @@ private void TemplateZoneCount_ValueChanged(object sender, RoutedPropertyChanged
FrameworkElementAutomationPeer.FromElement(TemplateZoneCount) as SliderAutomationPeer;
string activityId = "templateZoneCountValueChanged";
- string value = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Template_Zone_Count_Value, TemplateZoneCount.Value);
+ string value = string.Format(CultureInfo.CurrentCulture, TemplateZoneCountValue, TemplateZoneCount.Value);
if (peer != null && value != null)
{
@@ -609,7 +614,7 @@ private void Spacing_ValueChanged(object sender, RoutedPropertyChangedEventArgs<
FrameworkElementAutomationPeer.FromElement(Spacing) as SliderAutomationPeer;
string activityId = "spacingValueChanged";
- string value = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Pixel_Value, Spacing.Value);
+ string value = string.Format(CultureInfo.CurrentCulture, PixelValue, Spacing.Value);
if (peer != null && value != null)
{
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs
index 5e64d2fdb1ae..58ee764d59dc 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs
@@ -2,6 +2,7 @@
// 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.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
@@ -231,7 +232,7 @@ public LayoutModel UpdateSelectedLayoutModel()
{
foreach (LayoutModel model in CustomModels)
{
- if (model.Uuid == currentApplied.ZonesetUuid.ToUpperInvariant())
+ if (string.Equals(model.Uuid, currentApplied.ZonesetUuid, StringComparison.OrdinalIgnoreCase))
{
// found match
foundModel = model;
diff --git a/src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj b/src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj
index d32b74bb5077..c8d82684896a 100644
--- a/src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj
+++ b/src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj
@@ -121,7 +121,7 @@ MakeAppx.exe pack /d . /p $(OutDir)ImageResizerContextMenuPackage.msix /nv
-
+
@@ -129,6 +129,6 @@ MakeAppx.exe pack /d . /p $(OutDir)ImageResizerContextMenuPackage.msix /nv
-
+
\ No newline at end of file
diff --git a/src/modules/imageresizer/ImageResizerContextMenu/packages.config b/src/modules/imageresizer/ImageResizerContextMenu/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/modules/imageresizer/ImageResizerContextMenu/packages.config
+++ b/src/modules/imageresizer/ImageResizerContextMenu/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/modules/imageresizer/tests/ImageResizerUITest.csproj b/src/modules/imageresizer/tests/ImageResizerUITest.csproj
index cbcfa6aed745..b0839981e664 100644
--- a/src/modules/imageresizer/tests/ImageResizerUITest.csproj
+++ b/src/modules/imageresizer/tests/ImageResizerUITest.csproj
@@ -56,5 +56,9 @@
+
+
+ runtime
+
diff --git a/src/modules/imageresizer/tests/Properties/SettingsTests.cs b/src/modules/imageresizer/tests/Properties/SettingsTests.cs
index 9c337a42b38f..dcc88aeab64b 100644
--- a/src/modules/imageresizer/tests/Properties/SettingsTests.cs
+++ b/src/modules/imageresizer/tests/Properties/SettingsTests.cs
@@ -6,6 +6,7 @@
using System.ComponentModel;
using System.Globalization;
using System.Linq;
+using System.Text;
using System.Text.Json;
using ImageResizer.Models;
using ImageResizer.Test;
@@ -16,6 +17,13 @@ namespace ImageResizer.Properties
[TestClass]
public class SettingsTests
{
+ private static readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions
+ {
+ WriteIndented = true,
+ };
+
+ private static readonly CompositeFormat ValueMustBeBetween = System.Text.CompositeFormat.Parse(Properties.Resources.ValueMustBeBetween);
+
private static App _imageResizerApp;
public SettingsTests()
@@ -187,7 +195,7 @@ public void IDataErrorInfoItemJpegQualityLevelReturnsErrorWhenOutOfRange(int val
// Using InvariantCulture since this is used internally
Assert.AreEqual(
- string.Format(CultureInfo.InvariantCulture, Resources.ValueMustBeBetween, 1, 100),
+ string.Format(CultureInfo.InvariantCulture, ValueMustBeBetween, 1, 100),
result);
}
@@ -355,9 +363,9 @@ public void SystemTextJsonDeserializesCorrectly()
// Execute readFile/writefile twice and see if serialized string is still correct
var resultWrapper = JsonSerializer.Deserialize(defaultInput);
- var serializedInput = JsonSerializer.Serialize(resultWrapper, new JsonSerializerOptions() { WriteIndented = true });
+ var serializedInput = JsonSerializer.Serialize(resultWrapper, _serializerOptions);
var resultWrapper2 = JsonSerializer.Deserialize(serializedInput);
- var serializedInput2 = JsonSerializer.Serialize(resultWrapper2, new JsonSerializerOptions() { WriteIndented = true });
+ var serializedInput2 = JsonSerializer.Serialize(resultWrapper2, _serializerOptions);
Assert.AreEqual(serializedInput, serializedInput2);
Assert.AreEqual("Image Resizer", resultWrapper2.Name);
diff --git a/src/modules/imageresizer/ui/Models/ResizeSize.cs b/src/modules/imageresizer/ui/Models/ResizeSize.cs
index 92d64b6f4ff5..761f59adf2f8 100644
--- a/src/modules/imageresizer/ui/Models/ResizeSize.cs
+++ b/src/modules/imageresizer/ui/Models/ResizeSize.cs
@@ -12,7 +12,7 @@ namespace ImageResizer.Models
{
public class ResizeSize : Observable
{
- private static readonly IDictionary _tokens = new Dictionary
+ private static readonly Dictionary _tokens = new Dictionary
{
["$small$"] = Resources.Small,
["$medium$"] = Resources.Medium,
diff --git a/src/modules/imageresizer/ui/Properties/Settings.cs b/src/modules/imageresizer/ui/Properties/Settings.cs
index 0a0186c527f7..f2f555ccf015 100644
--- a/src/modules/imageresizer/ui/Properties/Settings.cs
+++ b/src/modules/imageresizer/ui/Properties/Settings.cs
@@ -10,6 +10,7 @@
using System.ComponentModel;
using System.Globalization;
using System.IO.Abstractions;
+using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
@@ -27,6 +28,8 @@ public sealed partial class Settings : IDataErrorInfo, INotifyPropertyChanged
WriteIndented = true,
};
+ private static readonly CompositeFormat ValueMustBeBetween = System.Text.CompositeFormat.Parse(Properties.Resources.ValueMustBeBetween);
+
// Used to synchronize access to the settings.json file
private static Mutex _jsonMutex = new Mutex();
private static string _settingsPath = _fileSystem.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "Microsoft", "PowerToys", "Image Resizer", "settings.json");
@@ -122,7 +125,7 @@ string IDataErrorInfo.this[string columnName]
if (JpegQualityLevel < 1 || JpegQualityLevel > 100)
{
// Using CurrentCulture since this is user facing
- return string.Format(CultureInfo.CurrentCulture, Resources.ValueMustBeBetween, 1, 100);
+ return string.Format(CultureInfo.CurrentCulture, ValueMustBeBetween, 1, 100);
}
return string.Empty;
diff --git a/src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp b/src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp
index 25965ffd9b71..31417e9c235f 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp
+++ b/src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp
@@ -61,9 +61,14 @@ int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/,
}
auto kbm = KeyboardManager();
- kbm.StartLowlevelKeyboardHook();
+ if (kbm.HasRegisteredRemappings())
+ kbm.StartLowlevelKeyboardHook();
- run_message_loop();
+ auto StartHookFunc = [&kbm]() {
+ kbm.StartLowlevelKeyboardHook();
+ };
+
+ run_message_loop({}, {}, { { KeyboardManager::StartHookMessageID, StartHookFunc } });
kbm.StopLowlevelKeyboardHook();
Trace::UnregisterProvider();
diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.cpp b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.cpp
index acfcacfc3b92..bb5b8a13e735 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.cpp
+++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.cpp
@@ -21,8 +21,15 @@ HHOOK KeyboardManager::hookHandleCopy;
HHOOK KeyboardManager::hookHandle;
KeyboardManager* KeyboardManager::keyboardManagerObjectPtr;
+namespace
+{
+ DWORD mainThreadId = {};
+}
+
KeyboardManager::KeyboardManager()
{
+ mainThreadId = GetCurrentThreadId();
+
// Load the initial settings.
LoadSettings();
@@ -38,9 +45,11 @@ KeyboardManager::KeyboardManager()
}
loadingSettings = true;
+ bool loadedSuccessfully = false;
try
{
LoadSettings();
+ loadedSuccessfully = true;
}
catch (...)
{
@@ -48,6 +57,18 @@ KeyboardManager::KeyboardManager()
}
loadingSettings = false;
+
+ if (!loadedSuccessfully)
+ return;
+
+ const bool newHasRemappings = HasRegisteredRemappingsUnchecked();
+ // We didn't have any bindings before and we have now
+ if (newHasRemappings && !hookHandle)
+ PostThreadMessageW(mainThreadId, StartHookMessageID, 0, 0);
+
+ // All bindings were removed
+ if (!newHasRemappings && hookHandle)
+ StopLowlevelKeyboardHook();
};
editorIsRunningEvent = CreateEvent(nullptr, true, false, KeyboardManagerConstants::EditorWindowEventName.c_str());
@@ -121,6 +142,32 @@ void KeyboardManager::StopLowlevelKeyboardHook()
}
}
+bool KeyboardManager::HasRegisteredRemappings() const
+{
+ constexpr int MaxAttempts = 5;
+
+ if (loadingSettings)
+ {
+ for (int currentAttempt = 0; currentAttempt < MaxAttempts; ++currentAttempt)
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ if (!loadingSettings)
+ break;
+ }
+ }
+
+ // Assume that we have registered remappings to be on the safe side if we couldn't check
+ if (loadingSettings)
+ return true;
+
+ return HasRegisteredRemappingsUnchecked();
+}
+
+bool KeyboardManager::HasRegisteredRemappingsUnchecked() const
+{
+ return !(state.appSpecificShortcutReMap.empty() && state.appSpecificShortcutReMapSortedKeys.empty() && state.osLevelShortcutReMap.empty() && state.osLevelShortcutReMapSortedKeys.empty() && state.singleKeyReMap.empty() && state.singleKeyToTextReMap.empty());
+}
+
intptr_t KeyboardManager::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept
{
if (loadingSettings)
diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.h b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.h
index 457a9135b778..846d19a3dbb8 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.h
+++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.h
@@ -7,6 +7,8 @@
class KeyboardManager
{
public:
+ static const inline DWORD StartHookMessageID = WM_APP + 1;
+
// Constructor
KeyboardManager();
@@ -21,7 +23,12 @@ class KeyboardManager
void StartLowlevelKeyboardHook();
void StopLowlevelKeyboardHook();
+ bool HasRegisteredRemappings() const;
+
private:
+ // Returns whether there are any remappings available without waiting for settings to load
+ bool HasRegisteredRemappingsUnchecked() const;
+
// Contains the non localized module name
std::wstring moduleName = KeyboardManagerConstants::ModuleName;
diff --git a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj
index 42a63f5d83d3..9984a9431ec9 100644
--- a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj
+++ b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj
@@ -77,7 +77,7 @@
-
+
@@ -85,6 +85,6 @@
-
+
\ No newline at end of file
diff --git a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters
index c6ceeb2f27e9..81c3649d1e57 100644
--- a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters
+++ b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters
@@ -41,4 +41,7 @@
Generated Files
+
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Microsoft.Launcher/packages.config b/src/modules/launcher/Microsoft.Launcher/packages.config
index e11b462529ec..2c654a3e7df1 100644
--- a/src/modules/launcher/Microsoft.Launcher/packages.config
+++ b/src/modules/launcher/Microsoft.Launcher/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/InputInterpreterTests.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/InputInterpreterTests.cs
index 4f4cfd0e9411..99a1a265b992 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/InputInterpreterTests.cs
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/InputInterpreterTests.cs
@@ -11,6 +11,7 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter.UnitTest
[TestClass]
public class InputInterpreterTests
{
+#pragma warning disable CA1861 // Avoid constant arrays as arguments
[DataTestMethod]
[DataRow(new string[] { "1,5'" }, new string[] { "1,5", "'" })]
[DataRow(new string[] { "1.5'" }, new string[] { "1.5", "'" })]
@@ -18,6 +19,12 @@ public class InputInterpreterTests
[DataRow(new string[] { "1'5\"" }, new string[] { "1", "'", "5", "\"" })]
[DataRow(new string[] { "5\"" }, new string[] { "5", "\"" })]
[DataRow(new string[] { "1'5" }, new string[] { "1", "'", "5" })]
+ [DataRow(new string[] { "-1,5'" }, new string[] { "-1,5", "'" })]
+ [DataRow(new string[] { "-1.5'" }, new string[] { "-1.5", "'" })]
+ [DataRow(new string[] { "-1'" }, new string[] { "-1", "'" })]
+ [DataRow(new string[] { "-1'5\"" }, new string[] { "-1", "'", "5", "\"" })]
+ [DataRow(new string[] { "-5\"" }, new string[] { "-5", "\"" })]
+ [DataRow(new string[] { "-1'5" }, new string[] { "-1", "'", "5" })]
public void RegexSplitsInput(string[] input, string[] expectedResult)
{
string[] shortsplit = InputInterpreter.RegexSplitter(input);
@@ -26,6 +33,7 @@ public void RegexSplitsInput(string[] input, string[] expectedResult)
[DataTestMethod]
[DataRow(new string[] { "1cm", "to", "mm" }, new string[] { "1", "cm", "to", "mm" })]
+ [DataRow(new string[] { "-1cm", "to", "mm" }, new string[] { "-1", "cm", "to", "mm" })]
public void InsertsSpaces(string[] input, string[] expectedResult)
{
InputInterpreter.InputSpaceInserter(ref input);
@@ -37,6 +45,10 @@ public void InsertsSpaces(string[] input, string[] expectedResult)
[DataRow(new string[] { "1\"", "in", "cm" }, new string[] { "1", "inch", "in", "cm" })]
[DataRow(new string[] { "1'6", "in", "cm" }, new string[] { "1.5", "foot", "in", "cm" })]
[DataRow(new string[] { "1'6\"", "in", "cm" }, new string[] { "1.5", "foot", "in", "cm" })]
+ [DataRow(new string[] { "-1'", "in", "cm" }, new string[] { "-1", "foot", "in", "cm" })]
+ [DataRow(new string[] { "-1\"", "in", "cm" }, new string[] { "-1", "inch", "in", "cm" })]
+ [DataRow(new string[] { "-1'6", "in", "cm" }, new string[] { "-1.5", "foot", "in", "cm" })]
+ [DataRow(new string[] { "-1'6\"", "in", "cm" }, new string[] { "-1.5", "foot", "in", "cm" })]
public void HandlesShorthandFeetInchNotation(string[] input, string[] expectedResult)
{
InputInterpreter.ShorthandFeetInchHandler(ref input, CultureInfo.InvariantCulture);
@@ -58,6 +70,7 @@ public void HandlesMetreVsMeterNotation(string[] input, string[] expectedResult)
[DataRow(new string[] { "5", "f", "in", "celsius" }, new string[] { "5", "°f", "in", "DegreeCelsius" })]
[DataRow(new string[] { "5", "c", "in", "f" }, new string[] { "5", "°c", "in", "°f" })]
[DataRow(new string[] { "5", "f", "in", "c" }, new string[] { "5", "°f", "in", "°c" })]
+#pragma warning restore CA1861 // Avoid constant arrays as arguments
public void PrefixesDegrees(string[] input, string[] expectedResult)
{
InputInterpreter.DegreePrefixer(ref input);
@@ -67,6 +80,8 @@ public void PrefixesDegrees(string[] input, string[] expectedResult)
[DataTestMethod]
[DataRow("a f in c")]
[DataRow("12 f in")]
+ [DataRow("1-2 f in c")]
+ [DataRow("12- f in c")]
public void ParseInvalidQueries(string queryString)
{
Query query = new Query(queryString);
@@ -77,6 +92,8 @@ public void ParseInvalidQueries(string queryString)
[DataTestMethod]
[DataRow("12 f in c", 12)]
[DataRow("10m to cm", 10)]
+ [DataRow("-12 f in c", -12)]
+ [DataRow("-10m to cm", -10)]
public void ParseValidQueries(string queryString, double result)
{
Query query = new Query(queryString);
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj
index 9d81ee857e69..99c7afcafc98 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj
@@ -45,6 +45,14 @@
+
+
+ runtime
+
+
+
+ runtime
+
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/InputInterpreter.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/InputInterpreter.cs
index 6e9d62190671..5cdaf9683e30 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/InputInterpreter.cs
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/InputInterpreter.cs
@@ -2,6 +2,7 @@
// 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.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -13,11 +14,11 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter
{
public static class InputInterpreter
{
- private static string pattern = @"(?<=\d)(?![,.])(?=\D)|(?<=\D)(?
@@ -30,7 +31,7 @@ public static void InputSpaceInserter(ref string[] split)
return;
}
- string[] parseInputWithoutSpace = Regex.Split(split[0], pattern);
+ string[] parseInputWithoutSpace = Regex.Split(split[0], Pattern);
if (parseInputWithoutSpace.Length > 1)
{
@@ -79,6 +80,12 @@ public static void ShorthandFeetInchHandler(ref string[] split, CultureInfo cult
// ex: 1'2 and 1'2"
if (shortsplit[1] == "\'")
{
+ bool isNegative = shortsplit[0].StartsWith('-');
+ if (isNegative)
+ {
+ shortsplit[0] = shortsplit[0].Remove(0, 1);
+ }
+
bool isFeet = double.TryParse(shortsplit[0], NumberStyles.AllowDecimalPoint, culture, out double feet);
bool isInches = double.TryParse(shortsplit[2], NumberStyles.AllowDecimalPoint, culture, out double inches);
@@ -88,9 +95,13 @@ public static void ShorthandFeetInchHandler(ref string[] split, CultureInfo cult
break;
}
- string convertedTotalInFeet = Length.FromFeetInches(feet, inches).Feet.ToString(culture);
+ double convertedTotalInFeet = Length.FromFeetInches(feet, inches).Feet;
+ if (isNegative)
+ {
+ convertedTotalInFeet *= -1;
+ }
- string[] newInput = new string[] { convertedTotalInFeet, "foot", split[1], split[2] };
+ string[] newInput = new string[] { convertedTotalInFeet.ToString(culture), "foot", split[1], split[2] };
split = newInput;
}
@@ -157,12 +168,12 @@ public static void DegreePrefixer(ref string[] split)
///
public static void FeetToFt(ref string[] split)
{
- if (split[1].ToLowerInvariant() == "feet")
+ if (string.Equals(split[1], "feet", StringComparison.OrdinalIgnoreCase))
{
split[1] = "ft";
}
- if (split[3].ToLowerInvariant() == "feet")
+ if (string.Equals(split[3], "feet", StringComparison.OrdinalIgnoreCase))
{
split[3] = "ft";
}
@@ -183,7 +194,8 @@ public static void MetreToMeter(ref string[] split)
public static void GallonHandler(ref string[] split, CultureInfo culture)
{
HashSet britishCultureNames = new HashSet() { "en-AI", "en-VG", "en-GB", "en-KY", "en-MS", "en-AG", "en-DM", "en-GD", "en-KN", "en-LC", "en-VC", "en-IE", "en-GY", "en-AE" };
- if (split[1].ToLowerInvariant() == "gal" || split[1].ToLowerInvariant() == "gallon")
+ if (string.Equals(split[1], "gal", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(split[1], "gallon", StringComparison.OrdinalIgnoreCase))
{
if (britishCultureNames.Contains(culture.Name))
{
@@ -195,7 +207,8 @@ public static void GallonHandler(ref string[] split, CultureInfo culture)
}
}
- if (split[3].ToLowerInvariant() == "gal" || split[3].ToLowerInvariant() == "gallon")
+ if (string.Equals(split[3], "gal", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(split[3], "gallon", StringComparison.OrdinalIgnoreCase))
{
if (britishCultureNames.Contains(culture.Name))
{
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs
index ce2f4cfe5ed2..44013e782d4a 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs
@@ -7,6 +7,7 @@
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
+using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Input;
@@ -27,6 +28,8 @@ public class Main : IPlugin, IPluginI18n, IContextMenu, IDisposable
private static string _icon_path;
private bool _disposed;
+ private static readonly CompositeFormat CopyToClipboard = System.Text.CompositeFormat.Parse(Properties.Resources.copy_to_clipboard);
+
public void Init(PluginInitContext context)
{
ArgumentNullException.ThrowIfNull(context);
@@ -61,7 +64,7 @@ private Result GetResult(UnitConversionResult result)
Title = result.ToString(null),
IcoPath = _icon_path,
Score = 300,
- SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.copy_to_clipboard, result.QuantityInfo.Name),
+ SubTitle = string.Format(CultureInfo.CurrentCulture, CopyToClipboard, result.QuantityInfo.Name),
Action = c =>
{
var ret = false;
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs
index 89b6938695c2..a24e6fd90baf 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs
@@ -36,7 +36,8 @@ public static class UnitHandler
private static Enum GetUnitEnum(string unit, QuantityInfo unitInfo)
{
UnitInfo first = Array.Find(unitInfo.UnitInfos, info =>
- unit.ToLowerInvariant() == info.Name.ToLowerInvariant() || unit.ToLowerInvariant() == info.PluralName.ToLowerInvariant());
+ string.Equals(unit, info.Name, StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(unit, info.PluralName, StringComparison.OrdinalIgnoreCase));
if (first != null)
{
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj
index 52a1b4b1e0f4..1f686fab7eea 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj
@@ -41,6 +41,14 @@
+
+
+ runtime
+
+
+
+ runtime
+
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Main.cs
index 7a41b008d4e3..94c4cd8484e8 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Main.cs
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Main.cs
@@ -138,7 +138,7 @@ public List Query(Query query)
});
}
- results = results.Where(a => a.Title.ToLowerInvariant().Contains(query.Search.ToLowerInvariant())).ToList();
+ results = results.Where(a => a.Title.Contains(query.Search, StringComparison.InvariantCultureIgnoreCase)).ToList();
results.ForEach(x =>
{
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachinesApi.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachinesApi.cs
index 56dfd4ae06ce..0fb5e20a9960 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachinesApi.cs
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachinesApi.cs
@@ -14,6 +14,12 @@ namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.RemoteMachinesHelper
{
public class VSCodeRemoteMachinesApi
{
+ private static readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions
+ {
+ AllowTrailingCommas = true,
+ ReadCommentHandling = JsonCommentHandling.Skip,
+ };
+
public VSCodeRemoteMachinesApi()
{
}
@@ -35,7 +41,7 @@ public List Machines
try
{
- JsonElement vscodeSettingsFile = JsonSerializer.Deserialize(fileContent, new JsonSerializerOptions() { AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip });
+ JsonElement vscodeSettingsFile = JsonSerializer.Deserialize(fileContent, _serializerOptions);
if (vscodeSettingsFile.TryGetProperty("remote.SSH.configFile", out var pathElement))
{
var path = pathElement.GetString();
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspacesApi.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspacesApi.cs
index bd318f0896c6..0be408dc4947 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspacesApi.cs
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspacesApi.cs
@@ -46,6 +46,11 @@ private VSCodeWorkspace ParseVSCodeUriAndAuthority(string uri, string authority,
path = path[1..];
}
+ if (!DoesPathExist(path, workspaceEnv.Value, machineName))
+ {
+ return null;
+ }
+
var folderName = Path.GetFileName(path);
// Check we haven't returned '' if we have a path like C:\
@@ -67,6 +72,24 @@ private VSCodeWorkspace ParseVSCodeUriAndAuthority(string uri, string authority,
};
}
+ private bool DoesPathExist(string path, WorkspaceEnvironment workspaceEnv, string machineName)
+ {
+ if (workspaceEnv == WorkspaceEnvironment.Local || workspaceEnv == WorkspaceEnvironment.RemoteWSL)
+ {
+ var resolvedPath = path;
+
+ if (workspaceEnv == WorkspaceEnvironment.RemoteWSL)
+ {
+ resolvedPath = $"\\\\wsl$\\{machineName}{resolvedPath.Replace('/', '\\')}";
+ }
+
+ return Directory.Exists(resolvedPath) || File.Exists(resolvedPath);
+ }
+
+ // If the workspace environment is not Local or WSL, assume the path exists
+ return true;
+ }
+
public List Workspaces
{
get
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs
index afc2d0232dd9..b6794611ee59 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs
@@ -30,12 +30,23 @@ public class InputParserTests
[DataRow("base99 abc", null)]
[DataRow("base64s abc", null)]
[DataRow("base64d abc=", typeof(Base64.Base64DecodeRequest))]
- public void ParserTest(string input, Type? expectedRequestType)
+ [DataRow("url http://googl.de/u oii d", typeof(Uri.UrlEncodeRequest))]
+ [DataRow("urld http://googl.de/u oii d=oo", typeof(Uri.UrlDecodeRequest))]
+ [DataRow("esc:data jjdje332j 3 3l2jl32", typeof(Uri.DataEscapeRequest))]
+ [DataRow("esc:hex d", typeof(Uri.HexEscapeRequest))]
+ [DataRow("esc:hex 4", typeof(Uri.HexEscapeRequest))]
+ [DataRow("esc:hex ", typeof(Uri.HexEscapeRequest), true)]
+ [DataRow("esc:hex z44", typeof(Uri.HexEscapeRequest), true)]
+ [DataRow("uesc:data jjdje332j 3 3l2jl32", typeof(Uri.DataUnescapeRequest))]
+ [DataRow("uesc:hex %21", typeof(Uri.HexUnescapeRequest))]
+ [DataRow("uesc:hex 4", typeof(Uri.HexUnescapeRequest))]
+ [DataRow("uesc:hex ", typeof(Uri.HexUnescapeRequest))]
+ [DataRow("uesc:hex z44", typeof(Uri.HexUnescapeRequest))]
+ public void ParserTest(string input, Type? expectedRequestType, bool expectException = false)
{
var parser = new InputParser();
var query = new Query(input);
- var expectException = false;
string? command = null;
if (query.Terms.Count == 0)
{
@@ -79,15 +90,24 @@ public void ParserTest(string input, Type? expectedRequestType)
private static bool CommandIsKnown(string command)
{
string[] hashes = new string[] { "md5", "sha1", "sha256", "sha384", "sha512", "base64", "base64d" };
-
if (hashes.Contains(command.ToLowerInvariant()))
{
return true;
}
- Regex regex = new Regex("^(guid|uuid)([1345]{0,1}|v[1345]{1})$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+ Regex regexGuiUUID = new Regex("^(guid|uuid)([1345]{0,1}|v[1345]{1})$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+ if (regexGuiUUID.IsMatch(command))
+ {
+ return true;
+ }
+
+ string[] uriCommands = new string[] { "url", "urld", "esc:hex", "uesc:hex", "esc:data", "uesc:data" };
+ if (uriCommands.Contains(command.ToLowerInvariant()))
+ {
+ return true;
+ }
- return regex.IsMatch(command);
+ return false;
}
}
}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj
index fde3e7f4abca..80fb46deea9a 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj
@@ -36,6 +36,17 @@
+
+
+
+ runtime
+
+
+
+ runtime
+
+
+
PreserveNewest
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64DecodeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64DecodeRequest.cs
similarity index 100%
rename from src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64DecodeRequest.cs
rename to src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64DecodeRequest.cs
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64Request.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64Request.cs
similarity index 100%
rename from src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64Request.cs
rename to src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64Request.cs
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDGenerator.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs
similarity index 100%
rename from src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDGenerator.cs
rename to src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDRequest.cs
similarity index 100%
rename from src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDRequest.cs
rename to src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDRequest.cs
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Hashing/HashRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Hashing/HashRequest.cs
similarity index 100%
rename from src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Hashing/HashRequest.cs
rename to src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Hashing/HashRequest.cs
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataEscapeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataEscapeRequest.cs
new file mode 100644
index 000000000000..cfe581a294fa
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataEscapeRequest.cs
@@ -0,0 +1,50 @@
+// 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.Text;
+using Wox.Plugin.Logger;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri
+{
+ public class DataEscapeRequest : IComputeRequest
+ {
+ public byte[] Result { get; set; }
+
+ public string Description => "Data string escaped";
+
+ public bool IsSuccessful { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ private string DataToEscape { get; set; }
+
+ public DataEscapeRequest(string dataToEscape)
+ {
+ DataToEscape = dataToEscape ?? throw new ArgumentNullException(nameof(dataToEscape));
+ }
+
+ public bool Compute()
+ {
+ IsSuccessful = true;
+ try
+ {
+ Result = Encoding.UTF8.GetBytes(System.Uri.EscapeDataString(DataToEscape));
+ }
+ catch (Exception e)
+ {
+ Log.Exception(e.Message, e, GetType());
+ ErrorMessage = e.Message;
+ IsSuccessful = false;
+ }
+
+ return IsSuccessful;
+ }
+
+ public string ResultToString()
+ {
+ return Encoding.UTF8.GetString(Result);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataUnescapeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataUnescapeRequest.cs
new file mode 100644
index 000000000000..2de144d81e27
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataUnescapeRequest.cs
@@ -0,0 +1,58 @@
+// 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.Diagnostics;
+using System.Text;
+using Wox.Plugin.Logger;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri
+{
+ public class DataUnescapeRequest : IComputeRequest
+ {
+ public byte[] Result { get; set; }
+
+ public string Description => "Data string unescaped";
+
+ public bool IsSuccessful { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ private string DataToUnescape { get; set; }
+
+ public DataUnescapeRequest(string dataToUnescape)
+ {
+ DataToUnescape = dataToUnescape ?? throw new ArgumentNullException(nameof(dataToUnescape));
+ }
+
+ public bool Compute()
+ {
+ IsSuccessful = true;
+ try
+ {
+ Result = Encoding.UTF8.GetBytes(System.Uri.UnescapeDataString(DataToUnescape));
+ }
+ catch (Exception e)
+ {
+ Log.Exception(e.Message, e, GetType());
+ ErrorMessage = e.Message;
+ IsSuccessful = false;
+ }
+
+ return IsSuccessful;
+ }
+
+ public string ResultToString()
+ {
+ if (Result != null)
+ {
+ return Encoding.UTF8.GetString(Result);
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexEscapeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexEscapeRequest.cs
new file mode 100644
index 000000000000..7c47af69bc1f
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexEscapeRequest.cs
@@ -0,0 +1,58 @@
+// 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.Linq;
+using System.Text;
+using Wox.Plugin.Logger;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri
+{
+ public class HexEscapeRequest : IComputeRequest
+ {
+ public byte[] Result { get; set; }
+
+ public string Description => "Hex escaped char";
+
+ public bool IsSuccessful { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ private string DataToEscape { get; set; }
+
+ public HexEscapeRequest(string dataToEscape)
+ {
+ DataToEscape = dataToEscape ?? throw new ArgumentNullException(nameof(dataToEscape));
+
+ // Validate that we have only one character
+ if (dataToEscape.Length != 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(dataToEscape));
+ }
+ }
+
+ public bool Compute()
+ {
+ IsSuccessful = true;
+ try
+ {
+ char charToEscape = DataToEscape[0];
+ Result = Encoding.UTF8.GetBytes(System.Uri.HexEscape(charToEscape));
+ }
+ catch (Exception e)
+ {
+ Log.Exception(e.Message, e, GetType());
+ ErrorMessage = e.Message;
+ IsSuccessful = false;
+ }
+
+ return IsSuccessful;
+ }
+
+ public string ResultToString()
+ {
+ return Encoding.UTF8.GetString(Result);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexUnescapeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexUnescapeRequest.cs
new file mode 100644
index 000000000000..8bd5e80ed05d
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexUnescapeRequest.cs
@@ -0,0 +1,62 @@
+// 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.Diagnostics;
+using System.Text;
+using Wox.Plugin.Logger;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri
+{
+ public class HexUnescapeRequest : IComputeRequest
+ {
+ public byte[] Result { get; set; }
+
+ public string Description => "Hex char unescaped";
+
+ public bool IsSuccessful { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ private string DataToUnescape { get; set; }
+
+ public HexUnescapeRequest(string dataToUnescape)
+ {
+ DataToUnescape = dataToUnescape ?? throw new ArgumentNullException(nameof(dataToUnescape));
+ }
+
+ public bool Compute()
+ {
+ IsSuccessful = true;
+ try
+ {
+ int index = 0;
+ if (System.Uri.IsHexEncoding(DataToUnescape, index))
+ {
+ Result = Encoding.UTF8.GetBytes(System.Uri.HexUnescape(DataToUnescape, ref index).ToString());
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Exception(e.Message, e, GetType());
+ ErrorMessage = e.Message;
+ IsSuccessful = false;
+ }
+
+ return IsSuccessful;
+ }
+
+ public string ResultToString()
+ {
+ if (Result != null)
+ {
+ return Encoding.UTF8.GetString(Result);
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlDecodeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlDecodeRequest.cs
new file mode 100644
index 000000000000..db26f7009a70
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlDecodeRequest.cs
@@ -0,0 +1,60 @@
+// 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.Diagnostics;
+using System.Security.Policy;
+using System.Text;
+using System.Web;
+using Wox.Plugin.Logger;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri
+{
+ public class UrlDecodeRequest : IComputeRequest
+ {
+ public byte[] Result { get; set; }
+
+ public string Description => "Decoded URL";
+
+ public bool IsSuccessful { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ private string DataToDecode { get; set; }
+
+ public UrlDecodeRequest(string dataToDecode)
+ {
+ DataToDecode = dataToDecode ?? throw new ArgumentNullException(nameof(dataToDecode));
+ }
+
+ public bool Compute()
+ {
+ IsSuccessful = true;
+ try
+ {
+ Result = Encoding.UTF8.GetBytes(HttpUtility.UrlDecode(DataToDecode));
+ }
+ catch (Exception e)
+ {
+ Log.Exception(e.Message, e, GetType());
+ ErrorMessage = e.Message;
+ IsSuccessful = false;
+ }
+
+ return IsSuccessful;
+ }
+
+ public string ResultToString()
+ {
+ if (Result != null)
+ {
+ return Encoding.UTF8.GetString(Result);
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlEncodeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlEncodeRequest.cs
new file mode 100644
index 000000000000..1022cd702ace
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlEncodeRequest.cs
@@ -0,0 +1,52 @@
+// 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.Security.Policy;
+using System.Text;
+using System.Web;
+using Wox.Plugin.Logger;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri
+{
+ public class UrlEncodeRequest : IComputeRequest
+ {
+ public byte[] Result { get; set; }
+
+ public string Description => "Encoded URL";
+
+ public bool IsSuccessful { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ private string DataToEncode { get; set; }
+
+ public UrlEncodeRequest(string dataToEncode)
+ {
+ DataToEncode = dataToEncode ?? throw new ArgumentNullException(nameof(dataToEncode));
+ }
+
+ public bool Compute()
+ {
+ IsSuccessful = true;
+ try
+ {
+ Result = Encoding.UTF8.GetBytes(HttpUtility.UrlEncode(DataToEncode));
+ }
+ catch (Exception e)
+ {
+ Log.Exception(e.Message, e, GetType());
+ ErrorMessage = e.Message;
+ IsSuccessful = false;
+ }
+
+ return IsSuccessful;
+ }
+
+ public string ResultToString()
+ {
+ return Encoding.UTF8.GetString(Result);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs
index 1581bab2972d..929366f597c8 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs
@@ -8,6 +8,7 @@
using Community.PowerToys.Run.Plugin.ValueGenerator.Base64;
using Community.PowerToys.Run.Plugin.ValueGenerator.GUID;
using Community.PowerToys.Run.Plugin.ValueGenerator.Hashing;
+using Community.PowerToys.Run.Plugin.ValueGenerator.Uri;
using Wox.Plugin;
using Wox.Plugin.Logger;
@@ -26,7 +27,7 @@ public IComputeRequest ParseInput(Query query)
string command = query.Terms[0];
- if (command.ToLower(null) == "md5")
+ if (command.Equals("md5", StringComparison.OrdinalIgnoreCase))
{
int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
@@ -115,18 +116,81 @@ public IComputeRequest ParseInput(Query query)
request = new GUIDRequest(version);
}
}
- else if (command.ToLower(null) == "base64")
+ else if (command.Equals("base64", StringComparison.OrdinalIgnoreCase))
{
int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
request = new Base64Request(Encoding.UTF8.GetBytes(content));
}
- else if (command.ToLower(null) == "base64d")
+ else if (command.Equals("base64d", StringComparison.OrdinalIgnoreCase))
{
int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
request = new Base64DecodeRequest(content);
}
+ else if (command.StartsWith("esc:", StringComparison.OrdinalIgnoreCase))
+ {
+ // Escape things
+ if (command.Equals("esc:data", StringComparison.OrdinalIgnoreCase))
+ {
+ int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
+ string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
+ request = new DataEscapeRequest(content);
+ }
+ else if (command.Equals("esc:hex", StringComparison.OrdinalIgnoreCase))
+ {
+ int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
+ string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
+
+ // This is only for single chars
+ if (content.Length > 1)
+ {
+ throw new ArgumentException($"Invalid Query: {query.RawUserQuery} (To many characters.)");
+ }
+ else if (content.Length == 0)
+ {
+ throw new FormatException($"Invalid Query: {query.RawUserQuery}");
+ }
+
+ request = new HexEscapeRequest(content);
+ }
+ else
+ {
+ throw new FormatException($"Invalid Query: {query.RawUserQuery}");
+ }
+ }
+ else if (command.StartsWith("uesc:", StringComparison.OrdinalIgnoreCase))
+ {
+ // Unescape things
+ if (command.Equals("uesc:data", StringComparison.OrdinalIgnoreCase))
+ {
+ int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
+ string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
+ request = new DataUnescapeRequest(content);
+ }
+ else if (command.Equals("uesc:hex", StringComparison.OrdinalIgnoreCase))
+ {
+ int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
+ string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
+ request = new HexUnescapeRequest(content);
+ }
+ else
+ {
+ throw new FormatException($"Invalid Query: {query.RawUserQuery}");
+ }
+ }
+ else if (command.Equals("url", StringComparison.OrdinalIgnoreCase))
+ {
+ int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
+ string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
+ request = new UrlEncodeRequest(content);
+ }
+ else if (command.Equals("urld", StringComparison.OrdinalIgnoreCase))
+ {
+ int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
+ string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
+ request = new UrlDecodeRequest(content);
+ }
else
{
throw new FormatException($"Invalid Query: {query.RawUserQuery}");
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs
index 36536da3039a..1ed218606356 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs
@@ -93,7 +93,16 @@ public List Query(Query query)
try
{
IComputeRequest computeRequest = _inputParser.ParseInput(query);
- results.Add(GetResult(computeRequest));
+ var result = GetResult(computeRequest);
+
+ if (!string.IsNullOrEmpty(result.Title))
+ {
+ results.Add(result);
+ }
+ else
+ {
+ return results;
+ }
}
catch (ArgumentException e)
{
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj
index f5740c38dd7b..fb6c164ad4f7 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj
@@ -37,6 +37,17 @@
+
+
+
+ runtime
+
+
+
+ runtime
+
+
+
PreserveNewest
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs
index 669f20d63c19..525b4c6ab374 100644
--- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs
@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Text;
using System.Windows.Controls;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
@@ -38,6 +39,10 @@ public class Main : IPlugin, IPluginI18n, IContextMenu, ISettingProvider, IReloa
public static string PluginID => "9F1B49201C3F4BF781CAAD5CD88EA4DC";
+ private static readonly CompositeFormat PluginInBrowserName = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_in_browser_name);
+ private static readonly CompositeFormat PluginOpen = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open);
+ private static readonly CompositeFormat PluginSearchFailed = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_search_failed);
+
public IEnumerable AdditionalOptions => new List()
{
new PluginAdditionalOption()
@@ -66,7 +71,7 @@ public List Query(Query query)
results.Add(new Result
{
Title = Properties.Resources.plugin_description.Remove(Description.Length - 1, 1),
- SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.plugin_in_browser_name, BrowserInfo.Name ?? BrowserInfo.MSEdgeName),
+ SubTitle = string.Format(CultureInfo.CurrentCulture, PluginInBrowserName, BrowserInfo.Name ?? BrowserInfo.MSEdgeName),
QueryTextDisplay = string.Empty,
IcoPath = _iconPath,
ProgramArguments = arguments,
@@ -98,7 +103,7 @@ public List Query(Query query)
var result = new Result
{
Title = searchTerm,
- SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.plugin_open, BrowserInfo.Name ?? BrowserInfo.MSEdgeName),
+ SubTitle = string.Format(CultureInfo.CurrentCulture, PluginOpen, BrowserInfo.Name ?? BrowserInfo.MSEdgeName),
QueryTextDisplay = searchTerm,
IcoPath = _iconPath,
};
@@ -170,7 +175,7 @@ public void Init(PluginInitContext context)
onPluginError = () =>
{
- string errorMsgString = string.Format(CultureInfo.CurrentCulture, Properties.Resources.plugin_search_failed, BrowserInfo.Name ?? BrowserInfo.MSEdgeName);
+ string errorMsgString = string.Format(CultureInfo.CurrentCulture, PluginSearchFailed, BrowserInfo.Name ?? BrowserInfo.MSEdgeName);
Log.Error(errorMsgString, this.GetType());
_context.API.ShowMsg(
diff --git a/src/modules/launcher/Plugins/DynamicPlugin.props b/src/modules/launcher/Plugins/DynamicPlugin.props
new file mode 100644
index 000000000000..746e635e8d1d
--- /dev/null
+++ b/src/modules/launcher/Plugins/DynamicPlugin.props
@@ -0,0 +1,12 @@
+
+
+
+ true
+
+
+
+
+ false
+
+
+
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/DriveOrSharedFolderTests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/DriveOrSharedFolderTests.cs
index e521246768e2..12cce1ea393d 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/DriveOrSharedFolderTests.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/DriveOrSharedFolderTests.cs
@@ -15,6 +15,8 @@ namespace Microsoft.Plugin.Folder.UnitTests
[TestClass]
public class DriveOrSharedFolderTests
{
+ private static readonly string[] DriverNames = new[] { "c:", "d:" };
+
[DataTestMethod]
[DataRow(@"\\test-server\testdir", true)]
[DataRow(@"c:", true)]
@@ -33,7 +35,7 @@ public void IsDriveOrSharedFolder_WhenCalled(string search, bool expectedSuccess
var driveInformationMock = new Mock();
driveInformationMock.Setup(r => r.GetDriveNames())
- .Returns(() => new[] { "c:", "d:" });
+ .Returns(() => DriverNames);
var folderLinksMock = new Mock();
var folderHelper = new FolderHelper(driveInformationMock.Object, folderLinksMock.Object);
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/QueryEnvironmentVariableTests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/QueryEnvironmentVariableTests.cs
index ff93b9ad9889..aa4e0a055367 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/QueryEnvironmentVariableTests.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/QueryEnvironmentVariableTests.cs
@@ -14,7 +14,7 @@ namespace Microsoft.Plugin.Folder.UnitTests
[TestClass]
public class QueryEnvironmentVariableTests
{
- private static IQueryEnvironmentVariable _queryEnvironmentVariable;
+ private static QueryEnvironmentVariable _queryEnvironmentVariable;
private static MockFileSystem _fileSystem;
[TestInitialize]
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Main.cs
index 84d52ebc45f0..9a6968bc117b 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Main.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Main.cs
@@ -37,7 +37,7 @@ public class Main : IPlugin, IPluginI18n, ISavable, IContextMenu, IDisposable
};
private static PluginInitContext _context;
- private IContextMenu _contextMenuLoader;
+ private ContextMenuLoader _contextMenuLoader;
private bool _disposed;
public string Name => Properties.Resources.wox_plugin_folder_plugin_name;
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj
index 384b278b2d9e..9f003281f342 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj
@@ -54,6 +54,14 @@
+
+
+ runtime
+
+
+
+ runtime
+
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/QueryInternalDirectory.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/QueryInternalDirectory.cs
index c4cf6fbb1d09..a827cb7e15b9 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/QueryInternalDirectory.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/QueryInternalDirectory.cs
@@ -75,7 +75,7 @@ public static bool RecursiveSearch(string query)
{
// folder exist, add \ at the end of doesn't exist
// Using Ordinal since this is internal and is used for a symbol
- if (!search.EndsWith(@"\", StringComparison.Ordinal))
+ if (!search.EndsWith('\\'))
{
search += @"\";
}
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/CreateOpenCurrentFolderResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/CreateOpenCurrentFolderResult.cs
index 5bf2a391f320..0a01febcc5c2 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/CreateOpenCurrentFolderResult.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/CreateOpenCurrentFolderResult.cs
@@ -4,6 +4,7 @@
using System.Globalization;
using System.IO;
+using System.Text;
using Wox.Plugin;
namespace Microsoft.Plugin.Folder.Sources.Result
@@ -12,6 +13,8 @@ public class CreateOpenCurrentFolderResult : IItemResult
{
private readonly IShellAction _shellAction;
+ private static readonly CompositeFormat WoxPluginFolderSelectFolderFirstResultTitle = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_folder_select_folder_first_result_title);
+
public string Search { get; set; }
public CreateOpenCurrentFolderResult(string search)
@@ -29,10 +32,10 @@ public Wox.Plugin.Result Create(IPublicAPI contextApi)
{
return new Wox.Plugin.Result
{
- Title = string.Format(CultureInfo.InvariantCulture, Properties.Resources.wox_plugin_folder_select_folder_first_result_title, new DirectoryInfo(Search).Name),
+ Title = string.Format(CultureInfo.InvariantCulture, WoxPluginFolderSelectFolderFirstResultTitle, new DirectoryInfo(Search).Name),
QueryTextDisplay = Search,
SubTitle = Properties.Resources.wox_plugin_folder_select_folder_first_result_subtitle,
- ToolTipData = new ToolTipData(string.Format(CultureInfo.InvariantCulture, Properties.Resources.wox_plugin_folder_select_folder_first_result_title, new DirectoryInfo(Search).Name), Properties.Resources.wox_plugin_folder_select_folder_first_result_subtitle),
+ ToolTipData = new ToolTipData(string.Format(CultureInfo.InvariantCulture, WoxPluginFolderSelectFolderFirstResultTitle, new DirectoryInfo(Search).Name), Properties.Resources.wox_plugin_folder_select_folder_first_result_subtitle),
IcoPath = Search,
Score = 500,
Action = c => _shellAction.ExecuteSanitized(Search, contextApi),
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/EnvironmentVariableResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/EnvironmentVariableResult.cs
index 3295c3e10002..f37e54d4381a 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/EnvironmentVariableResult.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/EnvironmentVariableResult.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Globalization;
+using System.Text;
using Wox.Infrastructure;
using Wox.Plugin;
@@ -10,7 +11,9 @@ namespace Microsoft.Plugin.Folder.Sources.Result
{
public class EnvironmentVariableResult : IItemResult
{
- private readonly IShellAction _shellAction = new ShellAction();
+ private readonly ShellAction _shellAction = new ShellAction();
+
+ private static readonly CompositeFormat WoxPluginFolderSelectFolderResultSubtitle = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_folder_select_folder_result_subtitle);
public string Search { get; set; }
@@ -35,8 +38,8 @@ public Wox.Plugin.Result Create(IPublicAPI contextApi)
IcoPath = Path,
// Using CurrentCulture since this is user facing
- SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Path),
- ToolTipData = new ToolTipData(Title, string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Path)),
+ SubTitle = string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFolderResultSubtitle, Path),
+ ToolTipData = new ToolTipData(Title, string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFolderResultSubtitle, Path)),
QueryTextDisplay = Path,
ContextData = new SearchResult { Type = ResultType.Folder, Path = Path },
Action = c => _shellAction.Execute(Path, contextApi),
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FileItemResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FileItemResult.cs
index bf55c89738f9..4ab22e5c514e 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FileItemResult.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FileItemResult.cs
@@ -4,6 +4,7 @@
using System.Globalization;
using System.IO.Abstractions;
+using System.Text;
using Wox.Infrastructure;
using Wox.Plugin;
@@ -11,7 +12,9 @@ namespace Microsoft.Plugin.Folder.Sources.Result
{
public class FileItemResult : IItemResult
{
- private static readonly IShellAction ShellAction = new ShellAction();
+ private static readonly ShellAction ShellAction = new ShellAction();
+
+ private static readonly CompositeFormat WoxPluginFolderSelectFileResultSubtitle = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_folder_select_file_result_subtitle);
private readonly IPath _path;
@@ -38,8 +41,8 @@ public Wox.Plugin.Result Create(IPublicAPI contextApi)
Title = Title,
// Using CurrentCulture since this is user facing
- SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_file_result_subtitle, FilePath),
- ToolTipData = new ToolTipData(Title, string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_file_result_subtitle, FilePath)),
+ SubTitle = string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFileResultSubtitle, FilePath),
+ ToolTipData = new ToolTipData(Title, string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFileResultSubtitle, FilePath)),
IcoPath = FilePath,
Action = c => ShellAction.Execute(FilePath, contextApi),
ContextData = new SearchResult { Type = ResultType.File, Path = FilePath },
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FolderItemResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FolderItemResult.cs
index 55ccdaf1732d..d8c37e31f1c6 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FolderItemResult.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FolderItemResult.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Globalization;
+using System.Text;
using Wox.Infrastructure;
using Wox.Plugin;
@@ -10,7 +11,9 @@ namespace Microsoft.Plugin.Folder.Sources.Result
{
public class FolderItemResult : IItemResult
{
- private static readonly IShellAction ShellAction = new ShellAction();
+ private static readonly ShellAction ShellAction = new ShellAction();
+
+ private static readonly CompositeFormat WoxPluginFolderSelectFolderResultSubtitle = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_folder_select_folder_result_subtitle);
public FolderItemResult()
{
@@ -39,8 +42,8 @@ public Wox.Plugin.Result Create(IPublicAPI contextApi)
IcoPath = Path,
// Using CurrentCulture since this is user facing
- SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Subtitle),
- ToolTipData = new ToolTipData(Title, string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Subtitle)),
+ SubTitle = string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFolderResultSubtitle, Subtitle),
+ ToolTipData = new ToolTipData(Title, string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFolderResultSubtitle, Subtitle)),
QueryTextDisplay = Path,
ContextData = new SearchResult { Type = ResultType.Folder, Path = Path },
Action = c => ShellAction.Execute(Path, contextApi),
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/TruncatedItemResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/TruncatedItemResult.cs
index e41f363a0ffb..16f1c8bd921e 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/TruncatedItemResult.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/TruncatedItemResult.cs
@@ -3,12 +3,15 @@
// See the LICENSE file in the project root for more information.
using System.Globalization;
+using System.Text;
using Wox.Plugin;
namespace Microsoft.Plugin.Folder.Sources.Result
{
public class TruncatedItemResult : IItemResult
{
+ private static readonly CompositeFormat MicrosoftPluginFolderTruncationWarningSubtitle = System.Text.CompositeFormat.Parse(Properties.Resources.Microsoft_plugin_folder_truncation_warning_subtitle);
+
public int PreTruncationCount { get; set; }
public int PostTruncationCount { get; set; }
@@ -25,8 +28,8 @@ public Wox.Plugin.Result Create(IPublicAPI contextApi)
QueryTextDisplay = Search,
// Using CurrentCulture since this is user facing
- SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Microsoft_plugin_folder_truncation_warning_subtitle, PostTruncationCount, PreTruncationCount),
- ToolTipData = new ToolTipData(Properties.Resources.Microsoft_plugin_folder_truncation_warning_title, string.Format(CultureInfo.CurrentCulture, Properties.Resources.Microsoft_plugin_folder_truncation_warning_subtitle, PostTruncationCount, PreTruncationCount)),
+ SubTitle = string.Format(CultureInfo.CurrentCulture, MicrosoftPluginFolderTruncationWarningSubtitle, PostTruncationCount, PreTruncationCount),
+ ToolTipData = new ToolTipData(Properties.Resources.Microsoft_plugin_folder_truncation_warning_title, string.Format(CultureInfo.CurrentCulture, MicrosoftPluginFolderTruncationWarningSubtitle, PostTruncationCount, PreTruncationCount)),
IcoPath = WarningIconPath,
};
}
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/ShellAction.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/ShellAction.cs
index 364f7faa3483..3e47f3a15537 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/ShellAction.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/ShellAction.cs
@@ -32,7 +32,7 @@ private static string SanitizedPath(string search)
// A network path must start with \\
// Using Ordinal since this is internal and used with a symbol
- if (!sanitizedPath.StartsWith("\\", StringComparison.Ordinal))
+ if (!sanitizedPath.StartsWith('\\'))
{
return sanitizedPath;
}
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderProcessor.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderProcessor.cs
index c8c21d30813e..a3d0d8a0d1e0 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderProcessor.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderProcessor.cs
@@ -24,7 +24,7 @@ public IEnumerable Results(string actionKeyword, string search)
.Select(item => CreateFolderResult(item.Nickname, item.Path, item.Path, search));
}
- private static IItemResult CreateFolderResult(string title, string subtitle, string path, string search)
+ private static UserFolderResult CreateFolderResult(string title, string subtitle, string path, string search)
{
return new UserFolderResult
{
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderResult.cs
index 2cebcad33ed5..58db87f3ceb6 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderResult.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderResult.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Globalization;
+using System.Text;
using Microsoft.Plugin.Folder.Sources;
using Microsoft.Plugin.Folder.Sources.Result;
using Wox.Infrastructure;
@@ -12,7 +13,7 @@ namespace Microsoft.Plugin.Folder
{
public class UserFolderResult : IItemResult
{
- private readonly IShellAction _shellAction = new ShellAction();
+ private readonly ShellAction _shellAction = new ShellAction();
public string Search { get; set; }
@@ -22,6 +23,8 @@ public class UserFolderResult : IItemResult
public string Subtitle { get; set; }
+ private static readonly CompositeFormat WoxPluginFolderSelectFolderResultSubtitle = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_folder_select_folder_result_subtitle);
+
public Result Create(IPublicAPI contextApi)
{
return new Result(StringMatcher.FuzzySearch(Search, Title).MatchData)
@@ -30,7 +33,7 @@ public Result Create(IPublicAPI contextApi)
IcoPath = Path,
// Using CurrentCulture since this is user facing
- SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Subtitle),
+ SubTitle = string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFolderResultSubtitle, Subtitle),
QueryTextDisplay = Path,
ContextData = new SearchResult { Type = ResultType.Folder, Path = Path },
Action = c => _shellAction.Execute(Path, contextApi),
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs
index 38944a836cf4..53e081be3262 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs
@@ -63,7 +63,7 @@ internal class Main : ISettingProvider, IPlugin, ISavable, IPluginI18n, IContext
},
};
- private IContextMenu _contextMenuLoader;
+ private ContextMenuLoader _contextMenuLoader;
private bool disposedValue;
// To save the configurations of plugins
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj
index bf3b3cb1eed2..43e1920f4682 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj
@@ -30,6 +30,26 @@
+
+
+ runtime
+
+
+
+ runtime
+
+
+
+ runtime
+
+
+
+ runtime
+
+
+
+ runtime
+
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/ListRepositoryTests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/ListRepositoryTests.cs
index 12c36c79c867..3cb8cf2f4896 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/ListRepositoryTests.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/ListRepositoryTests.cs
@@ -16,7 +16,7 @@ public void ContainsShouldReturnTrueWhenListIsInitializedWithItem()
{
// Arrange
var itemName = "originalItem1";
- IRepository repository = new ListRepository() { itemName };
+ ListRepository repository = new ListRepository() { itemName };
// Act
var result = repository.Contains(itemName);
@@ -29,7 +29,7 @@ public void ContainsShouldReturnTrueWhenListIsInitializedWithItem()
public void ContainsShouldReturnTrueWhenListIsUpdatedWithAdd()
{
// Arrange
- IRepository repository = new ListRepository();
+ ListRepository repository = new ListRepository();
// Act
var itemName = "newItem";
@@ -45,7 +45,7 @@ public void ContainsShouldReturnFalseWhenListIsUpdatedWithRemove()
{
// Arrange
var itemName = "originalItem1";
- IRepository repository = new ListRepository() { itemName };
+ ListRepository repository = new ListRepository() { itemName };
// Act
repository.Remove(itemName);
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs
index 4cb1b0510fa2..dc4b8e4f7ea8 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs
@@ -25,6 +25,7 @@ public class Win32ProgramRepositoryTest
private List _fileSystemWatchers;
private List> _fileSystemMocks;
+ private static readonly string[] Path = new string[] { "URL=steam://rungameid/1258080", "IconFile=iconFile" };
[TestInitialize]
public void SetFileSystemWatchers()
@@ -219,7 +220,7 @@ public void Win32ProgramRepositoryMustNotCreateUrlAppWhenCreatedEventIsRaised(st
// File.ReadAllLines must be mocked for url applications
var mockFile = new Mock();
- mockFile.Setup(m => m.ReadAllLines(It.IsAny())).Returns(new string[] { "URL=steam://rungameid/1258080", "IconFile=iconFile" });
+ mockFile.Setup(m => m.ReadAllLines(It.IsAny())).Returns(Path);
Win32Program.FileWrapper = mockFile.Object;
// Act
@@ -268,7 +269,7 @@ public void Win32ProgramRepositoryMustCallOnAppDeletedForUrlAppsWhenDeletedEvent
// File.ReadAllLines must be mocked for url applications
var mockFile = new Mock();
- mockFile.Setup(m => m.ReadLines(It.IsAny())).Returns(new string[] { "URL=steam://rungameid/1258080", "IconFile=iconFile" });
+ mockFile.Setup(m => m.ReadLines(It.IsAny())).Returns(Path);
Win32Program.FileWrapper = mockFile.Object;
string fullPath = directory + "\\" + path;
@@ -292,7 +293,7 @@ public void Win32ProgramRepositoryMustCallOnAppRenamedForUrlAppsWhenRenamedEvent
// File.ReadAllLines must be mocked for url applications
var mockFile = new Mock();
- mockFile.Setup(m => m.ReadLines(It.IsAny())).Returns(new string[] { "URL=steam://rungameid/1258080", "IconFile=iconFile" });
+ mockFile.Setup(m => m.ReadLines(It.IsAny())).Returns(Path);
Win32Program.FileWrapper = mockFile.Object;
string oldFullPath = directory + "\\" + oldpath;
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs
index ebd6d0d3dbbc..f17b4a239f3b 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs
@@ -91,7 +91,7 @@ private IEnumerable Query(string program, string programArguments)
.Where(r => r?.Score > 0)
.ToArray();
- if (result.Any())
+ if (result.Length != 0)
{
var maxScore = result.Max(x => x.Score);
return result
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Microsoft.Plugin.Program.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Microsoft.Plugin.Program.csproj
index c334a5876b77..4507376b0610 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Microsoft.Plugin.Program.csproj
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Microsoft.Plugin.Program.csproj
@@ -45,7 +45,18 @@
false
-
+
+
+
+
+ runtime
+
+
+
+ runtime
+
+
+
PreserveNewest
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs
index 51d166e66a2b..3085a53470b5 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs
@@ -308,7 +308,7 @@ internal string ResourceFromPri(string packageFullName, string resourceReference
{
parsed = prefix + key;
}
- else if (key.StartsWith("/", StringComparison.Ordinal))
+ else if (key.StartsWith('/'))
{
parsed = prefix + "//" + key;
}
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs
index 934f518bbfb2..a54b4497f335 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs
@@ -761,7 +761,7 @@ private static IEnumerable CustomProgramPaths(IEnumerable
.ToList() ?? Enumerable.Empty();
// Function to obtain the list of applications, the locations of which have been added to the env variable PATH
- private static IEnumerable PathEnvironmentProgramPaths(IList suffixes)
+ private static List PathEnvironmentProgramPaths(IList suffixes)
{
// To get all the locations stored in the PATH env variable
var pathEnvVariable = Environment.GetEnvironmentVariable("PATH");
@@ -788,7 +788,7 @@ private static List IndexPath(IList suffixes, List index
.SelectMany(indexLocation => ProgramPaths(indexLocation, suffixes))
.ToList();
- private static IEnumerable StartMenuProgramPaths(IList suffixes)
+ private static List StartMenuProgramPaths(IList suffixes)
{
var directory1 = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
var directory2 = Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu);
@@ -797,7 +797,7 @@ private static IEnumerable StartMenuProgramPaths(IList suffixes)
return IndexPath(suffixes, indexLocation);
}
- private static IEnumerable DesktopProgramPaths(IList suffixes)
+ private static List DesktopProgramPaths(IList suffixes)
{
var directory1 = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
var directory2 = Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory);
@@ -807,7 +807,7 @@ private static IEnumerable DesktopProgramPaths(IList suffixes)
return IndexPath(suffixes, indexLocation);
}
- private static IEnumerable RegistryAppProgramPaths(IList suffixes)
+ private static List RegistryAppProgramPaths(IList suffixes)
{
// https://msdn.microsoft.com/library/windows/desktop/ee872121
const string appPaths = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths";
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs
index 584a9a4f7a04..78269398eab7 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs
@@ -11,6 +11,7 @@
using System.IO.Abstractions;
using System.Linq;
using System.Reflection;
+using System.Text;
using System.Windows.Input;
using ManagedCommon;
using Microsoft.Plugin.Shell.Properties;
@@ -33,6 +34,8 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider, IContextMenu, ISavab
private readonly ShellPluginSettings _settings;
private readonly PluginJsonStorage _storage;
+ private static readonly CompositeFormat WoxPluginCmdCmdHasBeenExecutedTimes = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_cmd_cmd_has_been_executed_times);
+
private string IconPath { get; set; }
public string Name => Properties.Resources.wox_plugin_cmd_plugin_name;
@@ -71,6 +74,7 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider, IContextMenu, ISavab
};
private PluginInitContext _context;
+ private static readonly char[] Separator = new[] { ' ' };
public Main()
{
@@ -123,7 +127,7 @@ private List GetHistoryCmds(string cmd, Result result)
if (m.Key == cmd)
{
// Using CurrentCulture since this is user facing
- result.SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_cmd_cmd_has_been_executed_times, m.Value);
+ result.SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, WoxPluginCmdCmdHasBeenExecutedTimes, m.Value);
return null;
}
@@ -132,7 +136,7 @@ private List GetHistoryCmds(string cmd, Result result)
Title = m.Key,
// Using CurrentCulture since this is user facing
- SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_cmd_cmd_has_been_executed_times, m.Value),
+ SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, WoxPluginCmdCmdHasBeenExecutedTimes, m.Value),
IcoPath = IconPath,
Action = c =>
{
@@ -171,7 +175,7 @@ private List ResultsFromlHistory()
Title = m.Key,
// Using CurrentCulture since this is user facing
- SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_cmd_cmd_has_been_executed_times, m.Value),
+ SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, WoxPluginCmdCmdHasBeenExecutedTimes, m.Value),
IcoPath = IconPath,
Action = c =>
{
@@ -285,7 +289,7 @@ private ProcessStartInfo PrepareProcessStartInfo(string command, RunAsType runAs
}
else
{
- var parts = command.Split(new[] { ' ' }, 2);
+ var parts = command.Split(Separator, 2);
if (parts.Length == 2)
{
var filename = parts[0];
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Microsoft.Plugin.Shell.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Microsoft.Plugin.Shell.csproj
index b8218ad90f41..7e16361c9cdf 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Microsoft.Plugin.Shell.csproj
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Microsoft.Plugin.Shell.csproj
@@ -40,6 +40,17 @@
+
+
+
+ runtime
+
+
+
+ runtime
+
+
+
PreserveNewest
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/ShellPluginSettings.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/ShellPluginSettings.cs
index bf506e0efa2d..6edf30817b6f 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/ShellPluginSettings.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/ShellPluginSettings.cs
@@ -25,13 +25,13 @@ public class ShellPluginSettings
public void AddCmdHistory(string cmdName)
{
- if (Count.ContainsKey(cmdName))
+ if (Count.TryGetValue(cmdName, out int currentCount))
{
- Count[cmdName] += 1;
+ Count[cmdName] = currentCount + 1;
}
else
{
- Count.Add(cmdName, 1);
+ Count[cmdName] = 1;
}
}
}
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Microsoft.Plugin.Uri.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Microsoft.Plugin.Uri.csproj
index cff4db6b21b2..dc10119fe005 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Microsoft.Plugin.Uri.csproj
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Microsoft.Plugin.Uri.csproj
@@ -37,6 +37,17 @@
+
+
+
+ runtime
+
+
+
+ runtime
+
+
+
PreserveNewest
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ContextMenuHelper.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ContextMenuHelper.cs
index c2ffebddf3a1..1792793d6e68 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ContextMenuHelper.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ContextMenuHelper.cs
@@ -51,8 +51,8 @@ internal static List GetContextMenuResults(in Result result)
// Hide menu if Explorer.exe is the shell process or the process name is ApplicationFrameHost.exe
// In the first case we would crash the windows ui and in the second case we would kill the generic process for uwp apps.
- if (!windowData.Process.IsShellProcess && !(windowData.Process.IsUwpApp & windowData.Process.Name.ToLower(System.Globalization.CultureInfo.InvariantCulture) == "applicationframehost.exe")
- && !(windowData.Process.IsFullAccessDenied & WindowWalkerSettings.Instance.HideKillProcessOnElevatedProcesses))
+ if (!windowData.Process.IsShellProcess && !(windowData.Process.IsUwpApp && string.Equals(windowData.Process.Name, "ApplicationFrameHost.exe", StringComparison.OrdinalIgnoreCase))
+ && !(windowData.Process.IsFullAccessDenied && WindowWalkerSettings.Instance.HideKillProcessOnElevatedProcesses))
{
contextMenu.Add(new ContextMenuResult
{
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs
index b7b4484dfc9a..05f5f5d8e950 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs
@@ -1,6 +1,7 @@
// 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.Collections.Generic;
using Microsoft.Plugin.WindowWalker.Properties;
using Wox.Infrastructure;
@@ -26,7 +27,7 @@ internal static List GetResultList(List searchControllerRe
foreach (SearchResult x in searchControllerResults)
{
- if (x.Result.Process.Name.ToLower(System.Globalization.CultureInfo.InvariantCulture) == "explorer.exe" && x.Result.Process.IsShellProcess)
+ if (string.Equals(x.Result.Process.Name, "explorer.exe", StringComparison.OrdinalIgnoreCase) && x.Result.Process.IsShellProcess)
{
addExplorerInfo = true;
}
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs
index 8fd3c129b9b6..28ca9e062a64 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs
@@ -378,7 +378,7 @@ private static WindowProcess CreateWindowProcessInstance(IntPtr hWindow)
// Correct the process data if the window belongs to a uwp app hosted by 'ApplicationFrameHost.exe'
// (This only works if the window isn't minimized. For minimized windows the required child window isn't assigned.)
- if (_handlesToProcessCache[hWindow].Name.ToUpperInvariant() == "APPLICATIONFRAMEHOST.EXE")
+ if (string.Equals(_handlesToProcessCache[hWindow].Name, "ApplicationFrameHost.exe", StringComparison.OrdinalIgnoreCase))
{
new Task(() =>
{
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowProcess.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowProcess.cs
index c7d7bd1d1f46..b7bf00e96292 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowProcess.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowProcess.cs
@@ -113,7 +113,7 @@ internal bool IsFullAccessDenied
internal WindowProcess(uint pid, uint tid, string name)
{
UpdateProcessInfo(pid, tid, name);
- _isUwpApp = Name.ToUpperInvariant().Equals("APPLICATIONFRAMEHOST.EXE", StringComparison.Ordinal);
+ _isUwpApp = string.Equals(Name, "ApplicationFrameHost.exe", StringComparison.OrdinalIgnoreCase);
}
///
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Microsoft.Plugin.WindowWalker.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Microsoft.Plugin.WindowWalker.csproj
index cbb35f51c2f1..5077812e5cd2 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Microsoft.Plugin.WindowWalker.csproj
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Microsoft.Plugin.WindowWalker.csproj
@@ -43,6 +43,17 @@
+
+
+
+ runtime
+
+
+
+ runtime
+
+
+
True
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs
index b5bee39a8a02..6073278a364c 100644
--- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs
@@ -245,5 +245,30 @@ public void Interpret_MustReturnExpectedResult_WhenCalled(string input, decimal
Assert.IsNotNull(result);
Assert.AreEqual(expectedResult, result.Result);
}
+
+ private static IEnumerable