Compare commits

..

6 Commits

Author SHA1 Message Date
Shawn Yuan
8755aacd0b allow partially succeeded build
Signed-off-by: Shawn Yuan <shuai.yuan.zju@gmail.com>
2025-04-24 15:58:26 +08:00
Shawn Yuan
e59ea36ccf update
Signed-off-by: Shawn Yuan <shuai.yuan.zju@gmail.com>
2025-04-24 15:17:19 +08:00
Shawn Yuan
2f40aab220 Merge branch 'shawn/testAz' of https://github.com/microsoft/PowerToys into shawn/testAz 2025-04-24 15:13:58 +08:00
Shawn Yuan
33459b718b update
Signed-off-by: Shawn Yuan <shuai.yuan.zju@gmail.com>
2025-04-24 15:13:35 +08:00
Shawn Yuan
7146da92ea Update ci-using-the-latest-winappsdk.yml for Azure Pipelines 2025-04-23 17:39:58 +08:00
PesBandi
bbe248ca90 [ColorPicker]Only close on escape if focused 2025-03-12 07:06:32 +01:00
1391 changed files with 3452 additions and 97995 deletions

View File

@@ -6,16 +6,12 @@ properties:
directives:
description: Enable Developer Mode
allowPrerelease: true
# Requires elevation for the set operation
securityContext: elevated
settings:
Ensure: Present
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: vsPackage
directives:
description: Install Visual Studio 2022 Enterprise (Any edition will work)
# Requires elevation for the set operation
securityContext: elevated
settings:
id: Microsoft.VisualStudio.2022.Enterprise
source: winget
@@ -25,8 +21,6 @@ properties:
directives:
description: Install required VS workloads
allowPrerelease: true
# Requires elevation for the get and set operations
securityContext: elevated
settings:
productId: Microsoft.VisualStudio.Product.Enterprise
channelId: VisualStudio.17.Release

View File

@@ -6,16 +6,12 @@ properties:
directives:
description: Enable Developer Mode
allowPrerelease: true
# Requires elevation for the set operation
securityContext: elevated
settings:
Ensure: Present
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: vsPackage
directives:
description: Install Visual Studio 2022 Professional (Any edition will work)
# Requires elevation for the set operation
securityContext: elevated
settings:
id: Microsoft.VisualStudio.2022.Professional
source: winget
@@ -25,8 +21,6 @@ properties:
directives:
description: Install required VS workloads
allowPrerelease: true
# Requires elevation for the get and set operations
securityContext: elevated
settings:
productId: Microsoft.VisualStudio.Product.Professional
channelId: VisualStudio.17.Release

View File

@@ -6,16 +6,12 @@ properties:
directives:
description: Enable Developer Mode
allowPrerelease: true
# Requires elevation for the set operation
securityContext: elevated
settings:
Ensure: Present
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: vsPackage
directives:
description: Install Visual Studio 2022 Community (Any edition will work)
# Requires elevation for the set operation
securityContext: elevated
settings:
id: Microsoft.VisualStudio.2022.Community
source: winget
@@ -25,8 +21,6 @@ properties:
directives:
description: Install required VS workloads
allowPrerelease: true
# Requires elevation for the get and set operations
securityContext: elevated
settings:
productId: Microsoft.VisualStudio.Product.Community
channelId: VisualStudio.17.Release

View File

@@ -3,13 +3,13 @@
"isRoot": true,
"tools": {
"dotnet-consolidate": {
"version": "4.2.0",
"version": "2.0.0",
"commands": [
"dotnet-consolidate"
]
},
"xamlstyler.console": {
"version": "3.2501.8",
"version": "3.2404.2",
"commands": [
"xstyler"
]

View File

@@ -1,5 +1,6 @@
name: "🕷️ Bug report"
description: Report errors or unexpected behavior
type: Bug
labels:
- Issue-Bug
- Needs-Triage
@@ -7,17 +8,15 @@ body:
- type: markdown
attributes:
value: Please make sure to [search for existing issues](https://github.com/microsoft/PowerToys/issues) before filing a new one!
- id: version
type: input
- type: input
attributes:
label: Microsoft PowerToys version
placeholder: X.XX.X
placeholder: 0.70.0
description: Hover over system tray icon or look at Settings
validations:
required: true
- id: installed
type: dropdown
- type: dropdown
attributes:
label: Installation method
description: How / Where was PowerToys installed from?
@@ -34,6 +33,14 @@ body:
validations:
required: true
- type: dropdown
attributes:
label: Running as admin
description: Are you running PowerToys as Admin?
options:
- "Yes"
- "No"
- type: dropdown
attributes:
label: Area(s) with issue?
@@ -46,7 +53,6 @@ body:
- Awake
- ColorPicker
- Command not found
- Command Palette
- Crop and Lock
- Environment Variables
- FancyZones
@@ -60,7 +66,7 @@ body:
- Keyboard Manager
- Mouse Utilities
- Mouse Without Borders
- New+
- New+
- Peek
- PowerRename
- PowerToys Run
@@ -99,19 +105,6 @@ body:
validations:
required: false
- id: additionalInfo
type: textarea
attributes:
label: Additional Information
placeholder: |
OS version
.Net version
System Language
User or System Installation
Running as admin
validations:
required: false
- type: textarea
attributes:
label: Other Software
@@ -122,4 +115,3 @@ body:
My Cool Application v0.3 (include a code snippet if it would help!)
validations:
required: false

View File

@@ -89,7 +89,6 @@ onefuzzingestionpreparationtool
OTP
Yubi
Yubico
svgl
# KEYS
@@ -233,9 +232,6 @@ SWAPBUTTON
SYSTEMDOCKED
TABLETPC
# Units
nmi
# MATH
artanh
@@ -271,11 +267,6 @@ mengyuanchen
# DllName
testhost
Testably
#Tools
OIP
xef
xes
PACKAGEVERSIONNUMBER
APPXMANIFESTVERSION

View File

@@ -79,7 +79,6 @@ Gershaft
Giordani
Gokce
gordon
Griese
grzhan
Guo
hanselman
@@ -110,7 +109,6 @@ Lambson
Laute
laviusmotileng
Leilei
Loewen
Luecking
Mahalingam
Markovic
@@ -155,7 +153,6 @@ Santossio
Schoen
Sekan
Seraphima
Shmuelie
skttl
somil
Soref
@@ -187,9 +184,6 @@ zhaopy
zhaoqpcn
Zoltan
Zykova
Sameerjs
ruslanlap
vednig
# OTHERS
@@ -202,7 +196,6 @@ cortana
dlnilsson
fancymouse
firefox
fudan
gpt
Inkscape
Markdig
@@ -218,7 +211,6 @@ regedit
roslyn
Spotify
Vanara
wangyi
WEX
windowwalker
winui
@@ -226,9 +218,7 @@ winuiex
wix
wordpad
WWL
wyhash
xamlstyler
Xavalon
Xbox
Youdao
zadjii

View File

@@ -96,11 +96,10 @@
^\Qdoc/devdocs/localization.md\E$
^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$
^\Qsrc/common/notifications/BackgroundActivatorDLL/cpp.hint\E$
^\Qsrc/modules/cmdpal/doc/initial-sdk-spec/list-elements-mock-002.pdn\E$
^\Qsrc/modules/colorPicker/ColorPickerUI/Assets/ColorPicker/colorPicker.cur\E$
^\Qsrc/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso\E$
^\Qsrc/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs\E$
^\Qsrc/modules/MouseUtils/MouseJumpUI/MainForm.resx\E$
^\Qsrc/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmAbout.cs\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmInputCallback.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmLogon.resx\E$

File diff suppressed because it is too large Load Diff

View File

@@ -232,15 +232,6 @@ _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING
# ignore long runs of a single character:
\b([A-Za-z])\g{-1}{3,}\b
# Amazon
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
# imgur
\bimgur\.com/[^.]+
# Process Process (typename varname)
Process Process
# ZoomIt menu items with accelerator keys
E&xit
St&yle
St&yle

View File

@@ -1,26 +0,0 @@
# Dependency Review Action
#
# This Action will scan dependency manifest files that change as part of a Pull Request,
# surfacing known-vulnerable versions of the packages declared or updated in the PR.
# Once installed, if the workflow run is marked as required,
# PRs introducing known-vulnerable packages will be blocked from merging.
#
# As recommended by Microsoft's security guidelines (https://docs.opensource.microsoft.com/security/tsg/actions/#requirements-for-security-hardening-your-own-github-actions),
# 3rd-party actions should be pinned to a specific commit hash to prevent supply chain attacks.
# This update aligns with best practices; 1st/2nd-party actions is not required hash pinning.
#
# Source repository: https://github.com/actions/dependency-review-action
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v4
- name: 'Dependency Review'
uses: actions/dependency-review-action@v4

View File

@@ -39,11 +39,6 @@ jobs:
echo powerToysInstallerX64Url=$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("x64"))][0].browser_download_url') >> $GITHUB_OUTPUT
echo powerToysInstallerArm64Url=$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("arm64"))][0].browser_download_url') >> $GITHUB_OUTPUT
- name: Setup .NET 9.0
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- uses: microsoft/setup-msstore-cli@v1
- name: Fetch Store Credential

View File

@@ -93,7 +93,7 @@ jobs:
steps:
- name: check-spelling
id: spelling
uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24
uses: check-spelling/check-spelling@v0.0.24
with:
config: .github/actions/spell-check
suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }}
@@ -156,7 +156,7 @@ jobs:
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
steps:
- name: comment
uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24
uses: check-spelling/check-spelling@v0.0.24
with:
config: .github/actions/spell-check
checkout: true
@@ -175,7 +175,7 @@ jobs:
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
steps:
- name: comment
uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24
uses: check-spelling/check-spelling@v0.0.24
with:
config: .github/actions/spell-check
checkout: true
@@ -202,7 +202,7 @@ jobs:
cancel-in-progress: false
steps:
- name: apply spelling updates
uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24
uses: check-spelling/check-spelling@v0.0.24
with:
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
checkout: true

6
.gitignore vendored
View File

@@ -224,7 +224,7 @@ ClientBin/
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
@@ -322,7 +322,7 @@ ImageResizer/tools/**
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
@@ -331,7 +331,7 @@ ASALocalRun/
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Temp build files

Binary file not shown.

View File

@@ -1,51 +0,0 @@
{
"Version": "1.0.0",
"UseMinimatch": false,
"SignBatches": [
{
"MatchedPath": [
"*.dll",
"*.exe"
],
"SigningInfo": {
"Operations": [
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolSign",
"Parameters": [
{
"parameterName": "OpusName",
"parameterValue": "Microsoft"
},
{
"parameterName": "OpusInfo",
"parameterValue": "http://www.microsoft.com"
},
{
"parameterName": "FileDigest",
"parameterValue": "/fd \"SHA256\""
},
{
"parameterName": "PageHash",
"parameterValue": "/NPH"
},
{
"parameterName": "TimeStamp",
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
}
],
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolVerify",
"Parameters": [],
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
}
}
]
}

View File

@@ -217,11 +217,7 @@
"PowerToys.ZoomItSettingsInterop.dll",
"WinUI3Apps\\PowerToys.Settings.dll",
"WinUI3Apps\\PowerToys.Settings.exe",
"PowerToys.CmdPalModuleInterface.dll",
"CmdPalKeyboardService.dll",
"*Microsoft.CmdPal.UI_*.msix"
"WinUI3Apps\\PowerToys.Settings.exe"
],
"SigningInfo": {
"Operations": [
@@ -331,8 +327,6 @@
"TestableIO.System.IO.Abstractions.Wrappers.dll",
"WinUI3Apps\\TestableIO.System.IO.Abstractions.Wrappers.dll",
"WinUI3Apps\\OpenAI.dll",
"Testably.Abstractions.FileSystem.Interface.dll",
"WinUI3Apps\\Testably.Abstractions.FileSystem.Interface.dll",
"ColorCode.Core.dll",
"ColorCode.UWP.dll",
"UnitsNet.dll",

View File

@@ -1,108 +0,0 @@
{
"Version": "1.0.0",
"UseMinimatch": false,
"SignBatches": [
{
"MatchedPath": [
"Microsoft.CommandPalette.Extensions.Toolkit.dll"
],
"SigningInfo": {
"Operations": [
{
"KeyCode": "CP-233904-SN",
"OperationSetCode": "StrongNameSign",
"ToolName": "sign",
"ToolVersion": "1.0",
"Parameters": []
},
{
"KeyCode": "CP-233904-SN",
"OperationSetCode": "StrongNameVerify",
"ToolName": "sign",
"ToolVersion": "1.0",
"Parameters": []
},
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolSign",
"Parameters": [
{
"parameterName": "OpusName",
"parameterValue": "Microsoft"
},
{
"parameterName": "OpusInfo",
"parameterValue": "http://www.microsoft.com"
},
{
"parameterName": "FileDigest",
"parameterValue": "/fd \"SHA256\""
},
{
"parameterName": "PageHash",
"parameterValue": "/NPH"
},
{
"parameterName": "TimeStamp",
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
}
],
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolVerify",
"Parameters": [],
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
}
},
{
"MatchedPath": [
"Microsoft.CommandPalette.Extensions.dll"
],
"SigningInfo": {
"Operations": [
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolSign",
"Parameters": [
{
"parameterName": "OpusName",
"parameterValue": "Microsoft"
},
{
"parameterName": "OpusInfo",
"parameterValue": "http://www.microsoft.com"
},
{
"parameterName": "FileDigest",
"parameterValue": "/fd \"SHA256\""
},
{
"parameterName": "PageHash",
"parameterValue": "/NPH"
},
{
"parameterName": "TimeStamp",
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
}
],
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolVerify",
"Parameters": [],
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
}
}
]
}

View File

@@ -1,24 +1,16 @@
Param(
# Using the default value of 1.7 for winAppSdkVersionNumber and useExperimentalVersion as false
# Using the default value of 1.6 for winAppSdkVersionNumber and useExperimentalVersion as false
[Parameter(Mandatory=$False,Position=1)]
[string]$winAppSdkVersionNumber = "1.7",
[string]$winAppSdkVersionNumber = "1.6",
# When the pipeline calls the PS1 file, the passed parameters are converted to string type
[Parameter(Mandatory=$False,Position=2)]
[boolean]$useExperimentalVersion = $False,
# Root folder Path for processing
[Parameter(Mandatory=$False,Position=3)]
[string]$rootPath = $(Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)),
# Root folder Path for processing
[Parameter(Mandatory=$False,Position=4)]
[string]$sourceLink = "https://microsoft.pkgs.visualstudio.com/ProjectReunion/_packaging/Project.Reunion.nuget.internal/nuget/v3/index.json"
[boolean]$useExperimentalVersion = $False
)
function Update-NugetConfig {
param (
[string]$filePath = [System.IO.Path]::Combine($rootPath, "nuget.config")
[string]$filePath = "nuget.config"
)
Write-Host "Updating nuget.config file"
@@ -43,33 +35,7 @@ function Update-NugetConfig {
$xml.Save($filePath)
}
function Read-FileWithEncoding {
param (
[string]$Path
)
$reader = New-Object System.IO.StreamReader($Path, $true) # auto-detect encoding
$content = $reader.ReadToEnd()
$encoding = $reader.CurrentEncoding
$reader.Close()
return [PSCustomObject]@{
Content = $content
Encoding = $encoding
}
}
function Write-FileWithEncoding {
param (
[string]$Path,
[string]$Content,
[System.Text.Encoding]$Encoding
)
$writer = New-Object System.IO.StreamWriter($Path, $false, $Encoding)
$writer.Write($Content)
$writer.Close()
}
$sourceLink = "https://microsoft.pkgs.visualstudio.com/ProjectReunion/_packaging/Project.Reunion.nuget.internal/nuget/v3/index.json"
# Execute nuget list and capture the output
if ($useExperimentalVersion) {
@@ -113,53 +79,50 @@ if ($latestVersion) {
}
# Update packages.config files
Get-ChildItem -Path $rootPath -Recurse packages.config | ForEach-Object {
$file = Read-FileWithEncoding -Path $_.FullName
$content = $file.Content
Get-ChildItem -Recurse packages.config | ForEach-Object {
$content = Get-Content $_.FullName -Raw
if ($content -match 'package id="Microsoft.WindowsAppSDK"') {
$newVersionString = 'package id="Microsoft.WindowsAppSDK" version="' + $WinAppSDKVersion + '"'
$oldVersionString = 'package id="Microsoft.WindowsAppSDK" version="[-.0-9a-zA-Z]*"'
$content = $content -replace $oldVersionString, $newVersionString
Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding
Set-Content -Path $_.FullName -Value $content
Write-Host "Modified " $_.FullName
}
}
# Update Directory.Packages.props file
Get-ChildItem -Path $rootPath -Recurse "Directory.Packages.props" | ForEach-Object {
$file = Read-FileWithEncoding -Path $_.FullName
$content = $file.Content
$propsFile = "Directory.Packages.props"
if (Test-Path $propsFile) {
$content = Get-Content $propsFile -Raw
if ($content -match '<PackageVersion Include="Microsoft.WindowsAppSDK"') {
$newVersionString = '<PackageVersion Include="Microsoft.WindowsAppSDK" Version="' + $WinAppSDKVersion + '" />'
$oldVersionString = '<PackageVersion Include="Microsoft.WindowsAppSDK" Version="[-.0-9a-zA-Z]*" />'
$content = $content -replace $oldVersionString, $newVersionString
Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding
Write-Host "Modified " $_.FullName
Set-Content -Path $propsFile -Value $content
Write-Host "Modified " $propsFile
}
}
# Update .vcxproj files
Get-ChildItem -Path $rootPath -Recurse *.vcxproj | ForEach-Object {
$file = Read-FileWithEncoding -Path $_.FullName
$content = $file.Content
Get-ChildItem -Recurse *.vcxproj | ForEach-Object {
$content = Get-Content $_.FullName -Raw
if ($content -match '\\Microsoft.WindowsAppSDK.') {
$newVersionString = '\Microsoft.WindowsAppSDK.' + $WinAppSDKVersion
$oldVersionString = '\\Microsoft.WindowsAppSDK.(?=[-.0-9a-zA-Z]*\d)[-.0-9a-zA-Z]*' #positive lookahead for at least a digit
$newVersionString = '\Microsoft.WindowsAppSDK.' + $WinAppSDKVersion + '\'
$oldVersionString = '\\Microsoft.WindowsAppSDK.[-.0-9a-zA-Z]*\\'
$content = $content -replace $oldVersionString, $newVersionString
Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding
Set-Content -Path $_.FullName -Value $content
Write-Host "Modified " $_.FullName
}
}
# Update .csproj files
Get-ChildItem -Path $rootPath -Recurse *.csproj | ForEach-Object {
$file = Read-FileWithEncoding -Path $_.FullName
$content = $file.Content
Get-ChildItem -Recurse *.csproj | ForEach-Object {
$content = Get-Content $_.FullName -Raw
if ($content -match 'PackageReference Include="Microsoft.WindowsAppSDK"') {
$newVersionString = 'PackageReference Include="Microsoft.WindowsAppSDK" Version="'+ $WinAppSDKVersion + '"'
$oldVersionString = 'PackageReference Include="Microsoft.WindowsAppSDK" Version="[-.0-9a-zA-Z]*"'
$content = $content -replace $oldVersionString, $newVersionString
Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding
Set-Content -Path $_.FullName -Value $content
Write-Host "Modified " $_.FullName
}
}

View File

@@ -25,7 +25,7 @@ steps:
fetchDepth: 1 # Don't need a deep checkout for loc files!
persistCredentials: true
- task: MicrosoftTDBuild.tdbuild-task.tdbuild-task.TouchdownBuildTask@5
- task: MicrosoftTDBuild.tdbuild-task.tdbuild-task.TouchdownBuildTask@3
displayName: 'Touchdown Build - 37400, PRODEXT'
inputs:
teamId: 37400

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.PowerToys.Telemetry" version="2.0.3" />
<package id="Microsoft.PowerToys.Telemetry" version="2.0.2" />
</packages>

View File

@@ -3,5 +3,5 @@
"notificationAliases": ["powertoys@microsoft.com"],
"instanceUrl": "https://microsoft.visualstudio.com",
"projectName": "OS",
"areaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\PowerToys"
"areaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\PowerToys"
}

View File

@@ -47,4 +47,4 @@ extends:
useVSPreview: ${{ parameters.useVSPreview }}
useLatestWinAppSDK: ${{ parameters.useLatestWinAppSDK }}
winAppSDKVersionNumber: ${{ parameters.winAppSDKVersionNumber }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}

View File

@@ -40,9 +40,6 @@ parameters:
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
variables:
- template: templates/variables-nuget-package-version.yml
extends:
template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates
parameters:
@@ -82,7 +79,6 @@ extends:
buildConfigurations: ${{ parameters.buildConfigurations }}
versionNumber: ${{ parameters.versionNumber }}
publishArtifacts: false # 1ES PT handles publication for us.
official: true
codeSign: true
runTests: false
signingIdentity:
@@ -105,36 +101,12 @@ extends:
# Prepare the localizations and telemetry config before the release build
- template: .pipelines/v2/templates/steps-fetch-and-prepare-localizations.yml@self
- pwsh: |-
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
& nuget.exe restore -configFile .pipelines/release-nuget.config -PackagesDirectory . .pipelines/packages.config
Move-Item -Force -Verbose "Microsoft.PowerToys.Telemetry.*\build\include\TraceLoggingDefines.h" "src\common\Telemetry\TraceLoggingDefines.h"
Move-Item -Force -Verbose "Microsoft.PowerToys.Telemetry.*\build\include\TelemetryBase.cs" "src\common\Telemetry\TelemetryBase.cs"
- script: |
call nuget.exe restore -configFile .pipelines/release-nuget.config -PackagesDirectory . .pipelines/packages.config || exit /b 1
move /Y "Microsoft.PowerToys.Telemetry.2.0.2\build\include\TraceLoggingDefines.h" "src\common\Telemetry\TraceLoggingDefines.h" || exit /b 1
move /Y "Microsoft.PowerToys.Telemetry.2.0.2\build\include\TelemetryBase.cs" "src\common\Telemetry\TelemetryBase.cs" || exit /b 1
displayName: Emplace telemetry files
- stage: Build_SDK
displayName: Build SDK
dependsOn: []
jobs:
- template: .pipelines/v2/templates/job-build-sdk.yml@self
parameters:
pool:
name: SHINE-INT-L
image: SHINE-VS17-Latest
os: windows
official: true
codeSign: true
signingIdentity:
serviceName: $(SigningServiceName)
appId: $(SigningAppId)
tenantId: $(SigningTenantId)
akvName: $(SigningAKVName)
authCertName: $(SigningAuthCertName)
signCertName: $(SigningSignCertName)
useManagedIdentity: $(SigningUseManagedIdentity)
clientId: $(SigningOriginalClientId)
- stage: Publish
displayName: Publish
dependsOn: [Build]
@@ -143,7 +115,5 @@ extends:
parameters:
versionNumber: ${{ parameters.versionNumber }}
includePublicSymbolServer: ${{ parameters.publishSymbolsToPublic }}
${{ if ne(parameters.publishSymbolsToPublic, true) }}:
symbolExpiryTime: 10 # For private builds, expire symbols within 10 days. The default is 100 years.
subscription: $(SymbolPublishingServiceConnection)
symbolProject: $(SymbolPublishingProject)

View File

@@ -11,9 +11,6 @@ parameters:
default:
- x64
- arm64
- name: official
type: boolean
default: false
- name: codeSign
type: boolean
default: false
@@ -53,9 +50,6 @@ parameters:
- name: runTests
type: boolean
default: true
- name: buildTests
type: boolean
default: true
- name: useVSPreview
type: boolean
default: false
@@ -102,7 +96,6 @@ jobs:
${{ else }}:
OutputBuildPlatform: ${{ platform }}
variables:
MakeAppxPath: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\MakeAppx.exe'
# Azure DevOps abhors a vacuum
# If these are blank, expansion will fail later on... which will result in direct substitution of the variable *names*
# later on. We'll just... set them to a single space and if we need to, check IsNullOrWhiteSpace.
@@ -113,7 +106,7 @@ jobs:
JobOutputArtifactName: build-$(BuildPlatform)-$(BuildConfiguration)${{ parameters.artifactStem }}
NUGET_RESTORE_MSBUILD_ARGS: /p:Platform=$(BuildPlatform) # Required for nuget to work due to self contained
NODE_OPTIONS: --max_old_space_size=16384
${{ if or(eq(parameters.runTests, true), eq(parameters.buildTests, true)) }}:
${{ if eq(parameters.runTests, true) }}:
MSBuildMainBuildTargets: Build;Test
${{ else }}:
MSBuildMainBuildTargets: Build
@@ -152,7 +145,7 @@ jobs:
- template: steps-ensure-dotnet-version.yml
parameters:
sdk: true
version: '6.0' # .NET 6.0 is required in CI for ESRP code signing tasks. Please do not remove.
version: '6.0'
- template: steps-ensure-dotnet-version.yml
parameters:
@@ -218,11 +211,6 @@ jobs:
env:
VCWhereExtraVersionTarget: '-prerelease'
- ${{ if eq(parameters.official, true) }}:
- template: .\steps-setup-versioning.yml
parameters:
directory: $(build.sourcesdirectory)\src\modules\cmdpal
- pwsh: |-
& "$(build.sourcesdirectory)\.pipelines\installWiX.ps1"
displayName: Download and install WiX 3.14 development build
@@ -254,32 +242,6 @@ jobs:
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: CopyFiles@2
displayName: Stage SDK/build
inputs:
contents: |-
"**/cmdpal/extensionsdk/nuget/Microsoft.CommandPalette.Extensions.SDK.props"
"**/cmdpal/extensionsdk/nuget/Microsoft.CommandPalette.Extensions.SDK.targets"
flattenFolders: True
targetFolder: $(JobOutputDirectory)/sdk/build
- task: CopyFiles@2
displayName: Stage SDK/lib
inputs:
contents: |-
"**/Microsoft.CommandPalette.Extensions.Toolkit/$(BuildPlatform)/release/WinUI3Apps/CmdPal/Microsoft.CommandPalette.Extensions.Toolkit.dll"
"**/Microsoft.CommandPalette.Extensions.Toolkit/$(BuildPlatform)/release/WinUI3Apps/CmdPal/Microsoft.CommandPalette.Extensions.Toolkit.deps.json"
flattenFolders: True
targetFolder: $(JobOutputDirectory)/sdk/lib/net8.0-windows10.0.19041.0
- task: CopyFiles@2
displayName: Stage SDK/winmd
inputs:
contents: |-
"**/Microsoft.CommandPalette.Extensions/$(BuildPlatform)/release/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.winmd"
flattenFolders: True
targetFolder: $(JobOutputDirectory)/sdk/winmd
- task: VSBuild@1
displayName: Build BugReportTool
inputs:
@@ -347,16 +309,11 @@ jobs:
displayName: HACK Copy core WebView2 ARM64 dll to output directory
condition: eq(variables['BuildPlatform'],'arm64')
inputs:
contents: packages/Microsoft.Web.WebView2.1.0.2903.40/runtimes/win-ARM64/native_uap/Microsoft.Web.WebView2.Core.dll
contents: packages/Microsoft.Web.WebView2.1.0.2739.15/runtimes/win-ARM64/native_uap/Microsoft.Web.WebView2.Core.dll
targetFolder: $(Build.SourcesDirectory)/ARM64/Release/WinUI3Apps/
flattenFolders: True
OverWrite: True
# Check if all projects (located in src sub-folder) import common props
- pwsh: |-
& '.pipelines/verifyCommonProps.ps1' -sourceDir '$(build.sourcesdirectory)\src'
displayName: Audit shared common props for CSharp projects in src sub-folder
# Check if deps.json files don't reference different dll versions.
- pwsh: |-
& '.pipelines/verifyDepsJsonLibraryVersions.ps1' -targetDir '$(build.sourcesdirectory)\$(BuildPlatform)\$(BuildConfiguration)'
@@ -402,36 +359,7 @@ jobs:
**\UnitTests-FancyZones.dll
!**\obj\**
- pwsh: |-
$Package = (Get-ChildItem -Recurse -Filter "Microsoft.CmdPal.UI_*.msix" | Select -First 1)
$PackageFilename = $Package.FullName
Write-Host "##vso[task.setvariable variable=CmdPalPackagePath]${PackageFilename}"
displayName: Locate the CmdPal MSIX
- ${{ if eq(parameters.codeSign, true) }}:
- pwsh: |-
& "$(MakeAppxPath)" unpack /p "$(CmdPalPackagePath)" /d "$(JobOutputDirectory)/CmdPalPackageContents"
displayName: Unpack the MSIX for signing
- template: steps-esrp-signing.yml
parameters:
displayName: Sign CmdPal MSIX content
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: '$(JobOutputDirectory)/CmdPalPackageContents'
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_cmdpal_msix_content.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
- pwsh: |-
$outDir = New-Item -Type Directory "$(JobOutputDirectory)/_appx" -ErrorAction:Ignore
$PackageFilename = Join-Path $outDir.FullName (Split-Path -Leaf "$(CmdPalPackagePath)")
& "$(MakeAppxPath)" pack /h SHA256 /o /p $PackageFilename /d "$(JobOutputDirectory)/CmdPalPackageContents"
Copy-Item -Force $PackageFilename "$(CmdPalPackagePath)"
Remove-Item -Force -Recurse "$(JobOutputDirectory)/CmdPalPackageContents" -ErrorAction:Ignore
Remove-Item -Force -Recurse "$(JobOutputDirectory)/_appx" -ErrorAction:Ignore
displayName: Re-pack the new CmdPal package after signing
- template: steps-esrp-signing.yml
parameters:
displayName: Sign Core PowerToys
@@ -452,10 +380,6 @@ jobs:
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_DSC.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
- pwsh: |-
Copy-Item -Verbose -Force "$(CmdPalPackagePath)" "$(JobOutputDirectory)"
displayName: Stage the final CmdPal package
- template: steps-build-installer.yml
parameters:
codeSign: ${{ parameters.codeSign }}
@@ -511,7 +435,7 @@ jobs:
$machinePlat = "hash_machine_$(BuildPlatform).txt";
$combinedUserPath = $p + $userPlat;
$combinedMachinePath = $p + $machinePlat;
echo $p
echo $userPlat
@@ -521,7 +445,7 @@ jobs:
echo $machinePlat
echo $machineHash
echo $combinedMachinePath
$userHash | out-file -filepath $combinedUserPath
$machineHash | out-file -filepath $combinedMachinePath
displayName: Calculate file hashes
@@ -533,7 +457,7 @@ jobs:
displayName: Stage GPO files
# Running the tests may result in future jobs consuming artifacts out of this build
- ${{ if or(eq(parameters.runTests, true), eq(parameters.buildTests, true)) }}:
- ${{ if eq(parameters.runTests, true) }}:
- task: CopyFiles@2
displayName: Stage entire build output
inputs:

View File

@@ -1,100 +0,0 @@
parameters:
- name: buildConfigurations
type: object
default:
- Release
- name: official
type: boolean
default: false
- name: codeSign
type: boolean
default: false
- name: pool
type: object
default: []
- name: signingIdentity
type: object
default: {}
jobs:
- job: "BuildSDK"
${{ if ne(length(parameters.pool), 0) }}:
pool: ${{ parameters.pool }}
displayName: Build SDK
timeoutInMinutes: 240
cancelTimeoutInMinutes: 1
templateContext: # Required when this template is hosted in 1ES PT
outputs:
- output: pipelineArtifact
artifactName: SDK
targetPath: $(Build.ArtifactStagingDirectory)
steps:
- checkout: self
clean: true
submodules: true
persistCredentials: True
fetchTags: false
fetchDepth: 1
- template: .\steps-ensure-nuget-version.yml
- task: NuGetAuthenticate@1
- ${{ if eq(parameters.official, true) }}:
- template: .\steps-setup-versioning.yml
parameters:
directory: $(build.sourcesdirectory)\src\modules\cmdpal
- pwsh: |-
& "$(build.sourcesdirectory)\src\modules\cmdpal\extensionsdk\nuget\BuildSDKHelper.ps1" -Configuration "Release" -BuildStep "build" -IsAzurePipelineBuild
displayName: Build SDK
- ${{ if eq(parameters.codeSign, true) }}:
- template: steps-esrp-signing.yml
parameters:
displayName: Sign SDK
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: 'src/modules'
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_sdk.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
- pwsh: |-
& "$(build.sourcesdirectory)\src\modules\cmdpal\extensionsdk\nuget\BuildSDKHelper.ps1" -Configuration "Release" -BuildStep "pack" -IsAzurePipelineBuild
displayName: Pack SDK
- task: CopyFiles@2
displayName: Copy Nuget to Artifact Staging
inputs:
sourceFolder: "$(build.sourcesdirectory)/src/modules/cmdpal/extensionsdk/_build"
contents: '*.nupkg'
targetFolder: '$(Build.ArtifactStagingDirectory)'
- ${{ if eq(parameters.codeSign, true) }}:
- template: steps-esrp-signing.yml
parameters:
displayName: Sign NuGet packages
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: $(Build.ArtifactStagingDirectory)
Pattern: '*.nupkg'
UseMinimatch: true
signConfigType: inlineSignParams
inlineOperation: >-
[
{
"KeyCode": "CP-401405",
"OperationCode": "NuGetSign",
"Parameters": {},
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-401405",
"OperationCode": "NuGetVerify",
"Parameters": {},
"ToolName": "sign",
"ToolVersion": "1.0"
}
]

View File

@@ -0,0 +1,30 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/azure-pipelines-vscode/master/service-schema.json
jobs:
- job: Precheck
pool:
vmImage: windows-2022
steps:
- checkout: none
- pwsh: |-
try {
# Try based on pull request first
$pullRequestNumber = "$(system.pullRequest.pullRequestNumber)";
$gitHubPullRequest = Invoke-RestMethod -Method Get "https://api.github.com/repos/microsoft/PowerToys/pulls/$pullRequestNumber/files"
# If there are no files updated in the commit that are .md, set skipBuild variable
if(([array]($gitHubPullRequest.filename) -notmatch ".md|.txt").Length -eq 0) {
Write-Host '##vso[task.setvariable variable=skipBuild;isOutput=true]Yes'
Write-Host 'Skipping Build'
}
}
catch {
# Fall back to the latest commit otherwise.
$commit = "$(build.sourceVersion)";
$gitHubCommit = Invoke-RestMethod -Method Get "https://api.github.com/repos/microsoft/PowerToys/commits/$commit"
if(([array]($githubCommit.files.filename) -notmatch ".md|.txt").Length -eq 0) {
Write-Host '##vso[task.setvariable variable=skipBuild;isOutput=true]Yes'
Write-Host 'Skipping Build'
}
}
displayName: Verify whether we need to build at all
name: verifyBuildRequest

View File

@@ -11,36 +11,24 @@ parameters:
- name: useLatestWebView2
type: boolean
default: false
- name: isUIAutomationPipeline
type: boolean
default: false
jobs:
- job: Test${{ parameters.platform }}${{ parameters.configuration }}
displayName: Test ${{ parameters.platform }} ${{ parameters.configuration }}
timeoutInMinutes: 300
variables:
${{ if or(eq(parameters.platform, 'x64Win10'), eq(parameters.platform, 'x64Win11')) }}:
BuildPlatform: x64
${{ else }}:
BuildPlatform: ${{ parameters.platform }}
TestPlatform: ${{ parameters.platform }}
BuildPlatform: ${{ parameters.platform }}
BuildConfiguration: ${{ parameters.configuration }}
SrcPath: $(Build.Repository.LocalPath)
TestArtifactsName: build-${{ variables.BuildPlatform }}-${{ parameters.configuration }}${{ parameters.inputArtifactStem }}
TestArtifactsName: build-${{ parameters.platform }}-${{ parameters.configuration }}${{ parameters.inputArtifactStem }}
pool:
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
${{ if ne(parameters.platform, 'ARM64') }}:
name: SHINE-INT-Testing-x64
${{ if and(eq(parameters.isUIAutomationPipeline, true), eq(parameters.platform, 'x64Win11')) }}:
demands: ImageOverride -equals SHINE-W11-Testing
${{ else }}:
name: SHINE-INT-Testing-arm64
${{ else }}:
${{ if ne(parameters.platform, 'ARM64') }}:
name: SHINE-OSS-Testing-x64
${{ if and(eq(parameters.isUIAutomationPipeline, true), eq(parameters.platform, 'x64Win11')) }}:
demands: ImageOverride -equals SHINE-W11-Testing
${{ else }}:
name: SHINE-OSS-Testing-arm64
steps:
@@ -113,17 +101,8 @@ jobs:
vsTestVersion: 'toolsInstaller'
uiTests: true
rerunFailedTests: true
${{ if eq(parameters.isUIAutomationPipeline, true) }}:
testAssemblyVer2: |
**\*UITest*.dll
!**\obj\**
!**\ref\**
!**\UITests-FancyZones\**\UITests-FancyZonesEditor.dll
${{ else }}:
testAssemblyVer2: |
**\UITests-FancyZonesEditor.dll
!**\obj\**
!**\ref\**
!**\UITests-FancyZones\**\UITests-FancyZonesEditor.dll
env:
platform: '$(TestPlatform)'
testAssemblyVer2: |
**\UITests-FancyZones.dll
**\UITests-FancyZonesEditor.dll
!**\obj\**
!**\ref\**

View File

@@ -36,10 +36,20 @@ parameters:
default: false
stages:
# Allow manual builds to skip pre-check
- ${{ if ne(variables['Build.Reason'], 'Manual') }}:
- stage: Precheck
jobs:
- template: job-ci-precheck.yml
- ${{ each platform in parameters.buildPlatforms }}:
- stage: Build_${{ platform }}
displayName: Build ${{ platform }}
dependsOn: []
${{ if ne(variables['Build.Reason'], 'Manual') }}:
dependsOn: [Precheck]
condition: and(succeeded(), ne(dependencies.Precheck.outputs['Precheck.verifyBuildRequest.skipBuild'], 'Yes'))
${{ else }}:
dependsOn: []
jobs:
- template: job-build-project.yml
parameters:

View File

@@ -1,87 +0,0 @@
variables:
- name: runCodesignValidationInjectionBG
value: false
- name: EnablePipelineCache
value: true
- ${{ if eq(parameters.enableMsBuildCaching, true) }}:
- name: EnablePipelineCache
value: true
parameters:
- name: buildPlatforms
type: object
default:
- x64
- arm64
- name: enableMsBuildCaching
type: boolean
default: false
- name: useVSPreview
type: boolean
default: false
- name: useLatestWebView2
type: boolean
default: false
stages:
- ${{ each platform in parameters.buildPlatforms }}:
- stage: Build_${{ platform }}
displayName: Build ${{ platform }}
dependsOn: []
jobs:
- template: job-build-project.yml
parameters:
pool:
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
name: SHINE-INT-L
${{ else }}:
name: SHINE-OSS-L
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS17-Preview
buildPlatforms:
- ${{ platform }}
buildConfigurations: [Release]
enablePackageCaching: true
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
runTests: false
buildTests: true
useVSPreview: ${{ parameters.useVSPreview }}
- ${{ if eq(platform, 'x64') }}:
- stage: Test_x64Win10
displayName: Test x64Win10
dependsOn:
- Build_${{platform}}
jobs:
- template: job-test-project.yml
parameters:
platform: x64Win10
configuration: Release
useLatestWebView2: ${{ parameters.useLatestWebView2 }}
isUIAutomationPipeline: true
- ${{ if eq(platform, 'x64') }}:
- stage: Test_x64Win11
displayName: Test x64Win11
dependsOn:
- Build_${{platform}}
jobs:
- template: job-test-project.yml
parameters:
platform: x64Win11
configuration: Release
useLatestWebView2: ${{ parameters.useLatestWebView2 }}
isUIAutomationPipeline: true
- ${{ if ne(platform, 'x64') }}:
- stage: Test_${{ platform }}
displayName: Test ${{ platform }}
dependsOn:
- Build_${{platform}}
jobs:
- template: job-test-project.yml
parameters:
platform: ${{ platform }}
configuration: Release
useLatestWebView2: ${{ parameters.useLatestWebView2 }}
isUIAutomationPipeline: true

View File

@@ -87,30 +87,6 @@ steps:
dir $(build.sourcesdirectory)\extractedMsi
displayName: "${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Extract and verify MSI"
# Extract CmdPal msix package to check if its content is signed
- pwsh: |-
Write-Host "Extracting CmdPal MSIX package"
# Define the directory to search
$searchDir = "extractedMsi\File"
# Define the regex pattern for MSIX files
$pattern = '^Microsoft.CmdPal.UI.*\.msix$'
# Get all files in the directory and subdirectories
$msixFile = Get-ChildItem -Path $searchDir -Recurse -File | Where-Object {
$_.Name -match $pattern
}
Write-Host "MSIX file found: " $msixFile
$destinationDir = "$(build.sourcesdirectory)\extractedMsi\File\extractedCmdPalMsix"
Expand-Archive -Path $msixFile -DestinationPath $destinationDir
Get-ChildItem -Path $destinationDir -Recurse -File
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Extract CmdPal MSIX package
# Check if deps.json files don't reference different dll versions.
- pwsh: |-
& '.pipelines/verifyDepsJsonLibraryVersions.ps1' -targetDir '$(build.sourcesdirectory)\extractedMsi\File'

View File

@@ -4,7 +4,7 @@ parameters:
default: false
steps:
- task: TouchdownBuildTask@5
- task: TouchdownBuildTask@3
displayName: 'Download Localization Files -- PowerToys 37400'
inputs:
teamId: 37400

View File

@@ -1,11 +0,0 @@
parameters:
- name: directory
type: string
default: $(Build.SourcesDirectory)
steps:
- pwsh: |-
nuget install Microsoft.Windows.Terminal.Versioning -ConfigFile "$(Build.SourcesDirectory)\.pipelines\release-nuget.config" -OutputDirectory _versioning
$VersionRoot = (Get-Item _versioning\Microsoft.Windows.*).FullName
& "$VersionRoot\build\Setup.ps1" -ProjectDirectory "${{ parameters.directory }}" -Verbose
displayName: Set up versioning for ${{ parameters.directory }} via M.W.T.V

View File

@@ -17,7 +17,6 @@ steps:
arguments: >
-winAppSdkVersionNumber ${{ parameters.versionNumber }}
-useExperimentalVersion $${{ parameters.useExperimentalVersion }}
-rootPath "$(build.sourcesdirectory)"
- script: echo $(WinAppSDKVersion)
displayName: 'Display WinAppSDK Version Found'
@@ -28,6 +27,9 @@ steps:
buildType: 'specific'
project: '55e8140e-57ac-4e5f-8f9c-c7c15b51929d'
definition: '104083'
specificBuildWithTriggering: false
allowPartiallySucceededBuilds: true
allowFailedBuilds: true
buildVersionToDownload: 'latestFromBranch'
branchName: 'refs/heads/release/${{ parameters.versionNumber }}-stable'
artifactName: 'WindowsAppSDK_Nuget_And_MSIX'

View File

@@ -1,17 +0,0 @@
variables:
# If we are building a branch called "stable*", hide the NuGet suffix.
# If we don't do that, XES will set the suffix to "stable".
# main is special, however. XES ignores main. Since we never produce actual
# shipping builds from main, we want to force it to have a beta label.
#
# In effect:
# BRANCH / BRANDING | Version |
# ------------------|----------------------------|
# stable | 0.2.250512001 |
# main | 0.2.250512001-experimental |
# all others | 0.2.250512001-branch |
${{ if startsWith(variables['Build.SourceBranchName'], 'stable') }}:
NoNuGetPackBetaVersion: true
${{ elseif eq(variables['Build.SourceBranchName'], 'main') }}:
NuGetPackBetaVersion: experimental

View File

@@ -1,28 +1,7 @@
$VSInstances = ([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml))
$VSPackages = $VSInstances.instances.instance.packages.package
$LatestVCPackage = ($VSPackages | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" })
$LatestVCPackage = ($VSInstances.instances.instance.packages.package | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" })
$LatestVCToolsVersion = $LatestVCPackage.version;
$VSRoot = (& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property 'resolvedInstallationPath')
$VCToolsRoot = Join-Path $VSRoot "VC\Tools\MSVC"
# We have observed a few instances where the VC tools package version actually
# differs from the version on the files themselves. We might as well check
# whether the version we just found _actually exists_ before we use it.
# We'll use whichever highest version exists.
$PackageVCToolPath = Join-Path $VCToolsRoot $LatestVCToolsVersion
If ($Null -Eq (Get-Item $PackageVCToolPath -ErrorAction:Ignore)) {
$VCToolsVersions = Get-ChildItem $VCToolsRoot | ForEach-Object {
[Version]$_.Name
} | Sort -Descending
$LatestActualVCToolsVersion = $VCToolsVersions | Select -First 1
If ([Version]$LatestVCToolsVersion -Ne $LatestActualVCToolsVersion) {
Write-Output "VC Tools Mismatch: Directory = $LatestActualVCToolsVersion, Package = $LatestVCToolsVersion"
$LatestVCToolsVersion = $LatestActualVCToolsVersion.ToString(3)
}
}
Write-Output "Latest VCToolsVersion: $LatestVCToolsVersion"
Write-Output "Updating VCToolsVersion environment variable for job"
Write-Output "##vso[task.setvariable variable=VCToolsVersion]$LatestVCToolsVersion"

View File

@@ -1,54 +0,0 @@
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, Position = 1)]
[string]$sourceDir
)
# scan all csharp project in the source directory
function Get-CSharpProjects {
param (
[string]$path
)
# Get all .csproj files under the specified path
return Get-ChildItem -Path $path -Recurse -Filter *.csproj | Select-Object -ExpandProperty FullName
}
# Check if the project file imports 'Common.Dotnet.CsWinRT.props'
function Test-ImportSharedCsWinRTProps {
param (
[string]$filePath
)
# Load the XML content of the .csproj file
[xml]$csprojContent = Get-Content -Path $filePath
# Check if the Import element with Project attribute containing 'Common.Dotnet.CsWinRT.props' exists
return $csprojContent.Project.Import | Where-Object { $null -ne $_.Project -and $_.Project.EndsWith('Common.Dotnet.CsWinRT.props') }
}
# Call the function with the provided source directory
$csprojFilesArray = Get-CSharpProjects -path $sourceDir
$hasInvalidCsProj = $false
# Enumerate the array of file paths and call Validate-ImportSharedCsWinRTProps for each file
foreach ($csprojFile in $csprojFilesArray) {
# Skip if the file ends with 'TemplateCmdPalExtension.csproj'
if ($csprojFile -like '*TemplateCmdPalExtension.csproj') {
continue
}
$importExists = Test-ImportSharedCsWinRTProps -filePath $csprojFile
if (!$importExists) {
Write-Output "$csprojFile need to import 'Common.Dotnet.CsWinRT.props'."
$hasInvalidCsProj = $true
}
}
if ($hasInvalidCsProj) {
exit 1
}
exit 0

View File

@@ -18,11 +18,6 @@ $totalFailures = 0
Get-ChildItem $targetDir -Recurse -Filter *.deps.json -Exclude *UITest*,MouseJump.Common.UnitTests*,*.FuzzTests* | ForEach-Object {
# Temporarily exclude All UI-Test, Fuzzer-Test projects because of Appium.WebDriver dependencies
$depsJsonFullFileName = $_.FullName
if ($depsJsonFullFileName -like "*CmdPal*") {
return
}
$depsJsonFileName = $_.Name
$depsJson = Get-Content $depsJsonFullFileName | ConvertFrom-Json
@@ -91,4 +86,5 @@ if ($totalFailures -gt 0) {
}
Write-Host -ForegroundColor Green "All " $referencedFileVersionsPerDll.keys.Count " libraries are mentioned with the same version across the dependencies.`r`n"
exit 0
exit 0

View File

@@ -22,11 +22,7 @@ $versionExceptions = @(
"TraceReloggerLib.dll",
"Microsoft.WindowsAppRuntime.Release.Net.dll",
"Microsoft.Windows.Widgets.Projection.dll",
"WinRT.Host.Shim.dll",
"WyHash.dll",
"Microsoft.Recognizers.Text.DataTypes.TimexExpression.dll",
"ObjectModelCsProjection.dll",
"RendererCsProjection.dll") -join '|';
"WinRT.Host.Shim.dll") -join '|';
$nullVersionExceptions = @(
"codicon.ttf",
"e_sqlite3.dll",
@@ -47,14 +43,12 @@ $nullVersionExceptions = @(
"PushNotificationsLongRunningTask.ProxyStub.dll",
"WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll",
"System.Diagnostics.EventLog.Messages.dll",
"Microsoft.Windows.Widgets.dll",
"AdaptiveCards.ObjectModel.WinUI3.dll",
"AdaptiveCards.Rendering.WinUI3.dll") -join '|';
"Microsoft.Windows.Widgets.dll") -join '|';
$totalFailure = 0;
Write-Host $DirPath;
if (-not (Test-Path $DirPath)) {
if (-not (Test-Path $DirPath)) {
Write-Error "Folder does not exist!"
}
@@ -76,7 +70,7 @@ $items | ForEach-Object {
Write-Host "Version set to 1.0.0.0: " + $_.FullName
$totalFailure++;
}
elseif ($_.VersionInfo.FileVersion -eq $null -and $_.Name -notmatch $nullVersionExceptions) {
elseif ($_.VersionInfo.FileVersion -eq $null -and $_.Name -notmatch $nullVersionExceptions) {
# These items are exceptions that actually a version not set.
Write-Host "Version not set: " + $_.FullName
$totalFailure++;

View File

@@ -38,19 +38,9 @@ $verPropReadFileLocation = $verPropWriteFileLocation;
$verProps.Project.PropertyGroup.Version = $versionNumber;
$verProps.Project.PropertyGroup.DevEnvironment = $DevEnvironment;
Write-Host "xml" $verProps.Project.PropertyGroup.Version
Write-Host "xml" $verProps.Project.PropertyGroup.Version
$verProps.Save($verPropWriteFileLocation);
#### The same thing as above, but for the CmdPal version
$verPropWriteFileLocation = $PSScriptRoot + '/../src/CmdPalVersion.props';
$verPropReadFileLocation = $verPropWriteFileLocation;
[XML]$verProps = Get-Content $verPropReadFileLocation
$verProps.Project.PropertyGroup.DevEnvironment = $DevEnvironment;
Write-Host "xml" $verProps.Project.PropertyGroup.Version
$verProps.Save($verPropWriteFileLocation);
#######
# Set PowerRenameContextMenu package version in AppManifest.xml
$powerRenameContextMenuAppManifestWriteFileLocation = $PSScriptRoot + '/../src/modules/powerrename/PowerRenameContextMenu/AppxManifest.xml';
$powerRenameContextMenuAppManifestReadFileLocation = $powerRenameContextMenuAppManifestWriteFileLocation;

View File

@@ -1,13 +1,12 @@
{
"version": "1.0",
"components": [
"components": [
"Microsoft.VisualStudio.Component.CoreEditor",
"Microsoft.VisualStudio.Workload.CoreEditor",
"Microsoft.VisualStudio.Workload.NativeDesktop",
"Microsoft.VisualStudio.Workload.ManagedDesktop",
"Microsoft.VisualStudio.Workload.Universal",
"Microsoft.VisualStudio.Component.Windows10SDK.19041",
"Microsoft.VisualStudio.Component.Windows10SDK.20348",
"Microsoft.VisualStudio.Component.Windows10SDK.22621",
"Microsoft.VisualStudio.ComponentGroup.UWP.VC",
"Microsoft.VisualStudio.Component.UWP.VC.ARM64",
@@ -19,4 +18,4 @@
"Microsoft.VisualStudio.Component.VC.ATL.Spectre",
"Microsoft.VisualStudio.ComponentGroup.WindowsAppSDK.Cs"
]
}
}

View File

@@ -16,7 +16,7 @@ Christian contributed New+ utility
CleanCodeDeveloper helped do massive amounts of code stability and image resizer work.
### [@plante-msft](https://github.com/plante-msft) - Connor Plante
Connor was the creator of Workspaces and helped create Command Palette (PowerToys Run v2)
Connor was the creator of Workspaces and helped create PowerToys Run v2
### [@damienleroy](https://github.com/damienleroy) - [Damien Leroy](https://www.linkedin.com/in/Damien-Leroy-b2734416a/)
Damien has helped out by developing and contributing the Quick Accent utility.
@@ -46,7 +46,7 @@ Jeff added in multiple new features into Keyboard manager, such as key chord sup
Joe has helped triaging, discussing, issues as well as fixing bugs and building features for Text Extractor.
### [@joadoumie](https://github.com/joadoumie) - Jordi Adoumie
Jordi helped innovate amazing new features into Advanced Paste and helped create Command Palette (PowerToys Run v2)
Jordi helped innovate amazing new features into Advanced Paste and helped create PowerToys Run v2
### [@jsoref](https://github.com/jsoref) - [Josh Soref](https://check-spelling.dev/)
Helping keep our spelling correct :)
@@ -57,9 +57,6 @@ Color Picker is from Martin.
### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com)
Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility.
### [@pedrolamas](https://github.com/pedrolamas/) - Pedro Lamas
Pedro helped create the thumbnail and File Explorer previewers for 3D files like STL and GCode. If you like 3D printing, these are very helpful.
### [@PesBandi](https://github.com/PesBandi/) - PesBandi
PesBandi has helped do massive amounts of Quick Accent and bug fixes.
@@ -187,11 +184,15 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Lead
- [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Lead
- [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager
- [@craigloewen-msft](https://github.com/craigloewen-msft) - Craig Loewen - Product Manager
- [@zhiwei-ms](https://github.com/zhiwei-ms) - Zhiwei Yu - Product Manager
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev lead
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead
- [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev lead
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev lead
- [@drawbyperpetual](https://github.com/drawbyperpetual) - Anirudha Shankar - Dev
- [@mantaionut](https://github.com/mantaionut) - Ionut Manta - Dev
- [@donlaci](https://github.com/donlaci) - Laszlo Nemeth - Dev
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
- [@lei9444](https://github.com/lei9444) - Leilei Zhang - Dev
- [@shuaiyuanxx](https://github.com/shuaiyuanxx) - Shawn Yuan - Dev
- [@moooyo](https://github.com/moooyo) - Yu Leng - Dev
@@ -205,7 +206,7 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
- [@wang563681252](https://github.com/wang563681252) - Zhaopeng Wang - Dev
- [@vanzue](https://github.com/vanzue) - Kai Tao - Dev
## Former PowerToys core team members
# Former PowerToys core team members
- [@indierawk2k2](https://github.com/indierawk2k2) - Mike Harsh - Product Manager
- [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Product Manager
@@ -218,9 +219,3 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
- [@taras-janea](https://github.com/taras-janea) - Taras Sich - Dev
- [@yuyoyuppe](https://github.com/yuyoyuppe) - Andrey Nekrasov - Dev
- [@gokcekantarci](https://github.com/gokcekantarci) - Gokce Kantarci - Dev
- [@drawbyperpetual](https://github.com/drawbyperpetual) - Anirudha Shankar - Dev
- [@mantaionut](https://github.com/mantaionut) - Ionut Manta - Dev
- [@donlaci](https://github.com/donlaci) - Laszlo Nemeth - Dev
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev lead

View File

@@ -3,21 +3,17 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="AdaptiveCards.ObjectModel.WinUI3" Version="2.0.0-beta" />
<PackageVersion Include="AdaptiveCards.Rendering.WinUI3" Version="2.1.0-beta" />
<PackageVersion Include="AdaptiveCards.Templating" Version="2.0.2" />
<PackageVersion Include="Appium.WebDriver" Version="4.4.5" />
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.17" />
<PackageVersion Include="CommunityToolkit.Common" Version="8.4.0" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.2.250402" />
<PackageVersion Include="CommunityToolkit.WinUI.Collections" Version="8.2.250402" />
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
<PackageVersion Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250402" />
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250402" />
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.250402" />
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.0.240109" />
<PackageVersion Include="CommunityToolkit.WinUI.Collections" Version="8.0.240109" />
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.0.240109" />
<PackageVersion Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.240109" />
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.0.240109" />
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.0.240109" />
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.0.240109" />
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.0.240109" />
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.Markdown" Version="7.1.2" />
<PackageVersion Include="ControlzEx" Version="6.0.0" />
@@ -29,39 +25,38 @@
<PackageVersion Include="Mages" Version="3.0.0" />
<PackageVersion Include="Markdig.Signed" Version="0.34.0" />
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
<PackageVersion Include="MessagePack" Version="3.1.3" />
<PackageVersion Include="MessagePack" Version="2.5.187" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.5" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.2" />
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.5" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.2" />
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.2" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.0" />
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2739.15" />
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.5" />
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.340" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.5" />
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.2" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.2" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta" />
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
<!--
TODO: in Common.Dotnet.CsWinRT.props, on upgrade, verify RemoveCsWinRTPackageAnalyzer is no longer needed.
This is present due to a bug in CsWinRT where WPF projects cause the analyzer to fail.
-->
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.1.5" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.241114003" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />
<!-- Moq to stay below v4.20 due to behavior change. need to be sure fixed -->
<PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="MSTest" Version="3.8.3" />
<PackageVersion Include="MSTest" Version="3.6.3" />
<PackageVersion Include="NLog" Version="5.0.4" />
<PackageVersion Include="NLog.Extensions.Logging" Version="5.3.8" />
<PackageVersion Include="NLog.Schema" Version="5.2.8" />
@@ -69,42 +64,38 @@
<PackageVersion Include="ReverseMarkdown" Version="4.1.0" />
<PackageVersion Include="ScipBe.Common.Office.OneNote" Version="3.0.1" />
<PackageVersion Include="SharpCompress" Version="0.37.2" />
<PackageVersion Include="StreamJsonRpc" Version="2.21.69" />
<PackageVersion Include="StreamJsonRpc" Version="2.19.27" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<!-- Package System.CodeDom added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Management but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.CodeDom" Version="9.0.5" />
<PackageVersion Include="System.CodeDom" Version="9.0.2" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.5" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.5" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.5" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.2" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.2" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.2" />
<!-- Package System.Data.SqlClient added to force it as a dependency of Microsoft.Windows.Compatibility to the latest version available at this time. -->
<PackageVersion Include="System.Data.SqlClient" Version="4.8.6" />
<!-- Package System.Diagnostics.EventLog added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.5" />
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.2" />
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.11. -->
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.5" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.5" />
<PackageVersion Include="System.IO.Abstractions" Version="22.0.13" />
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
<PackageVersion Include="System.Management" Version="9.0.5" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.2" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.2" />
<PackageVersion Include="System.IO.Abstractions" Version="21.0.29" />
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="21.0.29" />
<PackageVersion Include="System.Management" Version="9.0.2" />
<PackageVersion Include="System.Reactive" Version="6.0.1" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.5" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.5" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.5" />
<PackageVersion Include="System.Text.Json" Version="9.0.5" />
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.2" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.2" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.2" />
<PackageVersion Include="System.Text.Json" Version="9.0.2" />
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
<PackageVersion Include="UnitsNet" Version="5.56.0" />
<PackageVersion Include="UTF.Unknown" Version="2.5.1" />
<PackageVersion Include="WinUIEx" Version="2.2.0" />
<PackageVersion Include="WPF-UI" Version="3.0.5" />
<PackageVersion Include="WyHash" Version="1.0.5" />
</ItemGroup>
<ItemGroup Condition="'$(IsExperimentationLive)'!=''">
<!-- Additional dependencies used by experimentation -->
<PackageVersion Include="Microsoft.VariantAssignment.Client" Version="2.4.17140001" />
<PackageVersion Include="Microsoft.VariantAssignment.Contract" Version="3.0.16990001" />
</ItemGroup>
</Project>
</Project>

217
NOTICE.md
View File

@@ -3,14 +3,12 @@
This software incorporates material from third parties.
- Color Picker
- Command Palette
- File Explorer Add-ins
- ImageResizer
- PowerToys Run
- Installer/Runner
- Measure tool
- Peek
- Registry Preview
## Utility: Color Picker
@@ -40,75 +38,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## Utility: Command Palette
### wyhash
We use the WyHash NuGet package for calculating stable hashes for strings.
**Source**: [https://github.com/wangyi-fudan/wyhash](https://github.com/wangyi-fudan/wyhash)
```
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
```
## Utility: Command Palette Built-in Extensions
### Calculator
#### Mages
We use the Mages NuGet package for calculating the result of expression.
**Source**: [https://github.com/FlorianRappl/Mages](https://github.com/FlorianRappl/Mages)
```
The MIT License (MIT)
Copyright (c) 2016 - 2025 Florian Rappl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
## Utility: File Explorer Add-ins
### Monaco Editor
@@ -859,34 +788,6 @@ SOFTWARE.
## Utility: Peek
### Monaco Editor
**Source**: https://github.com/Microsoft/monaco-editor
**Additional third party notifications:** https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt
The MIT License (MIT)
Copyright (c) 2016 - present Microsoft Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
### The Quite OK Image Format reference decoder
**Source**: https://github.com/phoboslab/qoi
@@ -1393,55 +1294,19 @@ EXHIBIT A -Mozilla Public License.
Original Code Source Code for Your Modifications.]
```
## Utility: Registry Preview
### Monaco Editor
**Source**: https://github.com/Microsoft/monaco-editor
**Additional third party notifications:** https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt
```
The MIT License (MIT)
Copyright (c) 2016 - present Microsoft Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
## NuGet Packages used by PowerToys
- AdaptiveCards.ObjectModel.WinUI3 2.0.0-beta
- AdaptiveCards.Rendering.WinUI3 2.1.0-beta
- AdaptiveCards.Templating 2.0.2
- Appium.WebDriver 4.4.5
- Azure.AI.OpenAI 1.0.0-beta.17
- CommunityToolkit.Common 8.4.0
- CommunityToolkit.Mvvm 8.4.0
- CommunityToolkit.WinUI.Animations 8.2.250402
- CommunityToolkit.WinUI.Collections 8.2.250402
- CommunityToolkit.WinUI.Controls.Primitives 8.2.250402
- CommunityToolkit.WinUI.Controls.Segmented 8.2.250402
- CommunityToolkit.WinUI.Controls.SettingsControls 8.2.250402
- CommunityToolkit.WinUI.Controls.Sizers 8.2.250402
- CommunityToolkit.WinUI.Converters 8.2.250402
- CommunityToolkit.WinUI.Extensions 8.2.250402
- 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
@@ -1452,65 +1317,59 @@ SOFTWARE.
- LazyCache 2.4.0
- Mages 3.0.0
- Markdig.Signed 0.34.0
- MessagePack 3.1.3
- Microsoft.Bcl.AsyncInterfaces 9.0.5
- MessagePack 2.5.187
- Microsoft.Bcl.AsyncInterfaces 9.0.2
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0
- Microsoft.Data.Sqlite 9.0.5
- Microsoft.Data.Sqlite 9.0.2
- Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16
- Microsoft.DotNet.ILCompiler (A)
- Microsoft.Extensions.DependencyInjection 9.0.5
- Microsoft.Extensions.Hosting 9.0.5
- Microsoft.Extensions.Hosting.WindowsServices 9.0.5
- Microsoft.Extensions.Logging 9.0.5
- Microsoft.Extensions.Logging.Abstractions 9.0.5
- Microsoft.Extensions.DependencyInjection 9.0.2
- Microsoft.Extensions.Hosting 9.0.2
- Microsoft.Extensions.Hosting.WindowsServices 9.0.2
- Microsoft.Extensions.Logging 9.0.2
- Microsoft.Extensions.Logging.Abstractions 9.0.2
- Microsoft.NET.ILLink.Tasks (A)
- Microsoft.SemanticKernel 1.15.0
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
- Microsoft.Web.WebView2 1.0.2903.40
- Microsoft.Win32.SystemEvents 9.0.5
- Microsoft.Windows.Compatibility 9.0.5
- Microsoft.Web.WebView2 1.0.2739.15
- Microsoft.Win32.SystemEvents 9.0.2
- Microsoft.Windows.Compatibility 9.0.2
- Microsoft.Windows.CsWin32 0.2.46-beta
- Microsoft.Windows.CsWinRT 2.2.0
- Microsoft.Windows.CsWinRT 2.1.5
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
- Microsoft.WindowsAppSDK 1.7.250513003
- Microsoft.WindowsPackageManager.ComInterop 1.10.340
- Microsoft.WindowsAppSDK 1.6.241114003
- Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9
- Microsoft.Xaml.Behaviors.Wpf 1.1.39
- ModernWpfUI 0.9.4
- Moq 4.18.4
- MSTest 3.8.3
- MSTest 3.6.3
- NLog.Extensions.Logging 5.3.8
- NLog.Schema 5.2.8
- OpenAI 2.0.0
- ReverseMarkdown 4.1.0
- ScipBe.Common.Office.OneNote 3.0.1
- SharpCompress 0.37.2
- StreamJsonRpc 2.21.69
- StreamJsonRpc 2.19.27
- StyleCop.Analyzers 1.2.0-beta.556
- System.CodeDom 9.0.5
- System.CodeDom 9.0.2
- System.CommandLine 2.0.0-beta4.22272.1
- System.ComponentModel.Composition 9.0.5
- System.Configuration.ConfigurationManager 9.0.5
- System.Data.OleDb 9.0.5
- System.ComponentModel.Composition 9.0.2
- System.Configuration.ConfigurationManager 9.0.2
- System.Data.OleDb 9.0.2
- System.Data.SqlClient 4.8.6
- System.Diagnostics.EventLog 9.0.5
- System.Diagnostics.PerformanceCounter 9.0.5
- System.Drawing.Common 9.0.5
- System.IO.Abstractions 22.0.13
- System.IO.Abstractions.TestingHelpers 22.0.13
- System.Management 9.0.5
- System.Net.Http 4.3.4
- System.Private.Uri 4.3.2
- System.Diagnostics.EventLog 9.0.2
- System.Diagnostics.PerformanceCounter 9.0.2
- System.Drawing.Common 9.0.2
- System.IO.Abstractions 21.0.29
- System.IO.Abstractions.TestingHelpers 21.0.29
- System.Management 9.0.2
- System.Reactive 6.0.1
- System.Runtime.Caching 9.0.5
- System.ServiceProcess.ServiceController 9.0.5
- System.Text.Encoding.CodePages 9.0.5
- System.Text.Json 9.0.5
- System.Text.RegularExpressions 4.3.1
- System.Runtime.Caching 9.0.2
- System.ServiceProcess.ServiceController 9.0.2
- System.Text.Encoding.CodePages 9.0.2
- System.Text.Json 9.0.2
- UnicodeInformation 2.6.0
- UnitsNet 5.56.0
- UTF.Unknown 2.5.1
- WinUIEx 2.2.0
- WPF-UI 3.0.5
- WyHash 1.0.5

View File

@@ -620,64 +620,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EtwTrace", "src\common\Tele
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBorders.UnitTests", "src\modules\MouseWithoutBorders\MouseWithoutBorders.UnitTests\MouseWithoutBorders.UnitTests.csproj", "{66614C26-314C-4B91-9071-76133422CFEF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CommandPalette", "CommandPalette", "{3846508C-77EB-4034-A702-F8BB263C4F79}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Built-in Extensions", "Built-in Extensions", "{ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Apps", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.Apps\Microsoft.CmdPal.Ext.Apps.csproj", "{6CE438DF-C245-4997-A360-0A0939E4BA34}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Bookmarks", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.Bookmark\Microsoft.CmdPal.Ext.Bookmarks.csproj", "{E09AA983-C755-474F-83D6-A5CDF528C070}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Calc", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.Calc\Microsoft.CmdPal.Ext.Calc.csproj", "{6D56B64D-FF1F-488F-AFED-9B9854A5D399}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Registry", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.Registry\Microsoft.CmdPal.Ext.Registry.csproj", "{92EC89E4-9972-453A-8A1A-3A9E230C146A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowsServices", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.WindowsServices\Microsoft.CmdPal.Ext.WindowsServices.csproj", "{51939B4F-1F62-4BFF-A6A2-C08646E5BE95}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowsSettings", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.WindowsSettings\Microsoft.CmdPal.Ext.WindowsSettings.csproj", "{D1160404-D3D1-497A-883A-4059C07C2273}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowsTerminal", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.WindowsTerminal\Microsoft.CmdPal.Ext.WindowsTerminal.csproj", "{40F6D69D-E321-400F-A767-5628C7AE453D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extension SDK", "Extension SDK", "{F3D09629-59A2-4924-A4B9-D6BFAA2C1B49}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.CommandPalette.Extensions", "src\modules\cmdpal\extensionsdk\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.vcxproj", "{305DD37E-C85D-4B08-AAFE-7381FA890463}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CommandPalette.Extensions.Toolkit", "src\modules\cmdpal\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj", "{CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Common", "src\modules\cmdpal\Microsoft.CmdPal.Common\Microsoft.CmdPal.Common.csproj", "{14E62033-58D0-4A7D-8990-52F50A08BBBD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.UI", "src\modules\cmdpal\Microsoft.Terminal.UI\Microsoft.Terminal.UI.vcxproj", "{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sample Extensions", "Sample Extensions", "{071E18A4-A530-46B8-AB7D-B862EE55E24E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProcessMonitorExtension", "src\modules\cmdpal\ext\ProcessMonitorExtension\ProcessMonitorExtension.csproj", "{C846F7A7-792A-47D9-B0CB-417C900EE03D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SamplePagesExtension", "src\modules\cmdpal\ext\SamplePagesExtension\SamplePagesExtension.csproj", "{C831231F-891C-4572-9694-45062534B42A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UI", "UI", "{7520A2FE-00A2-49B8-83ED-DB216E874C04}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.UI", "src\modules\cmdpal\Microsoft.CmdPal.UI\Microsoft.CmdPal.UI.csproj", "{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.UI.ViewModels", "src\modules\cmdpal\Microsoft.CmdPal.UI.ViewModels\Microsoft.CmdPal.UI.ViewModels.csproj", "{C66020D1-CB10-4CF7-8715-84C97FD5E5E2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.ClipboardHistory", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.ClipboardHistory\Microsoft.CmdPal.Ext.ClipboardHistory.csproj", "{79775343-7A3D-445D-9104-3DD5B2893DF9}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalModuleInterface", "src\modules\cmdpal\CmdPalModuleInterface\CmdPalModuleInterface.vcxproj", "{0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesCsharpLibrary", "src\modules\Workspaces\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj", "{89D0E199-B17A-418C-B2F8-7375B6708357}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewPlus.ShellExtension.win10", "src\modules\NewPlus\NewShellExtensionContextMenu.win10\NewPlus.ShellExtension.win10.vcxproj", "{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.Indexer", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.Indexer\Microsoft.CmdPal.Ext.Indexer.csproj", "{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Shell", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.Shell\Microsoft.CmdPal.Ext.Shell.csproj", "{C0CE3B5E-16D3-495D-B335-CA791B660162}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowWalker", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.WindowWalker\Microsoft.CmdPal.Ext.WindowWalker.csproj", "{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.WebSearch", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.WebSearch\Microsoft.CmdPal.Ext.WebSearch.csproj", "{605E914B-7232-4789-AF46-BF5D3DDFC14E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.WinGet", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.WinGet\Microsoft.CmdPal.Ext.WinGet.csproj", "{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdvancedPaste.UnitTests", "src\modules\AdvancedPaste\AdvancedPaste.UnitTests\AdvancedPaste.UnitTests.csproj", "{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdvancedPaste.FuzzTests", "src\modules\AdvancedPaste\AdvancedPaste.FuzzTests\AdvancedPaste.FuzzTests.csproj", "{7F5B9557-5878-4438-A721-3E28296BA193}"
@@ -690,8 +636,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZoomItModuleInterface", "sr
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZoomItSettingsInterop", "src\modules\ZoomIt\ZoomItSettingsInterop\ZoomItSettingsInterop.vcxproj", "{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.TimeDate", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.TimeDate\Microsoft.CmdPal.Ext.TimeDate.csproj", "{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UITestAutomation", "src\common\UITestAutomation\UITestAutomation.csproj", "{A558C25D-2007-498E-8B6F-43405AFAE9E2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeyboardManagerEditorUI", "src\modules\keyboardmanager\KeyboardManagerEditorUI\KeyboardManagerEditorUI.csproj", "{08F9155D-B6DC-46E5-9C83-AF60B655898B}"
@@ -702,20 +646,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hosts.FuzzTests", "src\modu
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hosts.UITests", "src\modules\Hosts\Hosts.UITests\Hosts.UITests.csproj", "{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegistryPreview.FuzzTests", "src\modules\registrypreview\RegistryPreview.FuzzTests\RegistryPreview.FuzzTests.csproj", "{5702B3CC-8575-48D5-83D8-15BB42269CD3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.System", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.System\Microsoft.CmdPal.Ext.System.csproj", "{64B88F02-CD88-4ED8-9624-989A800230F9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FancyZones.FuzzTests", "src\modules\fancyzones\FancyZones.FuzzTests\FancyZones.FuzzTests.csproj", "{0217E86E-3476-9946-DE8E-9D200CEBD47A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalKeyboardService", "src\modules\cmdpal\CmdPalKeyboardService\CmdPalKeyboardService.vcxproj", "{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.FuzzingTest", "src\modules\powerrename\PowerRename.FuzzingTest\PowerRename.FuzzingTest.vcxproj", "{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MouseUtils.UITests", "src\modules\MouseUtils\MouseUtils.UITests\MouseUtils.UITests.csproj", "{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesEditorUITest", "src\modules\Workspaces\WorkspacesEditorUITest\WorkspacesEditorUITest.csproj", "{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -2268,154 +2198,6 @@ Global
{66614C26-314C-4B91-9071-76133422CFEF}.Release|ARM64.Build.0 = Release|ARM64
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.ActiveCfg = Release|x64
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.Build.0 = Release|x64
{6CE438DF-C245-4997-A360-0A0939E4BA34}.Debug|ARM64.ActiveCfg = Debug|ARM64
{6CE438DF-C245-4997-A360-0A0939E4BA34}.Debug|ARM64.Build.0 = Debug|ARM64
{6CE438DF-C245-4997-A360-0A0939E4BA34}.Debug|x64.ActiveCfg = Debug|x64
{6CE438DF-C245-4997-A360-0A0939E4BA34}.Debug|x64.Build.0 = Debug|x64
{6CE438DF-C245-4997-A360-0A0939E4BA34}.Release|ARM64.ActiveCfg = Release|ARM64
{6CE438DF-C245-4997-A360-0A0939E4BA34}.Release|ARM64.Build.0 = Release|ARM64
{6CE438DF-C245-4997-A360-0A0939E4BA34}.Release|x64.ActiveCfg = Release|x64
{6CE438DF-C245-4997-A360-0A0939E4BA34}.Release|x64.Build.0 = Release|x64
{E09AA983-C755-474F-83D6-A5CDF528C070}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E09AA983-C755-474F-83D6-A5CDF528C070}.Debug|ARM64.Build.0 = Debug|ARM64
{E09AA983-C755-474F-83D6-A5CDF528C070}.Debug|x64.ActiveCfg = Debug|x64
{E09AA983-C755-474F-83D6-A5CDF528C070}.Debug|x64.Build.0 = Debug|x64
{E09AA983-C755-474F-83D6-A5CDF528C070}.Release|ARM64.ActiveCfg = Release|ARM64
{E09AA983-C755-474F-83D6-A5CDF528C070}.Release|ARM64.Build.0 = Release|ARM64
{E09AA983-C755-474F-83D6-A5CDF528C070}.Release|x64.ActiveCfg = Release|x64
{E09AA983-C755-474F-83D6-A5CDF528C070}.Release|x64.Build.0 = Release|x64
{6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Debug|ARM64.ActiveCfg = Debug|ARM64
{6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Debug|ARM64.Build.0 = Debug|ARM64
{6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Debug|x64.ActiveCfg = Debug|x64
{6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Debug|x64.Build.0 = Debug|x64
{6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Release|ARM64.ActiveCfg = Release|ARM64
{6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Release|ARM64.Build.0 = Release|ARM64
{6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Release|x64.ActiveCfg = Release|x64
{6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Release|x64.Build.0 = Release|x64
{92EC89E4-9972-453A-8A1A-3A9E230C146A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{92EC89E4-9972-453A-8A1A-3A9E230C146A}.Debug|ARM64.Build.0 = Debug|ARM64
{92EC89E4-9972-453A-8A1A-3A9E230C146A}.Debug|x64.ActiveCfg = Debug|x64
{92EC89E4-9972-453A-8A1A-3A9E230C146A}.Debug|x64.Build.0 = Debug|x64
{92EC89E4-9972-453A-8A1A-3A9E230C146A}.Release|ARM64.ActiveCfg = Release|ARM64
{92EC89E4-9972-453A-8A1A-3A9E230C146A}.Release|ARM64.Build.0 = Release|ARM64
{92EC89E4-9972-453A-8A1A-3A9E230C146A}.Release|x64.ActiveCfg = Release|x64
{92EC89E4-9972-453A-8A1A-3A9E230C146A}.Release|x64.Build.0 = Release|x64
{51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Debug|ARM64.ActiveCfg = Debug|ARM64
{51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Debug|ARM64.Build.0 = Debug|ARM64
{51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Debug|x64.ActiveCfg = Debug|x64
{51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Debug|x64.Build.0 = Debug|x64
{51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Release|ARM64.ActiveCfg = Release|ARM64
{51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Release|ARM64.Build.0 = Release|ARM64
{51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Release|x64.ActiveCfg = Release|x64
{51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Release|x64.Build.0 = Release|x64
{D1160404-D3D1-497A-883A-4059C07C2273}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D1160404-D3D1-497A-883A-4059C07C2273}.Debug|ARM64.Build.0 = Debug|ARM64
{D1160404-D3D1-497A-883A-4059C07C2273}.Debug|x64.ActiveCfg = Debug|x64
{D1160404-D3D1-497A-883A-4059C07C2273}.Debug|x64.Build.0 = Debug|x64
{D1160404-D3D1-497A-883A-4059C07C2273}.Release|ARM64.ActiveCfg = Release|ARM64
{D1160404-D3D1-497A-883A-4059C07C2273}.Release|ARM64.Build.0 = Release|ARM64
{D1160404-D3D1-497A-883A-4059C07C2273}.Release|x64.ActiveCfg = Release|x64
{D1160404-D3D1-497A-883A-4059C07C2273}.Release|x64.Build.0 = Release|x64
{40F6D69D-E321-400F-A767-5628C7AE453D}.Debug|ARM64.ActiveCfg = Debug|ARM64
{40F6D69D-E321-400F-A767-5628C7AE453D}.Debug|ARM64.Build.0 = Debug|ARM64
{40F6D69D-E321-400F-A767-5628C7AE453D}.Debug|x64.ActiveCfg = Debug|x64
{40F6D69D-E321-400F-A767-5628C7AE453D}.Debug|x64.Build.0 = Debug|x64
{40F6D69D-E321-400F-A767-5628C7AE453D}.Release|ARM64.ActiveCfg = Release|ARM64
{40F6D69D-E321-400F-A767-5628C7AE453D}.Release|ARM64.Build.0 = Release|ARM64
{40F6D69D-E321-400F-A767-5628C7AE453D}.Release|x64.ActiveCfg = Release|x64
{40F6D69D-E321-400F-A767-5628C7AE453D}.Release|x64.Build.0 = Release|x64
{305DD37E-C85D-4B08-AAFE-7381FA890463}.Debug|ARM64.ActiveCfg = Debug|ARM64
{305DD37E-C85D-4B08-AAFE-7381FA890463}.Debug|ARM64.Build.0 = Debug|ARM64
{305DD37E-C85D-4B08-AAFE-7381FA890463}.Debug|x64.ActiveCfg = Debug|x64
{305DD37E-C85D-4B08-AAFE-7381FA890463}.Debug|x64.Build.0 = Debug|x64
{305DD37E-C85D-4B08-AAFE-7381FA890463}.Release|ARM64.ActiveCfg = Release|ARM64
{305DD37E-C85D-4B08-AAFE-7381FA890463}.Release|ARM64.Build.0 = Release|ARM64
{305DD37E-C85D-4B08-AAFE-7381FA890463}.Release|x64.ActiveCfg = Release|x64
{305DD37E-C85D-4B08-AAFE-7381FA890463}.Release|x64.Build.0 = Release|x64
{CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Debug|ARM64.ActiveCfg = Debug|ARM64
{CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Debug|ARM64.Build.0 = Debug|ARM64
{CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Debug|x64.ActiveCfg = Debug|x64
{CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Debug|x64.Build.0 = Debug|x64
{CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Release|ARM64.ActiveCfg = Release|ARM64
{CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Release|ARM64.Build.0 = Release|ARM64
{CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Release|x64.ActiveCfg = Release|x64
{CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Release|x64.Build.0 = Release|x64
{14E62033-58D0-4A7D-8990-52F50A08BBBD}.Debug|ARM64.ActiveCfg = Debug|ARM64
{14E62033-58D0-4A7D-8990-52F50A08BBBD}.Debug|ARM64.Build.0 = Debug|ARM64
{14E62033-58D0-4A7D-8990-52F50A08BBBD}.Debug|x64.ActiveCfg = Debug|x64
{14E62033-58D0-4A7D-8990-52F50A08BBBD}.Debug|x64.Build.0 = Debug|x64
{14E62033-58D0-4A7D-8990-52F50A08BBBD}.Release|ARM64.ActiveCfg = Release|ARM64
{14E62033-58D0-4A7D-8990-52F50A08BBBD}.Release|ARM64.Build.0 = Release|ARM64
{14E62033-58D0-4A7D-8990-52F50A08BBBD}.Release|x64.ActiveCfg = Release|x64
{14E62033-58D0-4A7D-8990-52F50A08BBBD}.Release|x64.Build.0 = Release|x64
{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|ARM64.ActiveCfg = Debug|ARM64
{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|ARM64.Build.0 = Debug|ARM64
{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|x64.ActiveCfg = Debug|x64
{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|x64.Build.0 = Debug|x64
{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|ARM64.ActiveCfg = Release|ARM64
{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|ARM64.Build.0 = Release|ARM64
{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x64.ActiveCfg = Release|x64
{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x64.Build.0 = Release|x64
{C846F7A7-792A-47D9-B0CB-417C900EE03D}.Debug|ARM64.ActiveCfg = Debug|ARM64
{C846F7A7-792A-47D9-B0CB-417C900EE03D}.Debug|ARM64.Build.0 = Debug|ARM64
{C846F7A7-792A-47D9-B0CB-417C900EE03D}.Debug|ARM64.Deploy.0 = Debug|ARM64
{C846F7A7-792A-47D9-B0CB-417C900EE03D}.Debug|x64.ActiveCfg = Debug|x64
{C846F7A7-792A-47D9-B0CB-417C900EE03D}.Debug|x64.Build.0 = Debug|x64
{C846F7A7-792A-47D9-B0CB-417C900EE03D}.Debug|x64.Deploy.0 = Debug|x64
{C846F7A7-792A-47D9-B0CB-417C900EE03D}.Release|ARM64.ActiveCfg = Release|ARM64
{C846F7A7-792A-47D9-B0CB-417C900EE03D}.Release|ARM64.Build.0 = Release|ARM64
{C846F7A7-792A-47D9-B0CB-417C900EE03D}.Release|ARM64.Deploy.0 = Release|ARM64
{C846F7A7-792A-47D9-B0CB-417C900EE03D}.Release|x64.ActiveCfg = Release|x64
{C846F7A7-792A-47D9-B0CB-417C900EE03D}.Release|x64.Build.0 = Release|x64
{C846F7A7-792A-47D9-B0CB-417C900EE03D}.Release|x64.Deploy.0 = Release|x64
{C831231F-891C-4572-9694-45062534B42A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{C831231F-891C-4572-9694-45062534B42A}.Debug|ARM64.Build.0 = Debug|ARM64
{C831231F-891C-4572-9694-45062534B42A}.Debug|ARM64.Deploy.0 = Debug|ARM64
{C831231F-891C-4572-9694-45062534B42A}.Debug|x64.ActiveCfg = Debug|x64
{C831231F-891C-4572-9694-45062534B42A}.Debug|x64.Build.0 = Debug|x64
{C831231F-891C-4572-9694-45062534B42A}.Debug|x64.Deploy.0 = Debug|x64
{C831231F-891C-4572-9694-45062534B42A}.Release|ARM64.ActiveCfg = Release|ARM64
{C831231F-891C-4572-9694-45062534B42A}.Release|ARM64.Build.0 = Release|ARM64
{C831231F-891C-4572-9694-45062534B42A}.Release|ARM64.Deploy.0 = Release|ARM64
{C831231F-891C-4572-9694-45062534B42A}.Release|x64.ActiveCfg = Release|x64
{C831231F-891C-4572-9694-45062534B42A}.Release|x64.Build.0 = Release|x64
{C831231F-891C-4572-9694-45062534B42A}.Release|x64.Deploy.0 = Release|x64
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Debug|ARM64.ActiveCfg = Debug|ARM64
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Debug|ARM64.Build.0 = Debug|ARM64
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Debug|ARM64.Deploy.0 = Debug|ARM64
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Debug|x64.ActiveCfg = Debug|x64
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Debug|x64.Build.0 = Debug|x64
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Debug|x64.Deploy.0 = Debug|x64
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Release|ARM64.ActiveCfg = Release|ARM64
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Release|ARM64.Build.0 = Release|ARM64
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Release|ARM64.Deploy.0 = Release|ARM64
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Release|x64.ActiveCfg = Release|x64
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Release|x64.Build.0 = Release|x64
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Release|x64.Deploy.0 = Release|x64
{C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Debug|ARM64.ActiveCfg = Debug|ARM64
{C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Debug|ARM64.Build.0 = Debug|ARM64
{C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Debug|x64.ActiveCfg = Debug|x64
{C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Debug|x64.Build.0 = Debug|x64
{C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Release|ARM64.ActiveCfg = Release|ARM64
{C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Release|ARM64.Build.0 = Release|ARM64
{C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Release|x64.ActiveCfg = Release|x64
{C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Release|x64.Build.0 = Release|x64
{79775343-7A3D-445D-9104-3DD5B2893DF9}.Debug|ARM64.ActiveCfg = Debug|ARM64
{79775343-7A3D-445D-9104-3DD5B2893DF9}.Debug|ARM64.Build.0 = Debug|ARM64
{79775343-7A3D-445D-9104-3DD5B2893DF9}.Debug|x64.ActiveCfg = Debug|x64
{79775343-7A3D-445D-9104-3DD5B2893DF9}.Debug|x64.Build.0 = Debug|x64
{79775343-7A3D-445D-9104-3DD5B2893DF9}.Release|ARM64.ActiveCfg = Release|ARM64
{79775343-7A3D-445D-9104-3DD5B2893DF9}.Release|ARM64.Build.0 = Release|ARM64
{79775343-7A3D-445D-9104-3DD5B2893DF9}.Release|x64.ActiveCfg = Release|x64
{79775343-7A3D-445D-9104-3DD5B2893DF9}.Release|x64.Build.0 = Release|x64
{0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Debug|ARM64.ActiveCfg = Debug|ARM64
{0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Debug|ARM64.Build.0 = Debug|ARM64
{0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Debug|x64.ActiveCfg = Debug|x64
{0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Debug|x64.Build.0 = Debug|x64
{0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Release|ARM64.ActiveCfg = Release|ARM64
{0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Release|ARM64.Build.0 = Release|ARM64
{0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Release|x64.ActiveCfg = Release|x64
{0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Release|x64.Build.0 = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|ARM64.ActiveCfg = Debug|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|ARM64.Build.0 = Debug|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x64.ActiveCfg = Debug|x64
@@ -2432,54 +2214,6 @@ Global
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.Build.0 = Release|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.ActiveCfg = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.Build.0 = Release|x64
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|ARM64.ActiveCfg = Debug|ARM64
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|ARM64.Build.0 = Debug|ARM64
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|ARM64.Deploy.0 = Debug|ARM64
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|x64.ActiveCfg = Debug|x64
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|x64.Build.0 = Debug|x64
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|x64.Deploy.0 = Debug|x64
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|ARM64.ActiveCfg = Release|ARM64
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|ARM64.Build.0 = Release|ARM64
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|ARM64.Deploy.0 = Release|ARM64
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|x64.ActiveCfg = Release|x64
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|x64.Build.0 = Release|x64
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|x64.Deploy.0 = Release|x64
{C0CE3B5E-16D3-495D-B335-CA791B660162}.Debug|ARM64.ActiveCfg = Debug|ARM64
{C0CE3B5E-16D3-495D-B335-CA791B660162}.Debug|ARM64.Build.0 = Debug|ARM64
{C0CE3B5E-16D3-495D-B335-CA791B660162}.Debug|x64.ActiveCfg = Debug|x64
{C0CE3B5E-16D3-495D-B335-CA791B660162}.Debug|x64.Build.0 = Debug|x64
{C0CE3B5E-16D3-495D-B335-CA791B660162}.Release|ARM64.ActiveCfg = Release|ARM64
{C0CE3B5E-16D3-495D-B335-CA791B660162}.Release|ARM64.Build.0 = Release|ARM64
{C0CE3B5E-16D3-495D-B335-CA791B660162}.Release|x64.ActiveCfg = Release|x64
{C0CE3B5E-16D3-495D-B335-CA791B660162}.Release|x64.Build.0 = Release|x64
{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Debug|ARM64.ActiveCfg = Debug|ARM64
{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Debug|ARM64.Build.0 = Debug|ARM64
{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Debug|x64.ActiveCfg = Debug|x64
{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Debug|x64.Build.0 = Debug|x64
{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Release|ARM64.ActiveCfg = Release|ARM64
{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Release|ARM64.Build.0 = Release|ARM64
{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Release|x64.ActiveCfg = Release|x64
{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Release|x64.Build.0 = Release|x64
{605E914B-7232-4789-AF46-BF5D3DDFC14E}.Debug|ARM64.ActiveCfg = Debug|ARM64
{605E914B-7232-4789-AF46-BF5D3DDFC14E}.Debug|ARM64.Build.0 = Debug|ARM64
{605E914B-7232-4789-AF46-BF5D3DDFC14E}.Debug|x64.ActiveCfg = Debug|x64
{605E914B-7232-4789-AF46-BF5D3DDFC14E}.Debug|x64.Build.0 = Debug|x64
{605E914B-7232-4789-AF46-BF5D3DDFC14E}.Release|ARM64.ActiveCfg = Release|ARM64
{605E914B-7232-4789-AF46-BF5D3DDFC14E}.Release|ARM64.Build.0 = Release|ARM64
{605E914B-7232-4789-AF46-BF5D3DDFC14E}.Release|x64.ActiveCfg = Release|x64
{605E914B-7232-4789-AF46-BF5D3DDFC14E}.Release|x64.Build.0 = Release|x64
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Debug|ARM64.Build.0 = Debug|ARM64
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Debug|ARM64.Deploy.0 = Debug|ARM64
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Debug|x64.ActiveCfg = Debug|x64
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Debug|x64.Build.0 = Debug|x64
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Debug|x64.Deploy.0 = Debug|x64
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Release|ARM64.ActiveCfg = Release|ARM64
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Release|ARM64.Build.0 = Release|ARM64
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Release|ARM64.Deploy.0 = Release|ARM64
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Release|x64.ActiveCfg = Release|x64
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Release|x64.Build.0 = Release|x64
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Release|x64.Deploy.0 = Release|x64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Debug|ARM64.Build.0 = Debug|ARM64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Debug|x64.ActiveCfg = Debug|x64
@@ -2520,18 +2254,6 @@ Global
{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}.Release|ARM64.Build.0 = Release|ARM64
{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}.Release|x64.ActiveCfg = Release|x64
{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}.Release|x64.Build.0 = Release|x64
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Debug|ARM64.ActiveCfg = Debug|ARM64
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Debug|ARM64.Build.0 = Debug|ARM64
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Debug|ARM64.Deploy.0 = Debug|ARM64
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Debug|x64.ActiveCfg = Debug|x64
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Debug|x64.Build.0 = Debug|x64
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Debug|x64.Deploy.0 = Debug|x64
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Release|ARM64.ActiveCfg = Release|ARM64
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Release|ARM64.Build.0 = Release|ARM64
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Release|ARM64.Deploy.0 = Release|ARM64
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Release|x64.ActiveCfg = Release|x64
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Release|x64.Build.0 = Release|x64
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Release|x64.Deploy.0 = Release|x64
{A558C25D-2007-498E-8B6F-43405AFAE9E2}.Debug|ARM64.ActiveCfg = Debug|ARM64
{A558C25D-2007-498E-8B6F-43405AFAE9E2}.Debug|ARM64.Build.0 = Debug|ARM64
{A558C25D-2007-498E-8B6F-43405AFAE9E2}.Debug|x64.ActiveCfg = Debug|x64
@@ -2572,60 +2294,6 @@ Global
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0}.Release|ARM64.Build.0 = Release|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0}.Release|x64.ActiveCfg = Release|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0}.Release|x64.Build.0 = Release|x64
{5702B3CC-8575-48D5-83D8-15BB42269CD3}.Debug|ARM64.ActiveCfg = Debug|ARM64
{5702B3CC-8575-48D5-83D8-15BB42269CD3}.Debug|ARM64.Build.0 = Debug|ARM64
{5702B3CC-8575-48D5-83D8-15BB42269CD3}.Debug|x64.ActiveCfg = Debug|x64
{5702B3CC-8575-48D5-83D8-15BB42269CD3}.Debug|x64.Build.0 = Debug|x64
{5702B3CC-8575-48D5-83D8-15BB42269CD3}.Release|ARM64.ActiveCfg = Release|ARM64
{5702B3CC-8575-48D5-83D8-15BB42269CD3}.Release|ARM64.Build.0 = Release|ARM64
{5702B3CC-8575-48D5-83D8-15BB42269CD3}.Release|x64.ActiveCfg = Release|x64
{5702B3CC-8575-48D5-83D8-15BB42269CD3}.Release|x64.Build.0 = Release|x64
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|ARM64.ActiveCfg = Debug|ARM64
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|ARM64.Build.0 = Debug|ARM64
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x64.ActiveCfg = Debug|x64
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x64.Build.0 = Debug|x64
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.ActiveCfg = Release|ARM64
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.Build.0 = Release|ARM64
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.ActiveCfg = Release|x64
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.Build.0 = Release|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.ActiveCfg = Debug|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.Build.0 = Debug|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.ActiveCfg = Debug|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.Build.0 = Debug|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.ActiveCfg = Release|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.Build.0 = Release|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.ActiveCfg = Release|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.Build.0 = Release|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.ActiveCfg = Debug|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.Build.0 = Debug|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.ActiveCfg = Debug|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.Build.0 = Debug|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.ActiveCfg = Release|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.Build.0 = Release|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.ActiveCfg = Release|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.Build.0 = Release|x64
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|ARM64.ActiveCfg = Debug|ARM64
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|ARM64.Build.0 = Debug|ARM64
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|x64.ActiveCfg = Debug|x64
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|x64.Build.0 = Debug|x64
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Release|ARM64.ActiveCfg = Release|ARM64
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Release|ARM64.Build.0 = Release|ARM64
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Release|x64.ActiveCfg = Release|x64
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Release|x64.Build.0 = Release|x64
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Debug|ARM64.ActiveCfg = Debug|ARM64
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Debug|x64.ActiveCfg = Debug|x64
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Debug|x64.Build.0 = Debug|x64
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|ARM64.ActiveCfg = Release|ARM64
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.ActiveCfg = Release|x64
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.Build.0 = Release|x64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.Build.0 = Debug|ARM64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.ActiveCfg = Debug|x64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.Build.0 = Debug|x64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|ARM64.ActiveCfg = Release|ARM64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|ARM64.Build.0 = Release|ARM64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|x64.ActiveCfg = Release|x64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2765,7 +2433,7 @@ Global
{25C91A4E-BA4E-467A-85CD-8B62545BF674} = {A50C70A6-2DA0-4027-B90E-B1A40755A8A5}
{6AB6A2D6-F859-4A82-9184-0BD29C9F07D1} = {A50C70A6-2DA0-4027-B90E-B1A40755A8A5}
{212AD910-8488-4036-BE20-326931B75FB2} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{7AC943C9-52E8-44CF-9083-744D8049667B} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{7AC943C9-52E8-44CF-9083-744D8049667B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{54A93AF7-60C7-4F6C-99D2-FBB1F75F853A} = {7AC943C9-52E8-44CF-9083-744D8049667B}
{92C39820-9F84-4529-BC7D-22AAE514D63B} = {7AC943C9-52E8-44CF-9083-744D8049667B}
{515554D1-D004-4F7F-A107-2211FC0F6B2C} = {7AC943C9-52E8-44CF-9083-744D8049667B}
@@ -2853,54 +2521,19 @@ Global
{37D07516-4185-43A4-924F-3C7A5D95ECF6} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{8F021B46-362B-485C-BFBA-CCF83E820CBD} = {8F62026A-294B-41C6-8839-87463613F216}
{66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
{3846508C-77EB-4034-A702-F8BB263C4F79} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2} = {3846508C-77EB-4034-A702-F8BB263C4F79}
{6CE438DF-C245-4997-A360-0A0939E4BA34} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{E09AA983-C755-474F-83D6-A5CDF528C070} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{6D56B64D-FF1F-488F-AFED-9B9854A5D399} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{92EC89E4-9972-453A-8A1A-3A9E230C146A} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{51939B4F-1F62-4BFF-A6A2-C08646E5BE95} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{D1160404-D3D1-497A-883A-4059C07C2273} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{40F6D69D-E321-400F-A767-5628C7AE453D} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{F3D09629-59A2-4924-A4B9-D6BFAA2C1B49} = {3846508C-77EB-4034-A702-F8BB263C4F79}
{305DD37E-C85D-4B08-AAFE-7381FA890463} = {F3D09629-59A2-4924-A4B9-D6BFAA2C1B49}
{CA4D810F-C8F4-4B61-9DA9-71807E0B9F24} = {F3D09629-59A2-4924-A4B9-D6BFAA2C1B49}
{14E62033-58D0-4A7D-8990-52F50A08BBBD} = {7520A2FE-00A2-49B8-83ED-DB216E874C04}
{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F} = {7520A2FE-00A2-49B8-83ED-DB216E874C04}
{071E18A4-A530-46B8-AB7D-B862EE55E24E} = {3846508C-77EB-4034-A702-F8BB263C4F79}
{C846F7A7-792A-47D9-B0CB-417C900EE03D} = {071E18A4-A530-46B8-AB7D-B862EE55E24E}
{C831231F-891C-4572-9694-45062534B42A} = {071E18A4-A530-46B8-AB7D-B862EE55E24E}
{7520A2FE-00A2-49B8-83ED-DB216E874C04} = {3846508C-77EB-4034-A702-F8BB263C4F79}
{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90} = {7520A2FE-00A2-49B8-83ED-DB216E874C04}
{C66020D1-CB10-4CF7-8715-84C97FD5E5E2} = {7520A2FE-00A2-49B8-83ED-DB216E874C04}
{79775343-7A3D-445D-9104-3DD5B2893DF9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8} = {3846508C-77EB-4034-A702-F8BB263C4F79}
{89D0E199-B17A-418C-B2F8-7375B6708357} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{C0CE3B5E-16D3-495D-B335-CA791B660162} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{605E914B-7232-4789-AF46-BF5D3DDFC14E} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE} = {9873BA05-4C41-4819-9283-CF45D795431B}
{7F5B9557-5878-4438-A721-3E28296BA193} = {9873BA05-4C41-4819-9283-CF45D795431B}
{DD6E12FE-5509-4ABC-ACC2-3D6DC98A238C} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{0A84F764-3A88-44CD-AA96-41BDBD48627B} = {DD6E12FE-5509-4ABC-ACC2-3D6DC98A238C}
{E4585179-2AC1-4D5F-A3FF-CFC5392F694C} = {DD6E12FE-5509-4ABC-ACC2-3D6DC98A238C}
{CA7D8106-30B9-4AEC-9D05-B69B31B8C461} = {DD6E12FE-5509-4ABC-ACC2-3D6DC98A238C}
{DCC6BD67-17BB-47AA-B507-FB0FE43A7449} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{A558C25D-2007-498E-8B6F-43405AFAE9E2} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{08F9155D-B6DC-46E5-9C83-AF60B655898B} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
{4382A954-179A-4078-92AF-715187DFFF50} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
{EBED240C-8702-452D-B764-6DB9DA9179AF} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
{5702B3CC-8575-48D5-83D8-15BB42269CD3} = {929C1324-22E8-4412-A9A8-80E85F3985A5}
{64B88F02-CD88-4ED8-9624-989A800230F9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{0217E86E-3476-9946-DE8E-9D200CEBD47A} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2} = {3846508C-77EB-4034-A702-F8BB263C4F79}
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

222
README.md
View File

@@ -11,15 +11,14 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
| | Current utilities: | |
|--------------|--------------------|--------------|
| [Advanced Paste](https://aka.ms/PowerToysOverview_AdvancedPaste) | [Always on Top](https://aka.ms/PowerToysOverview_AoT) | [PowerToys Awake](https://aka.ms/PowerToysOverview_Awake) |
| [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | [Command Not Found](https://aka.ms/PowerToysOverview_CmdNotFound) | [Command Palette](https://aka.ms/PowerToysOverview_CmdPal) |
| [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) | [New+](https://aka.ms/PowerToysOverview_NewPlus) | [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) |
| [Peek](https://aka.ms/PowerToysOverview_Peek) | [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) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) |
| [ZoomIt](https://aka.ms/PowerToysOverview_ZoomIt) |
| [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) |
| [New+](https://aka.ms/PowerToysOverview_NewPlus) | [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) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) | [ZoomIt](https://aka.ms/PowerToysOverview_ZoomIt) |
## Installing and running Microsoft PowerToys
@@ -35,19 +34,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.
<!-- items that need to be updated release to release -->
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.92%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.91%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysUserSetup-0.91.1-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysUserSetup-0.91.1-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysSetup-0.91.1-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysSetup-0.91.1-arm64.exe
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.90%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.89%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.89.0/PowerToysUserSetup-0.89.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.89.0/PowerToysUserSetup-0.89.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.89.0/PowerToysSetup-0.89.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.89.0/PowerToysSetup-0.89.0-arm64.exe
| Description | Filename | sha256 hash |
|----------------|----------|-------------|
| Per user - x64 | [PowerToysUserSetup-0.91.1-x64.exe][ptUserX64] | 42EA4A3E8C79A5456476D19E72B3E2AB9393A89C4DC7442EB7EE5A1E3490D38A |
| Per user - ARM64 | [PowerToysUserSetup-0.91.1-arm64.exe][ptUserArm64] | F3F433FE04049F9197411D792AADEBF34E3BE7FE16327BD8B73D2A046ED8BAF6 |
| Machine wide - x64 | [PowerToysSetup-0.91.1-x64.exe][ptMachineX64] | EC4BC3A8625775866B0ED4577CCF83E6EC7B1A0AD267379DDBAF4FE49C7B5BDD |
| Machine wide - ARM64 | [PowerToysSetup-0.91.1-arm64.exe][ptMachineArm64] | 9CB8911008420D0E446AE3D5CE365E447FA4DF9DCF9337F3A80F933C00FC3689 |
| Per user - x64 | [PowerToysUserSetup-0.89.0-x64.exe][ptUserX64] | B4F130CC96F321024A257499247F6FF6DA56612215ED3882E868AAE26C689E33 |
| Per user - ARM64 | [PowerToysUserSetup-0.89.0-arm64.exe][ptUserArm64] | F69B00F4E520EB09FA0D1D1669E21910C5225FE7A2EEDC0FA7C283B201A5F9C6 |
| Machine wide - x64 | [PowerToysSetup-0.89.0-x64.exe][ptMachineX64] | E18AC8F9023E341CF7DAD35367FB9DDDB6565D83D8155DBCDDB40AE8A24AE731 |
| Machine wide - ARM64 | [PowerToysSetup-0.89.0-arm64.exe][ptMachineArm64] | 17DEADEC601D6061D7AF4F487595CC36D9191813003CC2ECE381017F0EC71FBB |
This is our preferred method.
@@ -93,167 +92,102 @@ 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.91 - May 2025 Update
### 0.89 - February 2025 Update
In this release, we focused on new features, stability, and automation.
In this release, we focused on new features, stability, accessibility and automation.
**✨Highlights**
- We focused on greatly improving Command Palette's performance and fixing a large amount of bugs. Some new features we've added are:
- Added the ability for Command Palette to search any file using a fallback command.
- Added the ability to make the Command Palette global hotkey a low-level keyboard hook.
- Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from Command Palette.
- You can now define custom formats in the Date and Time plugins of PT Run and Command Palette. Thanks [@htcfreek](https://github.com/htcfreek)!
- Enhanced Advanced Paste by adding media transcoding support to convert different video and audio file formats! Thanks [@snickler](https://github.com/snickler) for your help!
- Fixed crashes when loading thumbnails after the .NET 9 update and resolved PowerLauncher.exe blocking other MSI installers from creating shortcuts!
- Fixed accessibility issues across FancyZones, Image Resizer, and Settings to improve screen reader support and clarity!
- Enhanced UI automation framework across modules and added new tests to cover manual checks, with more improvements coming!
### General
- Fixed an issue where updating PowerToys on Windows 11 did not properly update context menu entries, impacting New+, PowerRename, Image Resizer, and File Locksmith.
- Updated .NET Packages from 9.0.1 to 9.0.2. Thanks [@snickler](https://github.com/snickler) for this.
- Enabled compatibility with VS17.3 and later, for C++23. Thanks [@LNKLEO](https://github.com/LNKLEO) for this.
### Advanced Paste
- Fixed an issue where Advanced Paste failed to create the OCR engine for certain English language tags (e.g., en-CA) by initializing the OCR engine with the user profile language. Thanks [@cryolithic](https://github.com/cryolithic)!
- Added media transcoding support to convert different video and audio file formats, improved UI layouts, refined clipboard handling, and integrated Semantic Kernel for smarter pasting. Thanks [@snickler](https://github.com/snickler) for your help!
### Color Picker
### FancyZones
- Fixed an issue where a resource leak caused hangs or crashes by properly disposing of the Graphics object. Thanks [@dcog989](https://github.com/dcog989)!
- Fixed an issue where Color Picker exited on Backspace keypress by ensuring it only closes when focused and aligning Escape/Backspace behavior. Thanks [@PesBandi](https://github.com/PesBandi)!
- Added support for Oklab and Oklch color formats in Color Picker. Thanks [@lemonyte](https://github.com/lemonyte)!
### Command Not Found
- Updated the WinGet Command Not Found script to only enable the experimental features if they actually exist.
### Command Palette
- Updated bug template to include Command Palette module.
- Fixed an issue where the toast window was not scaled for DPI, causing layout issues under display scaling.
- Fixed an issue where Up/Down keyboard navigation didn't move selection when caret was at position 0, and add continuous navigation like PT Run v1. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Updated the Time and Date extension code to simplify it and improve clarity.
- Fixed an issue where capitalization in the command causes failure when trying to go to the mouse pointer, resolved by adjusting the command to lowercase.
- Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from Command Palette. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added setting to enable/disable system tray icon in CmdPal and align terminology with Windows 11. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed an alias update issue by removing the old alias when a new one is set.
- Resolved GitHub casing conflict by migrating Exts and exts into a new ext directory, ensuring consistent structure across platforms and preventing path fragmentation.
- Fix an issue where the 'Create New Extension' command generated empty file names.
- Added the ability to make the global hotkey a low-level keyboard hook.
- Added support for JUMBO thumbnails, enabling access to high-resolution icons.
- Fixed crashes when CmdPal auto-hid itself while an MSAL dialog was opened, by preventing CmdPal from hiding if it's disabled.
- Added support for immediately selecting search text when a page is loaded.
- Fixed a bug where extension settings pages failed to reload on reopen by updating the settings form when extension settings are saved.
- Fixed an issue where the Command Palette failed to launch from the runner.
- Refactored and ported the PowerToys Run v1 calculator logic into Command Palette, added settings support, and improved fallback behavior.
- Re-added support for list item keyboard shortcuts.
- Enhanced accessibility in Command Palette by adding proper labels, refining animations, improving localization, and fixed a11y related issues.
- Ported custom format support to the Time and Date plugin, reordered and cleaned up settings, improved error messaging, and fixed edge-case crashes for more robust and user-friendly behavior. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added fallback item for system command.
- Fixed a bug in Windows System Command where the key prompt incorrectly displayed "Empty" for the "Open Recycle Bin" action. Thanks [@jironemo](https://github.com/jironemo)!
- Fixed an issue where the 'more commands' list showed commands that shouldn't be visible. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed an issue where the details view in Command Palette displayed an oversized icon and misaligned text, aligning it with Windows Search behavior.
- Fixed a bug where empty screen content and command bar commands were cut off when using long labels, ensuring proper layout and visibility.
- Improved CmdPals WinGet integration by fixing version display for installed packages, enabling updates with icons, and migrating the preview winget API to a stable version.
- Fixed a bug where commands for ContentPage didn't update until after exit, by ensuring context menus are fully initialized when they change.
- Added fallback support to the TimeDate extension, enabling direct date/time queries without pre-selecting the command.
- Added import of Common.Dotnet.AotCompatibility.props across multiple CmdPal project files to enhance AOT compilation support.
- Fixed a crash in CmdPal settings caused by a null HotKey when settings.json is missing or lacks a defined hotkey. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Added support for filterable, nested context menus in CmdPal, including a search box to maintain focus behavior.
- Refactored CmdPal classes to improve JSON serialization and introduced new serialization contexts for better performance and maintainability.
- Added support for ahead-of-time (AoT) compilation.
- Added retry mechanism for CmdPal launch.
- Removed some unused files from CmdPal.Common to simplify codebase and facilitate marking it as AoT-compatible.
- Fixed a bug where a race condition in the update of SearchText caused the cursor in the input box to automatically jump to the end of the line, ensuring SearchText is only updated after it has actually been changed.
- Added support for searching any file in fallback command.
- Cleaned up AoT-related code to prevent duplicate operations during testing.
- Reduced CmdPal load time by parallelizing extension startup and adding timeouts to prevent misbehaving extensions from blocking others.
- Enhanced UI behavior by dismissing the details pane when the list gets emptied, avoiding inconsistent visual states.
- Added support to unset the fallback command in CmdPal when no matching command is found, ensuring cleaner reload behavior.
- Fixed a leak in the CmdPal extension template by addressing improper ComServer use.
- Prevented CmdPal window from maximizing on title bar double-click to maintain intended window behavior. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed an issue where the Settings UI launched too small by making window dimensions DPI-aware and enforcing minimum width and height using WinUIEx.
- Fixed white flash and one-time animation issues in CmdPal by cloaking the window instead of hiding it.
- Fixed a bug where all extension settings were fetched on startup by lazy-loading extension settings, reducing initialization overhead.
- Added support for protecting CmdPal from crashes on Adaptive Card parse failure.
- Replaced shell:AppsFolder with URI activation in CmdPal to improve reliability.
- Added ability to open CmdPal settings from PowerToys Settings.
- Added ability for CmdPal to observe and dynamically update extension details by tracking property changes on the selected item.
- Bumped the toolkit version used in the CmdPal extension template to 0.2.0.
- Fixed accessibility by improving the text for monitors, ensuring clearer naming and help text for screen readers.
### Image Resizer
- Fixed issues with Width and Height fields in Image Resizer's Custom preset, ensuring empty values no longer cause errors, settings save correctly, and auto-scaling behaves as expected. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed accessibility by ensuring screen readers announce selected image dimensions in the combo-box for better navigation.
- Fixed an issue where deleting an Image Resizer preset removed the wrong preset.
### Monaco Preview
### Keyboard Manager
- Fixed open link in default browser rather than Microsoft Edge. Thanks [@OldUser101](https://github.com/OldUser101)!
- Fixed an issue where a modifier key, when set without specifying left or right, would get stuck due to incorrect key handling, by tracking the pressed keys and sending the correct key accordingly. Thanks [@mantaionut](https://github.com/mantaionut)!
### Mouse Highlighter
- Fixed a highlight released on an Administrator window will start fading, instead of staying on the screen indefinitely until the mouse button is pressed again on an unelevated window.
### Mouse Without Borders
- Fixed an issue in service mode where copy-paste and drag-drop file transfers didnt work, ensuring seamless file operations.
- Enabled GPO for enable/disable for Mouse Without Borders in Service Mode. Thanks [@htcfreek](https://github.com/htcfreek) for review and comments!
- Fixed code maintainability by refactoring the oversized 'Common' class in Mouse Without Borders into smaller, focused classes for better structure and clarity. Thanks [@mikeclayton](https://github.com/mikeclayton) and thanks [@htcfreek](https://github.com/htcfreek) for review!
### PowerRename
- Enhanced PowerRename's time formatting capabilities by adding 12-hour time format patterns with AM/PM support. Thanks [@bitmap4](https://github.com/bitmap4)!
- Supported negative value as Start value in regular expression, e.g. ${start=-1314}
- Enhanced RegEx help by adding $, ^, quantifiers, and common patterns for better usability. Thanks [@PesBandi](https://github.com/PesBandi) and thanks [@htcfreek](https://github.com/htcfreek) for review.
### PowerToys Run
- Added support for custom formats in the "Time and Date" plugin and improves error messages for invalid input formats. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fix two crashes: one for WFT on very early dates and another for calculating the week of the month on very late dates (e.g., 31.12.9999), and reorder UI settings. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fix an issue where capitalization in the command causes failure when trying to go to the mouse pointer, resolved by adjusting the command to lowercase.
- Added version details to plugin error messages for 'Loading error' and 'Init error'. Thanks [@htcfreek](https://github.com/htcfreek)!
- Enhanced result model by adding support for preventing usage-based ordering, giving plugin developers greater control over sorting behavior. Thanks [@CoreyHayward](https://github.com/CoreyHayward) and [@htcfreek](https://github.com/htcfreek)!
- Fixed crashes when loading thumbnails after the .NET 9 update by disabling CETCompat.
- Fixed PowerLauncher.exe blocking other MSI installers creating shortcuts. Thanks [@OneBlue](https://github.com/OneBlue)!
- Fixed Runs dark mode detection to work reliably, preventing issues with incorrect theme detection and ensuring a smoother user experience. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed list separator handling in Calculator, allowing functions with multiple arguments to work correctly across different locales. For example pow(2;3) would be replaced with pow(2,3). Thanks [@PesBandi](https://github.com/PesBandi) and thanks [@htcfreek](https://github.com/htcfreek) for review!
- Fixed angle unit conversions in the PowerToys Run calculator, allowing quick conversions between radians, degrees, and gradians. Thanks [@OldUser101](https://github.com/OldUser101)!
### Quick Accent
- Updated the letter mapping in GetDefaultLetterKeyEPO, replacing "ǔ" with "ŭ" for the VK_U key to accurately reflect Esperanto phonetics. Thanks [@OlegKharchevkin](https://github.com/OlegKharchevkin)!
- Fixed an issue where Quick Accent did not work properly when using the on-screen keyboard. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
### Registry Preview
- Enhanced Registry Preview to support pasting registry keys and values without manually writing the file header, and added a new button for resetting the app. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added ǎ, ǒ and ǔ to the IPA character set. Thanks [@PesBandi](https://github.com/PesBandi)!
- Added ` (backtick) and ~ (tilde) to the VK_OEM_5 character set. Thanks [@xanatos](https://github.com/xanatos)!
- Added ς (final sigma) to the Greek character set. Thanks [@IamSmeagol](https://github.com/IamSmeagol)!
### Settings
- Fix an issue where the Settings app randomly showed a blank icon in the taskbar by deferring icon assignment until the window is activated.
- Added the ability to maximize the "What's New" window for a more comfortable reading experience.
- Enabled GPO for the "run at startup" setting. Thanks [@htcfreek](https://github.com/htcfreek) for review and comments!
- Fixed accessibility issue by allowing screen readers to announce the group name for secondary links in Settings pages, instead of reading link descriptions without context.
- Fixed an issue where the Color Picker shortcut was not displaying correctly in the Dashboard.
### Workspaces
- Fixed bugs where Steam games were not captured or launched correctly by updating window filtering and integrating Steam URL protocol handling.
- Fixed if a window was last placed on a disconnected monitor, it launches minimized and repositions within the main monitor's visible area when restored, instead of remaining off-screen and invisible.
- Fixed on ARM64 to correctly display icons for packaged apps by resolving path mismatches.
### ZoomIt
- Fixed warning C4706 and related error C2220 during build. Thanks [@xanatos](https://github.com/xanatos)!
### Documentation
- Added QuickNotes to the third-party plugins documentation for PowerToys Run. Thanks [@ruslanlap](https://github.com/ruslanlap)!
- Added Weather and Pomodoro plugins to the PowerToys Run third-party plugin documentation. Thanks [@ruslanlap](https://github.com/ruslanlap)!
- Added the Linear plugin to PowerToys Run's third-party plugin documentation. Thanks [@vednig](https://github.com/vednig)!
- Fixed formatting issues in documentation files and updated contributor and team member information. Thanks [@DanielEScherzer](https://github.com/DanielEScherzer) and [@RokyZevon](https://github.com/RokyZevon)!
- Fixed runner-ipc.md doc on the broken link. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed the new plugin checklist by updating the target framework, removing duplicates, and improving statement organization. Thanks [@hlaueriksson](https://github.com/hlaueriksson)!
- Updated runner documentation to align with the latest code structure.
### Development
- Updated GitHub Action to install .NET 9 for MSStore release support.
- Updated version placeholder in bug_report.yml to prevent incorrect v0.70.0 versioning in issue reports.
- Updated GitHub Action to upgrade actions/setup-dotnet from version 3 to version 4 for MSStore release.
- Added securityContext to WinGet configuration files, allowing invocation from user context and prompting a single UAC for elevated resources in a separate process. Thanks [@mdanish-kh](https://github.com/mdanish-kh)!
- Changed log file extensions from .txt to .log to support proper file associations and tooling compatibility, and added logs for Workspace. Thanks [@benwa](https://github.com/benwa)!
- Upgraded testing framework dependencies and aligned package versions across components.
- Upgraded dependencies to fix vulnerabilities.
- Enhanced repository security by pinning GitHub Actions and Docker tags to immutable full-length commits and integrating automated dependency vulnerability scanning via Dependency Review Workflow. Thanks [@Nick2bad4u](https://github.com/Nick2bad4u)!
- Upgraded Boost dependencies to a newer version.
- Upgraded toolkit to the latest version, suppressed AoT-related warnings.
- Fixed an issue where missing signing for newly added files caused build failures.
- Update release pipeline to prevent publishing private symbols for 100 years.
- Introduced fuzzing for PowerRename to improve reliability and added setup guidance for extending fuzzing to other C++ modules.
- Added centralized pre-creation of generated folders for all .csproj projects to prevent build failures.
- Updated WinAppSDK to the latest 1.7 version.
- Upgraded Boost dependencies to the latest version for the PowerRename Fuzzing project.
- Updated the ADO area path in tsa.json to resolve TSA pipeline errors caused by a deprecated path.
- Initiated AoT support for CmdPal with foundational work in progress.
### Tool/General
- Stabilized pipeline on ARM64 and forked build.
- Added fuzz testing for HostUILib, added as part of pipeline for OneFuzz.
- Fixed and improved UI-Test automation framework, and added new test cases for the FancyZones and Hosts module.
- Optimized Logger function as AOT compatible, improving performance by 18%.
- Made Common.UI and Setting.UI to be AOT compatible.
### What is being planned for version 0.90
- Added support for automating bug report creation by generating a pre-filled GitHub issue URL with system and diagnostic information. Thanks [@donlaci](https://github.com/donlaci)!
- Added scripts to locally build the installer, ensuring the CmdPal can also be launched in a local environment.
- Removed export PFX logic to eliminate hardcoded password usage and resolve PSScriptAnalyzer security warning.
- Added PowerShell script and CI integration to enforce consistent use of Common.Dotnet.CsWinRT.props across all C# projects under the src folder.
### What is being planned for version 0.92
For [v0.90][github-next-release-work], we'll work on the items below:
For [v0.92][github-next-release-work], we'll work on the items below:
- Continued Command Palette polish
- New UI Automation tests
- New module: PowerToys Run v2
- New module: File Actions Menu
- Working on installer upgrades
- Upgrading Keyboard Manager's editor UI
- Upgrading keyboard manager's editor UI
- Stability / bug fixes
## PowerToys Community

View File

@@ -1,117 +0,0 @@
# 🧪 C++ Project Fuzzing Test Guide
This guide walks you through setting up a **fuzzing test** project for a C++ module using [libFuzzer](https://llvm.org/docs/LibFuzzer.html).
.
---
## 🏗️ Step-by-Step Setup
### 1. Create a New C++ Project
- Use **Empty Project** template.
- Name it `<ModuleName>.FuzzingTest`.
---
### 2. Update Build Configuration
- In **Configuration Manager**, Uncheck Build for both Release|ARM64, Debug|ARM64 and Debug|x64 configurations.
- Note: ARM64 is not supported in this case, so leave ARM64 configurations build disabled.
---
### 3. Enable ASan and libFuzzer in `.vcxproj`
Edit the project file to enable fuzzing:
```xml
<PropertyGroup>
<EnableASAN>true</EnableASAN>
<EnableFuzzer>true</EnableFuzzer>
</PropertyGroup>
```
---
### 4. Add Fuzzing Compiler Flags
Add this to `AdditionalOptions` under the `Fuzzing` configuration:
```xml
/fsanitize=address
/fsanitize-coverage=inline-8bit-counters
/fsanitize-coverage=edge
/fsanitize-coverage=trace-cmp
/fsanitize-coverage=trace-div
%(AdditionalOptions)
```
---
### 5. Link the Sanitizer Coverage Runtime
In `Linker → Input → Additional Dependencies`, add:
```text
$(VCToolsInstallDir)lib\$(Platform)\libsancov.lib
```
---
### 6. Copy Required Runtime DLL
Add a `PostBuildEvent` to copy the ASAN DLL:
```xml
<Command>
xcopy /y "$(VCToolsInstallDir)bin\Hostx64\x64\clang_rt.asan_dynamic-x86_64.dll" "$(OutDir)"
</Command>
```
---
### 7. Add Preprocessor Definitions
To avoid annotation issues, add these to the `Preprocessor Definitions`:
```text
_DISABLE_VECTOR_ANNOTATION;_DISABLE_STRING_ANNOTATION
```
---
## 🧬 Required Code
### `LLVMFuzzerTestOneInput` Entry Point
Every fuzzing project must expose this function:
```cpp
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
std::string input(reinterpret_cast<const char*>(data), size);
try
{
// Call your module with the input here.
}
catch (...) {}
return 0;
}
```
---
## ⚙️ [Test run in the cloud](https://eng.ms/docs/cloud-ai-platform/azure-edge-platform-aep/aep-security/epsf-edge-and-platform-security-fundamentals/the-onefuzz-service/onefuzz/faq/notwindows/walkthrough)
To submit a job to the cloud you can run with this command:
```
oip submit --config .\OneFuzzConfig.json --drop-path <your_submission_directory> --platform windows --do-not-file-bugs --duration 1
```
You want to run with --do-not-file-bugs because if there is an issue with running the parser in the cloud (which is very possible), you don't want bugs to be created if there is an issue. The --duration task is the number of hours you want the task to run. I recommend just running for 1 hour to make sure things work initially. If you don't specify this parameter, it will default to 48 hours. You can find more about submitting a test job here.
OneFuzz will send you an email when the job has started.
---

View File

@@ -21,78 +21,71 @@
- Create a new project and add the following references to the project file. Change the OutputPath to your own module's path.
```
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<ProjectGuid>{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0}</ProjectGuid>
<RootNamespace>PowerToys.Hosts.UITests</RootNamespace>
<AssemblyName>PowerToys.Hosts.UITests</AssemblyName>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<!-- This is a UI test, so don't run as part of MSBuild -->
<RunVSTest>false</RunVSTest>
</PropertyGroup>
<PropertyGroup>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\tests\Hosts.UITests\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest" />
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
</ItemGroup>
</Project>
<PropertyGroup>
<OutputType>Library</OutputType>
<!-- This is a UI test, so don't run as part of MSBuild -->
<RunVSTest>false</RunVSTest>
</PropertyGroup>
<PropertyGroup>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\tests\KeyboardManagerUITests\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest" />
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
<Folder Include="Properties\" />
</ItemGroup>
```
- Inherit your test class from UITestBase.
>Set Scope: The default scope starts from the PowerToys settings UI. If you want to start from your own module, set the constructor as shown below:
>Specify Scope:
```
[TestClass]
public class HostModuleTests : UITestBase
{
public HostModuleTests()
: base(PowerToysModule.Hosts, WindowSize.Small_Vertical)
{
}
}
[TestClass]
public class RunFancyZonesTest : UITestBase
{
public RunFancyZonesTest()
: base(PowerToysModule.FancyZone)
{
}
}
```
- Then you can start performing the UI operations.
- Then you can start using session to perform the UI operations.
**Example**
```
[TestMethod("Hosts.Basic.EmptyViewShouldWork")]
[TestCategory("Hosts File Editor #4")]
public void TestEmptyView()
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UITests_KeyboardManager
{
this.CloseWarningDialog();
this.RemoveAllEntries();
[TestClass]
public class RunKeyboardManagerUITests : UITestBase
{
[TestMethod]
public void OpenKeyboardManagerEditor()
{
// Open KeyboardManagerEditor
this.Session.Find<Button>(By.Name("Remap a key")).Click();
this.Session.Attach("Remap keys");
// 'Add an entry' button (only show-up when list is empty) should be visible
Assert.IsTrue(this.HasOne<HyperlinkButton>("Add an entry"), "'Add an entry' button should be visible in the empty view");
// Maximize window
var window = Session.Find<Window>(By.Name("Remap keys")).Maximize();
VisualAssert.AreEqual(this.TestContext, this.Find("Entries"), "EmptyView");
// Add Key Remapping
this.Session.Find<Button>(By.Name("Add key remapping")).Click();
window.Close();
// Click 'Add an entry' from empty-view for adding Host override rule
this.Find<HyperlinkButton>("Add an entry").Click();
this.AddEntry("192.168.0.1", "localhost", false, false);
// Should have one row now and not more empty view
Assert.IsTrue(this.Has<Button>("Delete"), "Should have one row now");
Assert.IsFalse(this.Has<HyperlinkButton>("Add an entry"), "'Add an entry' button should be invisible if not empty view");
VisualAssert.AreEqual(this.TestContext, this.Find("Entries"), "NonEmptyView");
// Back to Settings
this.Session.Attach(PowerToysModule.PowerToysSettings);
}
}
}
```
## Extra tools and information
**Accessibility Tools**:
While working on tests, you may need a tool that helps you to view the element's accessibility data, e.g. for finding the button to click. For this purpose, you could use [AccessibilityInsights](https://accessibilityinsights.io/docs/windows/overview).
While working on tests, you may need a tool that helps you to view the element's accessibility data, e.g. for finding the button to click. For this purpose, you could use [AccessibilityInsights](https://accessibilityinsights.io/docs/windows/overview)

View File

@@ -16,70 +16,49 @@ The 'Time and Date' plugin shows the date and time in different formats. For the
### Available formats
**Remarks**
- The following formats requires a prefix in the query when using them as date input:
- The following formats requires a prefix in the query:
- Unix Timestamp: `u`
- Unix Timestamp in milliseconds: `ums`
- Windows file time: `ft`
- OLE Automation date: `oa`
- Excel 1900 date value: `exc`
- Excel 1904 date value: `exf`
- On invalid number inputs we show a warning that tells the user which prefixes are allowed/required.
**List of available formats**
The following formats are currently available:
| Format | Example (Based on default settings) | As result | As input | Result as custom format only
| Format | Example (Based on default settings) | As result | As input |
|--------------|-----------|------------|------------|
| Time | 5:10 PM | x | x | |
| Date | 3/5/2022 | x | x | |
| Now | 3/5/2022 5:10 PM | x | x | |
| Time UTC | 4:10 PM | x | x | |
| Now UTC | 3/5/2022 4:10 PM | x | x | |
| Unix Timestamp | 1646496622 | x | x | |
| Unix Timestamp in milliseconds | 1646496622500 | x | x | |
| Hour | 10 | x | | |
| Minute | 30 | x | | |
| Second | 45 | x | | |
| Millisecond | 678 | x | | |
| Day (Week day) | Saturday | x | | |
| Day of the week | 6 | x | | |
| Day of the month | 5 | x | | |
| Day of the year | 64 | x | | |
| Week of the month | 1 | x | | |
| Week of the year (Calendar week, Week number) | 10 | x | | |
| Month | March | x | | |
| Month of the year | 3 | x | | |
| Month and day | March 7 | x | x | |
| Year | 2022 | x | | |
| Era | AD | x | | |
| Era abbreviation | A | x | | |
| Month and year | March 2022 | x | x | |
| Windows file time (Int64 number) | 637820976123938199 | x | x | |
| Universal time format: YYYY-MM-DD hh:mm:ss| 2022-03-05 16:20:12Z | x | x | |
| ISO 8601 | 2022-03-05T17:23:04 | x | x | |
| ISO 8601 UTC | 2022-03-05T16:23:04 | x | x | |
| ISO 8601 with time zone | 2022-03-05T17:23:04+01:00 | x | x | |
| ISO 8601 UTC with time zone | 2022-03-05T16:23:04Z | x | x | |
| RFC1123 | Sat, 05 Mar 2022 16:23:04 GMT | x | x | |
| OLE Automation date | 45723.44143763889 | | x | x |
| Excel's 1900 date value | 45723.44143763889 | | x | x |
| Excel's 1904 date value | 44261.44143763889 | | x | x |
**Custom format definition**
The user can create its own formats. One per line in the settings text box. The format of each line is `<name>=<syntax pattern>`.
If the syntax pattern starting with `UTC:` then we use the UTC time instead of the local time.
As syntax pattern the pattern from `DateTime.ToString()` and the following custom pattern are available:
- DOW: Number of the day in the week.
- WOM: Number of week in the month.
- WOY: Number of the week in the year.
- EAB: Era abbreviation.
- WFT: Windows file time.
- UXT: Unix time stamp.
- UMS: Unix time stamp in milliseconds.
- OAD: OLE Automation date.
- EXC: Excel's 1900 based date value.
- EXF: Excel's 1904 based date value.
| Time | 5:10 PM | x | x |
| Date | 3/5/2022 | x | x |
| Now | 3/5/2022 5:10 PM | x | x |
| Time UTC | 4:10 PM | x | x |
| Now UTC | 3/5/2022 4:10 PM | x | x |
| Unix Timestamp | 1646496622 | x | x |
| Unix Timestamp in milliseconds | 1646496622500 | x | x |
| Hour | 10 | x | |
| Minute | 30 | x | |
| Second | 45 | x | |
| Millisecond | 678 | x | |
| Day (Week day) | Saturday | x | |
| Day of the week | 6 | x | |
| Day of the month | 5 | x | |
| Day of the year | 64 | x | |
| Week of the month | 1 | x | |
| Week of the year (Calendar week, Week number) | 10 | x | |
| Month | March | x | |
| Month of the year | 3 | x | |
| Month and day | March 7 | x | x |
| Year | 2022 | x | |
| Era | AD | x | |
| Era abbreviation | A | x | |
| Month and year | March 2022 | x | x |
| Windows file time (Int64 number) | 637820976123938199 | x | x |
| Universal time format: YYYY-MM-DD hh:mm:ss| 2022-03-05 16:20:12Z | x | x |
| ISO 8601 | 2022-03-05T17:23:04 | x | x |
| ISO 8601 UTC | 2022-03-05T16:23:04 | x | x |
| ISO 8601 with time zone | 2022-03-05T17:23:04+01:00 | x | x |
| ISO 8601 UTC with time zone | 2022-03-05T16:23:04Z | x | x |
| RFC1123 | Sat, 05 Mar 2022 16:23:04 GMT | x | x |
### Add new formats
- To add a new formats you have to add them to the method `GetList()` of the [`AvailableResultsList`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs) class.
@@ -94,13 +73,13 @@ As syntax pattern the pattern from `DateTime.ToString()` and the following custo
| Key | Type | Default value | Name | Description |
|--------------|--------------|-----------|------------|------------|
| `CalendarFirstWeekRule` | Combo box | `-1` (Use system settings) | First week of the year | Configure the calendar rule for the first week of the year. |
| `FirstDayOfWeek` | Combo box | `-1` (Use system settings) | First day of the week | |
| `OnlyDateTimeNowGlobal` | Checkbox | `true` | Show only 'Time', 'Date', and 'Now' result for system time on global queries | Regardless of this setting, for global queries the first word of the query has to be a complete match. |
| `TimeWithSeconds` | Checkbox | `false` | Show time with seconds | This setting applies to the 'Time' and 'Now' result. |
| `DateWithWeekday` | Checkbox | `false` | Show date with weekday and name of month | This setting applies to the 'Date' and 'Now' result. |
| `HideNumberMessageOnGlobalQuery` | Checkbox | `false` | Hide 'Invalid number input' error message on global queries | |
| `CalendarFirstWeekRule` | Combo box | `-1` (Use system settings) | First week of the year | Configure the calendar rule for the first week of the year. |
| `FirstDayOfWeek` | Combo box | `-1` (Use system settings) | First day of the week | |
| `CustomFormats` | Multiline text box | `string.Empty` | Custom formats | Use date and time string format syntax and DOW (Day of Week), WOM (Week of Month), WOY (Week of the year), EAB (Era abbreviation), WFT (Windows File Time), UXT (Unix Time), UMS (Unix Time in milliseconds), OAD (OLE Automation date), EXC (Excel's 1900 based date value), EXF (Excel's 1904 based date value). If the format starts with UTC:, then Universal Time (UTC) is used. (Use a backslash to escape format sequences and the backslash character as text.) |
## Classes

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 MiB

View File

@@ -42,10 +42,6 @@ Contact the developers of a plugin directly for assistance with a specific plugi
| [CanIUse](https://github.com/skttl/ptrun-caniuse) | [skttl](https://github.com/skttl) | Look up browser feature support with caniuse.com |
| [TailwindCSS](https://github.com/skttl/ptrun-tailwindcss) | [skttl](https://github.com/skttl) | Search the documentation of TailwindCSS |
| [HttpStatusCodes](https://github.com/grzhan/HttpStatusCodePowerToys) | [grzhan](https://github.com/grzhan) | Search for http status codes |
| [SVGL](https://github.com/Sameerjs6/powertoys-svgl) | [SameerJS6](https://github.com/SameerJS6) | Search, Browse and copy SVG logos from SVGL. |
| [QuickNotes](https://github.com/ruslanlap/CommunityPowerToysRunPlugin-QuickNotes) | [ruslanlap](https://github.com/ruslanlap) | Create, manage, and search notes directly from PowerToys Run. |
| [Weather](https://github.com/ruslanlap/PowerToysRun-Weather) | [ruslanlap](https://github.com/ruslanlap) | Get real-time weather information directly from PowerToys Run. |
| [Pomodoro](https://github.com/ruslanlap/PowerToysRun-Pomodoro) | [ruslanlap](https://github.com/ruslanlap) | Manage Pomodoro productivity sessions directly from PowerToys Run. |
## Extending software plugins
@@ -67,5 +63,3 @@ Below are community created plugins that target a website or software. They are
| [SSH](https://github.com/8LWXpg/PowerToysRun-SSH) | [8LWXpg](https://github.com/8LWXpg) | Connect to ssh clients |
| [Bilibili](https://github.com/Whuihuan/PowerToysRun-Bilibili) | [Whuihuan](https://github.com/Whuihuan) | Use AVID or BVID to parse and jump to Bilibili |
| [YubicoOauthOTP](https://github.com/dlnilsson/Community.PowerToys.Run.Plugin.YubicoOauthOTP) | [dlnilsson](https://github.com/dlnilsson) | Display generated codes from OATH accounts stored on the YubiKey in powerToys Run |
| [Firefox Bookmark](https://github.com/8LWXpg/PowerToysRun-FirefoxBookmark) | [8LWXpg](https://github.com/8LWXpg) | Open bookmarks in Firefox based browser |
[Linear](https://github.com/vednig/powertoys-linear) | [vednig](https://github.com/vednig) | Create Linear Issues directly from Powertoys Run |

View File

@@ -1,74 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<?include $(sys.CURRENTDIR)\Common.wxi?>
<?define CmdPalBuildDir="$(var.BinDir)\WinUI3Apps\CmdPal\"?>
<Fragment>
<DirectoryRef Id="WinUI3AppsInstallFolder">
<Directory Id="CmdPalInstallFolder" Name="CmdPal">
<Directory Id="CmdPalDepsInstallFolder" Name="Dependencies">
<?if $(sys.BUILDARCH) = x64 ?>
<Directory Id="CmdPalDepsX64InstallFolder" Name="x64" />
<?else ?>
<Directory Id="CmdPalDepsArm64InstallFolder" Name="arm64" />
<?endif ?>
</Directory>
</Directory>
</DirectoryRef>
<DirectoryRef Id="CmdPalInstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test">
<Component Id="Module_CmdPal" Win64="yes" Guid="3A4942B2-1A86-4182-B3B4-65157365A980">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_CmdPal" Value="" KeyPath="yes"/>
</RegistryKey>
<?if $(sys.BUILDARCH) = x64 ?>
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_x64.msix" />
<?else ?>
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_arm64.msix" />
<?endif ?>
</Component>
</DirectoryRef>
<?if $(sys.BUILDARCH) = x64 ?>
<DirectoryRef Id="CmdPalDepsX64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64">
<Component Id="Module_CmdPal_Deps" Win64="yes" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes"/>
</RegistryKey>
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" />
</Component>
</DirectoryRef>
<?else ?>
<DirectoryRef Id="CmdPalDepsArm64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64">
<Component Id="Module_CmdPal_Deps" Win64="yes" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes"/>
</RegistryKey>
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64\Microsoft.VCLibs.ARM64.14.00.Desktop.appx" />
</Component>
</DirectoryRef>
<?endif ?>
<ComponentGroup Id="CmdPalComponentGroup">
<Component Id="RemoveCmdPalFolder" Guid="2DF90C08-CC75-4245-A14E-B82904636C53" Directory="INSTALLFOLDER">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="RemoveCmdPalFolder" Value="" KeyPath="yes"/>
</RegistryKey>
<RemoveFolder Id="RemoveCmdPalInstallDirFolder" Directory="CmdPalInstallFolder" On="uninstall"/>
<RemoveFolder Id="RemoveCmdPalDepsInstallDirFolder" Directory="CmdPalDepsInstallFolder" On="uninstall"/>
<?if $(sys.BUILDARCH) = x64 ?>
<RemoveFolder Id="RemoveCmdPalDepsX64InstallDirFolder" Directory="CmdPalDepsX64InstallFolder" On="uninstall"/>
<?else ?>
<RemoveFolder Id="RemoveCmdPalDepsArm64InstallDirFolder" Directory="CmdPalDepsArm64InstallFolder" On="uninstall"/>
<?endif ?>
</Component>
<ComponentRef Id="Module_CmdPal" />
<ComponentRef Id="Module_CmdPal_Deps" />
</ComponentGroup>
</Fragment>
</Wix>

View File

@@ -17,12 +17,6 @@
<PropertyGroup Label="UserMacros" Condition=" '$(PerUser)' != 'true' ">
<DefineConstants>$(DefineConstants);PerUser=false</DefineConstants>
</PropertyGroup>
<PropertyGroup Label="UserMacros" Condition=" '$(CIBuild)' == 'true' ">
<DefineConstants>$(DefineConstants);CIBuild=true</DefineConstants>
</PropertyGroup>
<PropertyGroup Label="UserMacros" Condition=" '$(CIBuild)' != 'true' ">
<DefineConstants>$(DefineConstants);CIBuild=false</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform Condition="'$(Platform)'=='x64'">x64</Platform>

View File

@@ -1,11 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureNuGetPackageBuildImports"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureNuGetPackageBuildImports" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\src\Version.props" Condition="Exists('..\..\src\Version.props')" />
<Import Project="..\..\src\CmdPalVersion.props" Condition="Exists('..\..\src\CmdPalVersion.props')" />
<Import Project="..\wix.props" Condition="Exists('..\wix.props')" />
<PropertyGroup Condition="'$(Platform)' == 'x64'">
<DefineConstants>Version=$(Version);MonacoSRCHarvestPath=$(ProjectDir)..\..\x64\$(Configuration)\Assets\Monaco\monacoSRC;CmdPalVersion=$(CmdPalVersion)</DefineConstants>
<DefineConstants>Version=$(Version);MonacoSRCHarvestPath=$(ProjectDir)..\..\x64\$(Configuration)\Assets\Monaco\monacoSRC</DefineConstants>
<!-- THIS IS AN INNER LOOP OPTIMIZATION
The build pipeline builds the Settings and Launcher projects for Publication
using a specific profile. If you're doing local installer builds, this will
@@ -19,7 +17,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
</PreBuildEvent>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)' != 'x64'">
<DefineConstants>Version=$(Version);MonacoSRCHarvestPath=$(ProjectDir)..\..\ARM64\$(Configuration)\Assets\Monaco\monacoSRC;CmdPalVersion=$(CmdPalVersion);</DefineConstants>
<DefineConstants>Version=$(Version);MonacoSRCHarvestPath=$(ProjectDir)..\..\ARM64\$(Configuration)\Assets\Monaco\monacoSRC</DefineConstants>
<PreBuildEvent>IF NOT DEFINED IsPipeline (
call "$([MSBuild]::GetVsInstallRoot())\Common7\Tools\VsDevCmd.bat" -arch=arm64 -host_arch=amd64 -winsdk=10.0.19041.0 -vcvars_ver=$(VCToolsVersion)
SET PTRoot=$(SolutionDir)\..
@@ -34,7 +32,6 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
call move /Y ..\..\..\AdvancedPaste.wxs.bk ..\..\..\AdvancedPaste.wxs
call move /Y ..\..\..\Awake.wxs.bk ..\..\..\Awake.wxs
call move /Y ..\..\..\BaseApplications.wxs.bk ..\..\..\BaseApplications.wxs
call move /Y ..\..\..\CmdPal.wxs.bk ..\..\..\CmdPal.wxs
call move /Y ..\..\..\ColorPicker.wxs.bk ..\..\..\ColorPicker.wxs
call move /Y ..\..\..\Core.wxs.bk ..\..\..\Core.wxs
call move /Y ..\..\..\EnvironmentVariables.wxs.bk ..\..\..\EnvironmentVariables.wxs
@@ -57,7 +54,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
call move /Y ..\..\..\WinAppSDK.wxs.bk ..\..\..\WinAppSDK.wxs
call move /Y ..\..\..\WinUI3Applications.wxs.bk ..\..\..\WinUI3Applications.wxs
call move /Y ..\..\..\Workspaces.wxs.bk ..\..\..\Workspaces.wxs
</PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<PropertyGroup>
<Name>PowerToysInstaller</Name>
@@ -68,12 +65,6 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
<PropertyGroup Label="UserMacros" Condition=" '$(PerUser)' != 'true' ">
<DefineConstants>$(DefineConstants);PerUser=false</DefineConstants>
</PropertyGroup>
<PropertyGroup Label="UserMacros" Condition=" '$(CIBuild)' == 'true' ">
<DefineConstants>$(DefineConstants);CIBuild=true</DefineConstants>
</PropertyGroup>
<PropertyGroup Label="UserMacros" Condition=" '$(CIBuild)' != 'true' ">
<DefineConstants>$(DefineConstants);CIBuild=false</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<!-- We do not support debug installer builds -->
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
@@ -113,7 +104,6 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
<Compile Include="AdvancedPaste.wxs" />
<Compile Include="Awake.wxs" />
<Compile Include="BaseApplications.wxs" />
<Compile Include="CmdPal.wxs" />
<Compile Include="ColorPicker.wxs" />
<Compile Include="EnvironmentVariables.wxs" />
<Compile Include="FileExplorerPreview.wxs" />
@@ -198,4 +188,4 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
</ItemGroup>
</Target>
<Target Name="Restore" />
</Project>
</Project>

View File

@@ -79,7 +79,6 @@
<ComponentGroupRef Id="ToolComponentGroup" />
<ComponentGroupRef Id="MonacoSRCHeatGenerated" />
<ComponentGroupRef Id="WorkspacesComponentGroup" />
<ComponentGroupRef Id="CmdPalComponentGroup" />
</Feature>
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLFOLDER]" After="CostFinalize" />
@@ -136,7 +135,6 @@
<InstallExecuteSequence>
<Custom Action="DetectPrevInstallPath" After="AppSearch" />
<Custom Action="SetLaunchPowerToysParam" Before="LaunchPowerToys" />
<Custom Action="SetInstallCmdPalPackageParam" Before="InstallCmdPalPackage" />
<Custom Action="SetUninstallCommandNotFoundParam" Before="UninstallCommandNotFound" />
<Custom Action="SetUpgradeCommandNotFoundParam" Before="UpgradeCommandNotFound" />
<Custom Action="SetApplyModulesRegistryChangeSetsParam" Before="ApplyModulesRegistryChangeSets" />
@@ -152,9 +150,6 @@
<Custom Action="ApplyModulesRegistryChangeSets" After="InstallFiles">
NOT Installed
</Custom>
<Custom Action="InstallCmdPalPackage" After="InstallFiles">
NOT Installed
</Custom>
<Custom Action="WixCloseApplications" Before="RemoveFiles" />
<Custom Action="RemovePowerToysSchTasks" After="RemoveFiles" />
<!-- TODO: Use to activate embedded MSIX -->
@@ -176,9 +171,6 @@
<Custom Action="UnRegisterContextMenuPackages" Before="RemoveFiles">
Installed AND (REMOVE="ALL")
</Custom>
<Custom Action="UnRegisterCmdPalPackage" Before="RemoveFiles">
Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
</Custom>
<Custom Action="UnsetAdvancedPasteAPIKey" Before="RemoveFiles">
Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
</Custom>
@@ -212,10 +204,6 @@
Property="LaunchPowerToys"
Value="[INSTALLFOLDER]" />
<CustomAction Id="SetInstallCmdPalPackageParam"
Property="InstallCmdPalPackage"
Value="[INSTALLFOLDER]" />
<CustomAction
Id="LaunchPowerToys"
Return="ignore"
@@ -439,14 +427,6 @@
DllEntry="UnRegisterContextMenuPackagesCA"
/>
<CustomAction Id="UnRegisterCmdPalPackage"
Return="ignore"
Impersonate="yes"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="UnRegisterCmdPalPackageCA"
/>
<CustomAction Id="CheckGPO"
Return="check"
Impersonate="yes"
@@ -454,14 +434,6 @@
DllEntry="CheckGPOCA"
/>
<CustomAction Id="InstallCmdPalPackage"
Return="ignore"
Impersonate="yes"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="InstallCmdPalPackageCA"
/>
<!-- Close 'PowerToys.exe' before uninstall-->
<Property Id="MSIRESTARTMANAGERCONTROL" Value="DisableShutdown" />
<Property Id="MSIFASTINSTALL" Value="DisableShutdown" />

View File

@@ -11,7 +11,6 @@
#include "../../src/common/updating/installer.h"
#include "../../src/common/version/version.h"
#include "../../src/common/Telemetry/EtwTrace/EtwTrace.h"
#include "../../src/common/utils/package.h"
#include "../../src/common/utils/clean_video_conference.h"
#include <winrt/Windows.ApplicationModel.h>
@@ -36,13 +35,13 @@ TRACELOGGING_DEFINE_PROVIDER(
TraceLoggingOptionProjectTelemetry());
const DWORD USERNAME_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0'
const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
static const wchar_t *POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
static const wchar_t *POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
static const wchar_t* POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
static const wchar_t* POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
constexpr inline const wchar_t *DataDiagnosticsRegKey = L"Software\\Classes\\PowerToys";
constexpr inline const wchar_t *DataDiagnosticsRegValueName = L"AllowDataDiagnostics";
constexpr inline const wchar_t* DataDiagnosticsRegKey = L"Software\\Classes\\PowerToys";
constexpr inline const wchar_t* DataDiagnosticsRegValueName = L"AllowDataDiagnostics";
#define TraceLoggingWriteWrapper(provider, eventName, ...) \
if (isDataDiagnosticEnabled()) \
@@ -53,16 +52,16 @@ constexpr inline const wchar_t *DataDiagnosticsRegValueName = L"AllowDataDiagnos
trace.UpdateState(false); \
}
static Shared::Trace::ETWTrace trace{L"PowerToys_Installer"};
static Shared::Trace::ETWTrace trace{ L"PowerToys_Installer" };
inline bool isDataDiagnosticEnabled()
{
HKEY key{};
if (RegOpenKeyExW(HKEY_CURRENT_USER,
DataDiagnosticsRegKey,
0,
KEY_READ,
&key) != ERROR_SUCCESS)
DataDiagnosticsRegKey,
0,
KEY_READ,
&key) != ERROR_SUCCESS)
{
return false;
}
@@ -87,7 +86,8 @@ inline bool isDataDiagnosticEnabled()
return isDataDiagnosticsEnabled == 1;
}
HRESULT getInstallFolder(MSIHANDLE hInstall, std::wstring &installationDir)
HRESULT getInstallFolder(MSIHANDLE hInstall, std::wstring& installationDir)
{
DWORD len = 0;
wchar_t _[1];
@@ -116,13 +116,13 @@ BOOL IsLocalSystem()
// open process token
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_QUERY,
&hToken))
TOKEN_QUERY,
&hToken))
return FALSE;
// retrieve user SID
if (!GetTokenInformation(hToken, TokenUser, pTokenUser,
sizeof(bTokenUser), &cbTokenUser))
sizeof(bTokenUser), &cbTokenUser))
{
CloseHandle(hToken);
return FALSE;
@@ -132,7 +132,7 @@ BOOL IsLocalSystem()
// allocate LocalSystem well-known SID
if (!AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0, &pSystemSid))
0, 0, 0, 0, 0, 0, 0, &pSystemSid))
return FALSE;
// compare the user SID from the token with the LocalSystem SID
@@ -194,7 +194,7 @@ static std::filesystem::path GetUserPowerShellModulesPath()
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &myDocumentsBlockPtr)))
{
const std::wstring myDocuments{myDocumentsBlockPtr};
const std::wstring myDocuments{ myDocumentsBlockPtr };
CoTaskMemFree(myDocumentsBlockPtr);
return std::filesystem::path(myDocuments) / "PowerShell" / "Modules";
}
@@ -227,12 +227,10 @@ UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall)
BOOL isSystemUser = IsLocalSystem();
if (isSystemUser)
{
if (isSystemUser) {
auto action = [&commandLine](HANDLE userToken)
{
STARTUPINFO startupInfo{.cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL};
auto action = [&commandLine](HANDLE userToken) {
STARTUPINFO startupInfo{ .cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL };
PROCESS_INFORMATION processInformation;
PVOID lpEnvironment = NULL;
@@ -271,7 +269,7 @@ UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall)
}
else
{
STARTUPINFO startupInfo{.cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL};
STARTUPINFO startupInfo{ .cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL };
PROCESS_INFORMATION processInformation;
@@ -315,7 +313,7 @@ UINT __stdcall CheckGPOCA(MSIHANDLE hInstall)
LPWSTR currentScope = nullptr;
hr = WcaGetProperty(L"InstallScope", &currentScope);
if (std::wstring{currentScope} == L"perUser")
if (std::wstring{ currentScope } == L"perUser")
{
if (powertoys_gpo::getDisablePerUserInstallationValue() == powertoys_gpo::gpo_rule_configured_enabled)
{
@@ -356,7 +354,7 @@ UINT __stdcall ApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installFolder.");
for (const auto &changeSet : getAllOnByDefaultModulesChangeSets(installationFolder))
for (const auto& changeSet : getAllOnByDefaultModulesChangeSets(installationFolder))
{
if (!changeSet.apply())
{
@@ -384,7 +382,7 @@ UINT __stdcall UnApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
ExitOnFailure(hr, "Failed to initialize");
hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installFolder.");
for (const auto &changeSet : getAllModulesChangeSets(installationFolder))
for (const auto& changeSet : getAllModulesChangeSets(installationFolder))
{
changeSet.unApply();
}
@@ -398,8 +396,8 @@ LExit:
return WcaFinalize(er);
}
const wchar_t *DSC_CONFIGURE_PSD1_NAME = L"Microsoft.PowerToys.Configure.psd1";
const wchar_t *DSC_CONFIGURE_PSM1_NAME = L"Microsoft.PowerToys.Configure.psm1";
const wchar_t* DSC_CONFIGURE_PSD1_NAME = L"Microsoft.PowerToys.Configure.psd1";
const wchar_t* DSC_CONFIGURE_PSM1_NAME = L"Microsoft.PowerToys.Configure.psm1";
UINT __stdcall InstallDSCModuleCA(MSIHANDLE hInstall)
{
@@ -431,7 +429,7 @@ UINT __stdcall InstallDSCModuleCA(MSIHANDLE hInstall)
ExitOnFailure(hr, "Unable to create Powershell modules folder");
}
for (const auto *filename : {DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME})
for (const auto* filename : { DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME })
{
fs::copy_file(fs::path(installationFolder) / "DSCModules" / filename, modulesPath / filename, fs::copy_options::overwrite_existing, errorCode);
@@ -479,7 +477,7 @@ UINT __stdcall UninstallDSCModuleCA(MSIHANDLE hInstall)
std::error_code errorCode;
for (const auto *filename : {DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME})
for (const auto* filename : { DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME })
{
fs::remove(versionedModulePath / filename, errorCode);
@@ -490,7 +488,7 @@ UINT __stdcall UninstallDSCModuleCA(MSIHANDLE hInstall)
}
}
for (const auto *modulePath : {&versionedModulePath, &powerToysModulePath})
for (const auto* modulePath : { &versionedModulePath, &powerToysModulePath })
{
fs::remove(*modulePath, errorCode);
@@ -537,7 +535,7 @@ UINT __stdcall InstallEmbeddedMSIXCA(MSIHANDLE hInstall)
using namespace winrt::Windows::Management::Deployment;
using namespace winrt::Windows::Foundation;
Uri msix_uri{msix_path.wstring()};
Uri msix_uri{ msix_path.wstring() };
PackageManager pm;
auto result = pm.AddPackageAsync(msix_uri, nullptr, DeploymentOptions::None).get();
if (!result)
@@ -571,7 +569,7 @@ UINT __stdcall UninstallEmbeddedMSIXCA(MSIHANDLE hInstall)
hr = WcaInitialize(hInstall, "UninstallEmbeddedMSIXCA");
ExitOnFailure(hr, "Failed to initialize");
for (const auto &p : pm.FindPackagesForUser({}, package_name, publisher))
for (const auto& p : pm.FindPackagesForUser({}, package_name, publisher))
{
auto result = pm.RemovePackageAsync(p.Id().FullName()).get();
if (result)
@@ -685,6 +683,7 @@ UINT __stdcall UninstallCommandNotFoundModuleCA(MSIHANDLE hInstall)
command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(installationFolder) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "\"";
#endif
system(command.c_str());
LExit:
@@ -739,10 +738,10 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
ITaskService *pService = nullptr;
ITaskFolder *pTaskFolder = nullptr;
IRegisteredTaskCollection *pTaskCollection = nullptr;
ITaskFolder *pRootFolder = nullptr;
ITaskService* pService = nullptr;
ITaskFolder* pTaskFolder = nullptr;
IRegisteredTaskCollection* pTaskCollection = nullptr;
ITaskFolder* pRootFolder = nullptr;
LONG numTasks = 0;
hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA");
@@ -755,10 +754,10 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
// ------------------------------------------------------
// Create an instance of the Task Service.
hr = CoCreateInstance(CLSID_TaskScheduler,
nullptr,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
reinterpret_cast<void **>(&pService));
nullptr,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
reinterpret_cast<void**>(&pService));
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
// Connect to the task service.
@@ -786,7 +785,7 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
{
// Delete all the tasks found.
// If some tasks can't be deleted, the folder won't be deleted later and the user will still be notified.
IRegisteredTask *pRegisteredTask = nullptr;
IRegisteredTask* pRegisteredTask = nullptr;
hr = pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask);
if (SUCCEEDED(hr))
{
@@ -862,7 +861,8 @@ UINT __stdcall TelemetryLogInstallSuccessCA(MSIHANDLE hInstall)
TraceLoggingWideString(get_product_version().c_str(), "Version"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)
);
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
@@ -1028,7 +1028,7 @@ UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
try
{
if (auto install_path = GetMsiPackageInstalledPath(std::wstring{currentScope} == L"perUser"))
if (auto install_path = GetMsiPackageInstalledPath(std::wstring{ currentScope } == L"perUser"))
{
MsiSetPropertyW(hInstall, L"PREVIOUSINSTALLFOLDER", install_path->data());
}
@@ -1040,82 +1040,6 @@ UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
return WcaFinalize(er);
}
UINT __stdcall InstallCmdPalPackageCA(MSIHANDLE hInstall)
{
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Management::Deployment;
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
std::wstring installationFolder;
hr = WcaInitialize(hInstall, "InstallCmdPalPackage");
hr = getInstallFolder(hInstall, installationFolder);
try
{
auto msix = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\", false);
auto dependencies = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\Dependencies\\", true);
if (!msix.empty())
{
auto msixPath = msix[0];
if (!package::RegisterPackage(msixPath, dependencies))
{
Logger::error(L"Failed to install CmdPal package");
er = ERROR_INSTALL_FAILURE;
}
}
}
catch (std::exception &e)
{
std::string errorMessage{"Exception thrown while trying to install CmdPal package: "};
errorMessage += e.what();
Logger::error(errorMessage);
er = ERROR_INSTALL_FAILURE;
}
er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
return WcaFinalize(er);
}
UINT __stdcall UnRegisterCmdPalPackageCA(MSIHANDLE hInstall)
{
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Management::Deployment;
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "UnRegisterCmdPalPackageCA");
try
{
// Packages to unregister
std::wstring packageToRemoveDisplayName {L"Microsoft.CommandPalette"};
if (!package::UnRegisterPackage(packageToRemoveDisplayName))
{
Logger::error(L"Failed to unregister package: " + packageToRemoveDisplayName);
er = ERROR_INSTALL_FAILURE;
}
}
catch (std::exception &e)
{
std::string errorMessage{"Exception thrown while trying to unregister the CmdPal package: "};
errorMessage += e.what();
Logger::error(errorMessage);
er = ERROR_INSTALL_FAILURE;
}
er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
return WcaFinalize(er);
}
UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
{
using namespace winrt::Windows::Foundation;
@@ -1129,20 +1053,54 @@ UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
try
{
// Packages to unregister
const std::vector<std::wstring> packagesToRemoveDisplayName{{L"PowerRenameContextMenu"}, {L"ImageResizerContextMenu"}, {L"FileLocksmithContextMenu"}, {L"NewPlusContextMenu"}};
const std::vector<std::wstring> packagesToRemoveDisplayName{ { L"PowerRenameContextMenu" }, { L"ImageResizerContextMenu" }, { L"FileLocksmithContextMenu" }, { L"NewPlusContextMenu" } };
for (auto const &package : packagesToRemoveDisplayName)
PackageManager packageManager;
for (auto const& package : packageManager.FindPackages())
{
if (!package::UnRegisterPackage(package))
const auto& packageFullName = std::wstring{ package.Id().FullName() };
for (const auto& packageToRemove : packagesToRemoveDisplayName)
{
Logger::error(L"Failed to unregister package: " + package);
er = ERROR_INSTALL_FAILURE;
if (packageFullName.contains(packageToRemove))
{
auto deploymentOperation{ packageManager.RemovePackageAsync(packageFullName) };
deploymentOperation.get();
// Check the status of the operation
if (deploymentOperation.Status() == AsyncStatus::Error)
{
auto deploymentResult{ deploymentOperation.GetResults() };
auto errorCode = deploymentOperation.ErrorCode();
auto errorText = deploymentResult.ErrorText();
Logger::error(L"Unregister {} package failed. ErrorCode: {}, ErrorText: {}", packageFullName, std::to_wstring(errorCode), errorText);
er = ERROR_INSTALL_FAILURE;
}
else if (deploymentOperation.Status() == AsyncStatus::Canceled)
{
Logger::error(L"Unregister {} package canceled.", packageFullName);
er = ERROR_INSTALL_FAILURE;
}
else if (deploymentOperation.Status() == AsyncStatus::Completed)
{
Logger::info(L"Unregister {} package completed.", packageFullName);
}
else
{
Logger::debug(L"Unregister {} package started.", packageFullName);
}
}
}
}
}
catch (std::exception &e)
catch (std::exception& e)
{
std::string errorMessage{"Exception thrown while trying to unregister sparse packages: "};
std::string errorMessage{ "Exception thrown while trying to unregister sparse packages: " };
errorMessage += e.what();
Logger::error(errorMessage);
@@ -1170,7 +1128,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
}
processes.resize(bytes / sizeof(processes[0]));
std::array<std::wstring_view, 39> processesToTerminate = {
std::array<std::wstring_view, 38> processesToTerminate = {
L"PowerToys.PowerLauncher.exe",
L"PowerToys.Settings.exe",
L"PowerToys.AdvancedPaste.exe",
@@ -1207,7 +1165,6 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
L"PowerToys.WorkspacesLauncherUI.exe",
L"PowerToys.WorkspacesEditor.exe",
L"PowerToys.WorkspacesWindowArranger.exe",
L"Microsoft.CmdPal.UI.exe",
L"PowerToys.ZoomIt.exe",
L"PowerToys.exe",
};
@@ -1220,7 +1177,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
}
wchar_t processName[MAX_PATH] = L"<unknown>";
HANDLE hProcess{OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, procID)};
HANDLE hProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, procID) };
if (!hProcess)
{
continue;
@@ -1240,9 +1197,8 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
if (processName == processToTerminate)
{
const DWORD timeout = 500;
auto windowEnumerator = [](HWND hwnd, LPARAM procIDPtr) -> BOOL
{
auto targetProcID = *reinterpret_cast<const DWORD *>(procIDPtr);
auto windowEnumerator = [](HWND hwnd, LPARAM procIDPtr) -> BOOL {
auto targetProcID = *reinterpret_cast<const DWORD*>(procIDPtr);
DWORD windowProcID = 0;
GetWindowThreadProcessId(hwnd, &windowProcID);
if (windowProcID == targetProcID)
@@ -1268,15 +1224,15 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
void initSystemLogger()
{
static std::once_flag initLoggerFlag;
std::call_once(initLoggerFlag, []()
{
WCHAR temp_path[MAX_PATH];
auto ret = GetTempPath(MAX_PATH, temp_path);
std::call_once(initLoggerFlag, []() {
WCHAR temp_path[MAX_PATH];
auto ret = GetTempPath(MAX_PATH, temp_path);
if (ret)
{
Logger::init("PowerToysMSI", std::wstring{ temp_path } + L"\\PowerToysMSIInstaller", L"");
} });
if (ret)
{
Logger::init("PowerToysMSI", std::wstring{ temp_path } + L"\\PowerToysMSIInstaller", L"");
}
});
}
// DllMain - Initialize and cleanup WiX custom action utils.

View File

@@ -18,9 +18,7 @@ EXPORTS
TerminateProcessesCA
InstallEmbeddedMSIXCA
InstallDSCModuleCA
InstallCmdPalPackageCA
UnApplyModulesRegistryChangeSetsCA
UnRegisterCmdPalPackageCA
UnRegisterContextMenuPackagesCA
UninstallEmbeddedMSIXCA
UninstallDSCModuleCA

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<Import Project="..\wix.props" Condition="Exists('..\wix.props')" />
<PropertyGroup Label="Globals">
@@ -55,7 +54,6 @@
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\AdvancedPaste.wxs"" ""$(ProjectDir)..\PowerToysSetup\AdvancedPaste.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Awake.wxs"" ""$(ProjectDir)..\PowerToysSetup\Awake.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\BaseApplications.wxs"" ""$(ProjectDir)..\PowerToysSetup\BaseApplications.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\CmdPal.wxs"" ""$(ProjectDir)..\PowerToysSetup\CmdPal.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\ColorPicker.wxs"" ""$(ProjectDir)..\PowerToysSetup\ColorPicker.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Core.wxs"" ""$(ProjectDir)..\PowerToysSetup\Core.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\EnvironmentVariables.wxs"" ""$(ProjectDir)..\PowerToysSetup\EnvironmentVariables.wxs.bk""""

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CmdPalVersion Condition="'$(CmdPalVersion)'=='' and '$(XES_APPXMANIFESTVERSION)'!=''">$(XES_APPXMANIFESTVERSION)</CmdPalVersion>
<CmdPalVersion Condition="'$(CmdPalVersion)'==''">0.0.1.0</CmdPalVersion>
<DevEnvironment>Local</DevEnvironment>
<!-- Forcing for every DLL on by default -->
<ChecksumAlgorithm>SHA256</ChecksumAlgorithm>
</PropertyGroup>
</Project>

View File

@@ -5,8 +5,5 @@
<IsAotCompatible>true</IsAotCompatible>
<CsWinRTAotOptimizerEnabled>true</CsWinRTAotOptimizerEnabled>
<CsWinRTAotWarningLevel>2</CsWinRTAotWarningLevel>
<!-- Suppress DynamicallyAccessedMemberTypes.PublicParameterlessConstructor in fallback code path of Windows SDK projection -->
<WarningsNotAsErrors>IL2081;$(WarningsNotAsErrors)</WarningsNotAsErrors>
</PropertyGroup>
</Project>

View File

@@ -1,10 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Some items may be set in Directory.Build.props in root -->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project=".\Common.Dotnet.PrepareGeneratedFolder.targets" />
<PropertyGroup>
<WindowsSdkPackageVersion>10.0.22621.57</WindowsSdkPackageVersion>
<WindowsSdkPackageVersion>10.0.22621.48</WindowsSdkPackageVersion>
<TargetFramework>net9.0-windows10.0.22621.0</TargetFramework>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
@@ -16,7 +14,7 @@
<WarningLevel>4</WarningLevel>
<NoWarn></NoWarn>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<WarningsNotAsErrors>CA1824;CA1416;CA1720;CA1859;CA2263;CA2022;MVVMTK0045;MVVMTK0049</WarningsNotAsErrors>
<WarningsNotAsErrors>CA1720;CA1859;CA2263;CA2022</WarningsNotAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Some items may be set in Directory.Build.props in root -->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- OneFuzz does not currently support testing with .NET 9.
As a temporary workaround, create a .NET 8 project and use file links
to include the code that needs testing. -->
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -1,16 +0,0 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="EnsureGeneratedBaseFolder" BeforeTargets="XamlPreCompile">
<PropertyGroup>
<!-- Only create the base 'generated' folder -->
<CompilerGeneratedFilesOutputPath>$(ProjectDir)obj\g</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<!-- Create 'generated' folder if missing -->
<MakeDir Directories="$(CompilerGeneratedFilesOutputPath)" />
<!-- Optional logging for debugging -->
<Message Text="Ensured: $(GeneratedBasePath)" Importance="Low" />
</Target>
</Project>

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
@@ -12,10 +12,10 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:PrefixLocalCallsWithThis", Justification = "We follow the C# Core Coding Style which avoids using `this` unless absolutely necessary.")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:ElementsMustAppearInTheCorrectOrder", Justification = "It is not a priority and has high impact in code changes.")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "It is not a priority and has high impact in code changes.")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1203:ConstantsMustAppearBeforeFields", Justification = "It is not a priority and has high impact in code changes.")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:StaticElementsMustAppearBeforeInstanceElements", Justification = "It is not a priority and has high impact in code changes.")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:ElementsMustAppearInTheCorrectOrder", Justification = "It is not a priority and have hight impact in code changes.")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "It is not a priority and have hight impact in code changes.")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1203:ConstantsMustAppearBeforeFields", Justification = "It is not a priority and have hight impact in code changes.")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:StaticElementsMustAppearBeforeInstanceElements", Justification = "It is not a priority and have hight impact in code changes.")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1309:FieldNamesMustNotBeginWithUnderscore", Justification = "We follow the C# Core Coding Style which uses underscores as prefixes rather than using `this.`.")]
@@ -62,17 +62,3 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "<Dotnet port with style preservation>", Scope = "namespaceanddescendants", Target = "MouseWithoutBorders")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "<Dotnet port with style preservation>", Scope = "namespaceanddescendants", Target = "MouseWithoutBorders")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "<Dotnet port with style preservation>", Scope = "namespaceanddescendants", Target = "MouseWithoutBorders")]
// AOT MVVMTK0045
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "AdvancedPaste.ViewModels")]
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "HostsUILib")]
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "EnvironmentVariablesUILib")]
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.FilePreviewer")]
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.UI")]
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.UI.Views")]
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib")]
// AOT MVVMTK0049
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.FilePreviewer")]
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "type", Target = "~T:Peek.UI.Views.TitleBar")]
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib")]

View File

@@ -25,7 +25,7 @@ namespace AllExperiments
}
// Using InvariantCulture since this is used for a log file name
var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log");
var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));

View File

@@ -31,7 +31,6 @@ namespace Common.UI
Dashboard,
AdvancedPaste,
Workspaces,
CmdPal,
ZoomIt,
}
@@ -79,8 +78,6 @@ namespace Common.UI
return "AdvancedPaste";
case SettingsWindow.Workspaces:
return "Workspaces";
case SettingsWindow.CmdPal:
return "CmdPal";
case SettingsWindow.ZoomIt:
return "ZoomIt";
default:

View File

@@ -92,9 +92,6 @@ namespace Microsoft.PowerToys.FilePreviewCommon
var run = 0;
var chunksLen = fileSize - QOI_PADDING_LENGTH;
var x = 0;
var rowAdd = bitmapData.Stride - (channels * bitmapData.Width);
for (var dataIndex = 0; dataIndex < dataLength; dataIndex += channels)
{
if (run > 0)
@@ -156,14 +153,6 @@ namespace Microsoft.PowerToys.FilePreviewCommon
bitmapPixel[3] = pixel.A;
}
}
x++;
if (x == bitmapData.Width)
{
// We align dataIndex with the stride
dataIndex += rowAdd;
x = 0;
}
}
bitmap.UnlockBits(bitmapData);

View File

@@ -16,10 +16,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredCmdNotFoundEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredCmdPalEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredCmdPalEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredColorPickerEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredColorPickerEnabledValue());
@@ -236,10 +232,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowDataDiagnosticsValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredNewPlusReplaceVariablesValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredNewPlusReplaceVariablesValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredRunAtStartupValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredRunAtStartupValue());

View File

@@ -10,7 +10,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetConfiguredAlwaysOnTopEnabledValue();
static GpoRuleConfigured GetConfiguredAwakeEnabledValue();
static GpoRuleConfigured GetConfiguredCmdNotFoundEnabledValue();
static GpoRuleConfigured GetConfiguredCmdPalEnabledValue();
static GpoRuleConfigured GetConfiguredColorPickerEnabledValue();
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
@@ -65,7 +64,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetConfiguredNewPlusHideTemplateFilenameExtensionValue();
static GpoRuleConfigured GetAllowDataDiagnosticsValue();
static GpoRuleConfigured GetConfiguredRunAtStartupValue();
static GpoRuleConfigured GetConfiguredNewPlusReplaceVariablesValue();
};
}

View File

@@ -14,7 +14,6 @@ namespace PowerToys
static GpoRuleConfigured GetConfiguredAlwaysOnTopEnabledValue();
static GpoRuleConfigured GetConfiguredAwakeEnabledValue();
static GpoRuleConfigured GetConfiguredCmdNotFoundEnabledValue();
static GpoRuleConfigured GetConfiguredCmdPalEnabledValue();
static GpoRuleConfigured GetConfiguredColorPickerEnabledValue();
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
@@ -69,7 +68,6 @@ namespace PowerToys
static GpoRuleConfigured GetConfiguredNewPlusHideTemplateFilenameExtensionValue();
static GpoRuleConfigured GetAllowDataDiagnosticsValue();
static GpoRuleConfigured GetConfiguredRunAtStartupValue();
static GpoRuleConfigured GetConfiguredNewPlusReplaceVariablesValue();
}
}
}

View File

@@ -141,40 +141,6 @@ namespace ManagedCommon
return lab;
}
/// <summary>
/// Convert a given <see cref="Color"/> to a Oklab color
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert</param>
/// <returns>The perceptual lightness [0..1] and two chromaticities [-0.5..0.5]</returns>
public static (double Lightness, double ChromaticityA, double ChromaticityB) ConvertToOklabColor(Color color)
{
var linear = ConvertSRGBToLinearRGB(color.R / 255d, color.G / 255d, color.B / 255d);
var oklab = GetOklabColorFromLinearRGB(linear.R, linear.G, linear.B);
return oklab;
}
/// <summary>
/// Convert a given <see cref="Color"/> to a Oklch color
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert</param>
/// <returns>The perceptual lightness [0..1], the chroma [0..0.5], and the hue angle [0°..360°]</returns>
public static (double Lightness, double Chroma, double Hue) ConvertToOklchColor(Color color)
{
var oklab = ConvertToOklabColor(color);
var oklch = GetOklchColorFromOklab(oklab.Lightness, oklab.ChromaticityA, oklab.ChromaticityB);
return oklch;
}
public static (double R, double G, double B) ConvertSRGBToLinearRGB(double r, double g, double b)
{
// inverse companding, gamma correction must be undone
double rLinear = (r > 0.04045) ? Math.Pow((r + 0.055) / 1.055, 2.4) : (r / 12.92);
double gLinear = (g > 0.04045) ? Math.Pow((g + 0.055) / 1.055, 2.4) : (g / 12.92);
double bLinear = (b > 0.04045) ? Math.Pow((b + 0.055) / 1.055, 2.4) : (b / 12.92);
return (rLinear, gLinear, bLinear);
}
/// <summary>
/// Convert a given <see cref="Color"/> to a CIE XYZ color (XYZ)
/// The constants of the formula matches this Wikipedia page, but at a higher precision:
@@ -190,7 +156,10 @@ namespace ManagedCommon
double g = color.G / 255d;
double b = color.B / 255d;
(double rLinear, double gLinear, double bLinear) = ConvertSRGBToLinearRGB(r, g, b);
// inverse companding, gamma correction must be undone
double rLinear = (r > 0.04045) ? Math.Pow((r + 0.055) / 1.055, 2.4) : (r / 12.92);
double gLinear = (g > 0.04045) ? Math.Pow((g + 0.055) / 1.055, 2.4) : (g / 12.92);
double bLinear = (b > 0.04045) ? Math.Pow((b + 0.055) / 1.055, 2.4) : (b / 12.92);
return (
(rLinear * 0.41239079926595948) + (gLinear * 0.35758433938387796) + (bLinear * 0.18048078840183429),
@@ -241,63 +210,6 @@ namespace ManagedCommon
return (l, a, b);
}
/// <summary>
/// Convert a linear RGB color <see cref="double"/> to an Oklab color.
/// The constants of this formula come from https://github.com/Evercoder/culori/blob/2bedb8f0507116e75f844a705d0b45cf279b15d0/src/oklab/convertLrgbToOklab.js
/// and the implementation is based on https://bottosson.github.io/posts/oklab/
/// </summary>
/// <param name="r">Linear R value</param>
/// <param name="g">Linear G value</param>
/// <param name="b">Linear B value</param>
/// <returns>The perceptual lightness [0..1] and two chromaticities [-0.5..0.5]</returns>
private static (double Lightness, double ChromaticityA, double ChromaticityB)
GetOklabColorFromLinearRGB(double r, double g, double b)
{
double l = (0.41222147079999993 * r) + (0.5363325363 * g) + (0.0514459929 * b);
double m = (0.2119034981999999 * r) + (0.6806995450999999 * g) + (0.1073969566 * b);
double s = (0.08830246189999998 * r) + (0.2817188376 * g) + (0.6299787005000002 * b);
double l_ = Math.Cbrt(l);
double m_ = Math.Cbrt(m);
double s_ = Math.Cbrt(s);
return (
(0.2104542553 * l_) + (0.793617785 * m_) - (0.0040720468 * s_),
(1.9779984951 * l_) - (2.428592205 * m_) + (0.4505937099 * s_),
(0.0259040371 * l_) + (0.7827717662 * m_) - (0.808675766 * s_)
);
}
/// <summary>
/// Convert an Oklab color <see cref="double"/> from Cartesian form to its polar form Oklch
/// https://bottosson.github.io/posts/oklab/#the-oklab-color-space
/// </summary>
/// <param name="lightness">The <see cref="lightness"/></param>
/// <param name="chromaticity_a">The <see cref="chromaticity_a"/></param>
/// <param name="chromaticity_b">The <see cref="chromaticity_b"/></param>
/// <returns>The perceptual lightness [0..1], the chroma [0..0.5], and the hue angle [0°..360°]</returns>
private static (double Lightness, double Chroma, double Hue)
GetOklchColorFromOklab(double lightness, double chromaticity_a, double chromaticity_b)
{
return GetLCHColorFromLAB(lightness, chromaticity_a, chromaticity_b);
}
/// <summary>
/// Convert a color in Cartesian form (Lab) to its polar form (LCh)
/// </summary>
/// <param name="lightness">The <see cref="lightness"/></param>
/// <param name="chromaticity_a">The <see cref="chromaticity_a"/></param>
/// <param name="chromaticity_b">The <see cref="chromaticity_b"/></param>
/// <returns>The lightness, chroma, and hue angle</returns>
private static (double Lightness, double Chroma, double Hue)
GetLCHColorFromLAB(double lightness, double chromaticity_a, double chromaticity_b)
{
// Lab to LCh transformation
double chroma = Math.Sqrt(Math.Pow(chromaticity_a, 2) + Math.Pow(chromaticity_b, 2));
double hue = Math.Round(chroma, 3) == 0 ? 0.0 : ((Math.Atan2(chromaticity_b, chromaticity_a) * 180d / Math.PI) + 360d) % 360d;
return (lightness, chroma, hue);
}
/// <summary>
/// Convert a given <see cref="Color"/> to a natural color (hue, whiteness, blackness)
/// </summary>
@@ -364,17 +276,12 @@ namespace ManagedCommon
{ "Br", 'p' }, // brightness percent
{ "In", 'p' }, // intensity percent
{ "Ll", 'p' }, // lightness (HSL) percent
{ "Lc", 'p' }, // lightness(CIELAB)percent
{ "Va", 'p' }, // value percent
{ "Wh", 'p' }, // whiteness percent
{ "Bn", 'p' }, // blackness percent
{ "Lc", 'p' }, // lightness (CIE) percent
{ "Ca", 'p' }, // chromaticityA (CIELAB) percent
{ "Cb", 'p' }, // chromaticityB (CIELAB) percent
{ "Lo", 'p' }, // lightness (Oklab/Oklch) percent
{ "Oa", 'p' }, // chromaticityA (Oklab) percent
{ "Ob", 'p' }, // chromaticityB (Oklab) percent
{ "Oc", 'p' }, // chroma (Oklch) percent
{ "Oh", 'p' }, // hue angle (Oklch) percent
{ "Ca", 'p' }, // chromaticityA percent
{ "Cb", 'p' }, // chromaticityB percent
{ "Xv", 'i' }, // X value int
{ "Yv", 'i' }, // Y value int
{ "Zv", 'i' }, // Z value int
@@ -517,10 +424,6 @@ namespace ManagedCommon
var (lightnessC, _, _) = ConvertToCIELABColor(color);
lightnessC = Math.Round(lightnessC, 2);
return lightnessC.ToString(CultureInfo.InvariantCulture);
case "Lo":
var (lightnessO, _, _) = ConvertToOklabColor(color);
lightnessO = Math.Round(lightnessO, 2);
return lightnessO.ToString(CultureInfo.InvariantCulture);
case "Wh":
var (_, whiteness, _) = ConvertToHWBColor(color);
whiteness = Math.Round(whiteness * 100);
@@ -537,22 +440,6 @@ namespace ManagedCommon
var (_, _, chromaticityB) = ConvertToCIELABColor(color);
chromaticityB = Math.Round(chromaticityB, 2);
return chromaticityB.ToString(CultureInfo.InvariantCulture);
case "Oa":
var (_, chromaticityAOklab, _) = ConvertToOklabColor(color);
chromaticityAOklab = Math.Round(chromaticityAOklab, 2);
return chromaticityAOklab.ToString(CultureInfo.InvariantCulture);
case "Ob":
var (_, _, chromaticityBOklab) = ConvertToOklabColor(color);
chromaticityBOklab = Math.Round(chromaticityBOklab, 2);
return chromaticityBOklab.ToString(CultureInfo.InvariantCulture);
case "Oc":
var (_, chromaOklch, _) = ConvertToOklchColor(color);
chromaOklch = Math.Round(chromaOklch, 2);
return chromaOklch.ToString(CultureInfo.InvariantCulture);
case "Oh":
var (_, _, hueOklch) = ConvertToOklchColor(color);
hueOklch = Math.Round(hueOklch, 2);
return hueOklch.ToString(CultureInfo.InvariantCulture);
case "Xv":
var (x, _, _) = ConvertToCIEXYZColor(color);
x = Math.Round(x * 100, 4);
@@ -608,10 +495,8 @@ namespace ManagedCommon
case "HSI": return "hsi(%Hu, %Si%, %In%)";
case "HWB": return "hwb(%Hu, %Wh%, %Bn%)";
case "NCol": return "%Hn, %Wh%, %Bn%";
case "CIEXYZ": return "XYZ(%Xv, %Yv, %Zv)";
case "CIELAB": return "CIELab(%Lc, %Ca, %Cb)";
case "Oklab": return "oklab(%Lo, %Oa, %Ob)";
case "Oklch": return "oklch(%Lo, %Oc, %Oh)";
case "CIEXYZ": return "XYZ(%Xv, %Yv, %Zv)";
case "VEC4": return "(%Reff, %Grff, %Blff, 1f)";
case "Decimal": return "%Dv";
case "HEX Int": return "0xFF%ReX%GrX%BlX";

View File

@@ -1,51 +0,0 @@
// 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 System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ManagedCommon
{
public static class IdRecoveryHelper
{
/// <summary>
/// Fixes invalid IDs in the given list by assigning unique values.
/// It ensures that all IDs are non-empty and unique, correcting any duplicates or empty IDs.
/// </summary>
/// <param name="items">The list of items that may contain invalid IDs.</param>
public static void RecoverInvalidIds<T>(IEnumerable<T> items)
where T : class, IHasId
{
var idSet = new HashSet<int>();
int newId = 0;
var sortedItems = items.OrderBy(i => i.Id).ToList(); // Sort items by ID for consistent processing
// Iterate through the list and fix invalid IDs
foreach (var item in sortedItems)
{
// If the ID is invalid or already exists in the set (duplicate), assign a new unique ID
if (!idSet.Add(item.Id))
{
// Find the next available unique ID
while (idSet.Contains(newId))
{
newId++;
}
item.Id = newId;
idSet.Add(newId); // Add the newly assigned ID to the set
}
}
}
}
public interface IHasId
{
int Id { get; set; }
}
}

View File

@@ -6,10 +6,9 @@ using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using PowerToys.Interop;
namespace ManagedCommon
@@ -41,72 +40,25 @@ namespace ManagedCommon
/// <param name="isLocalLow">If the process using Logger is a low-privilege process.</param>
public static void InitializeLogger(string applicationLogPath, bool isLocalLow = false)
{
string basePath;
if (isLocalLow)
{
basePath = Environment.GetEnvironmentVariable("userprofile") + "\\appdata\\LocalLow\\Microsoft\\PowerToys" + applicationLogPath;
applicationLogPath = Environment.GetEnvironmentVariable("userprofile") + "\\appdata\\LocalLow\\Microsoft\\PowerToys" + applicationLogPath + "\\" + Version;
}
else
{
basePath = Constants.AppDataPath() + applicationLogPath;
applicationLogPath = Constants.AppDataPath() + applicationLogPath + "\\" + Version;
}
string versionedPath = Path.Combine(basePath, Version);
if (!Directory.Exists(versionedPath))
if (!Directory.Exists(applicationLogPath))
{
Directory.CreateDirectory(versionedPath);
Directory.CreateDirectory(applicationLogPath);
}
var logFilePath = Path.Combine(versionedPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log");
var logFilePath = Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
Trace.AutoFlush = true;
// Clean up old version log folders
Task.Run(() => DeleteOldVersionLogFolders(basePath, versionedPath));
}
/// <summary>
/// Deletes old version log folders, keeping only the current version's folder.
/// </summary>
/// <param name="basePath">The base path to the log files folder.</param>
/// <param name="currentVersionPath">The path to the current version's log folder.</param>
private static void DeleteOldVersionLogFolders(string basePath, string currentVersionPath)
{
try
{
if (!Directory.Exists(basePath))
{
return;
}
var dirs = Directory.GetDirectories(basePath)
.Select(d => new DirectoryInfo(d))
.OrderBy(d => d.CreationTime)
.Where(d => !string.Equals(d.FullName, currentVersionPath, StringComparison.OrdinalIgnoreCase))
.Take(3)
.ToList();
foreach (var directory in dirs)
{
try
{
Directory.Delete(directory.FullName, true);
LogInfo($"Deleted old log directory: {directory.FullName}");
Task.Delay(500).Wait();
}
catch (Exception ex)
{
LogError($"Failed to delete old log directory: {directory.FullName}", ex);
}
}
}
catch (Exception ex)
{
LogError("Error cleaning up old log folders", ex);
}
}
public static void LogError(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)

View File

@@ -10,7 +10,6 @@ namespace ManagedCommon
AlwaysOnTop,
Awake,
ColorPicker,
CmdPal,
CropAndLock,
EnvironmentVariables,
FancyZones,

View File

@@ -11,40 +11,17 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
public class By
{
private readonly OpenQA.Selenium.By? by;
private readonly bool isAccessibilityId;
private readonly string? accessibilityId;
private readonly OpenQA.Selenium.By by;
private By(OpenQA.Selenium.By by)
{
isAccessibilityId = false;
this.by = by;
}
private By(string accessibilityId)
{
isAccessibilityId = true;
this.accessibilityId = accessibilityId;
}
public override string ToString()
{
// override ToString to return detailed debugging content provided by OpenQA.Selenium.By
return this.GetAccessibilityId();
}
public bool GetIsAccessibilityId() => this.isAccessibilityId;
public string GetAccessibilityId()
{
if (this.isAccessibilityId)
{
return this.accessibilityId!;
}
else
{
return this.by!.ToString();
}
return this.by.ToString();
}
/// <summary>
@@ -54,13 +31,6 @@ namespace Microsoft.PowerToys.UITest
/// <returns>A By object.</returns>
public static By Name(string name) => new By(OpenQA.Selenium.By.Name(name));
/// <summary>
/// Creates a By object using the className attribute.
/// </summary>
/// <param name="className">The className attribute to search for.</param>
/// <returns>A By object.</returns>
public static By ClassName(string className) => new By(OpenQA.Selenium.By.ClassName(className));
/// <summary>
/// Creates a By object using the ID attribute.
/// </summary>
@@ -68,13 +38,6 @@ namespace Microsoft.PowerToys.UITest
/// <returns>A By object.</returns>
public static By Id(string id) => new By(OpenQA.Selenium.By.Id(id));
/// <summary>
/// Creates a By object using the ID attribute.
/// </summary>
/// <param name="accessibilityId">The ID attribute to search for.</param>
/// <returns>A By object.</returns>
public static By AccessibilityId(string accessibilityId) => new By(accessibilityId);
/// <summary>
/// Creates a By object using the XPath expression.
/// </summary>
@@ -107,6 +70,6 @@ namespace Microsoft.PowerToys.UITest
/// Converts the By object to an OpenQA.Selenium.By object.
/// </summary>
/// <returns>An OpenQA.Selenium.By object.</returns>
internal OpenQA.Selenium.By ToSeleniumBy() => by!;
internal OpenQA.Selenium.By ToSeleniumBy() => by;
}
}

View File

@@ -1,53 +0,0 @@
// 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.
namespace Microsoft.PowerToys.UITest
{
public class CheckBox : Element
{
private static readonly string ExpectedControlType = "ControlType.CheckBox";
/// <summary>
/// Initializes a new instance of the <see cref="CheckBox"/> class.
/// </summary>
public CheckBox()
{
this.TargetControlType = CheckBox.ExpectedControlType;
}
/// <summary>
/// Select the item of the ComboBox.
/// </summary>
/// <param name="value">The text to select from the list view.</param>
public void Select(string value)
{
this.Find<NavigationViewItem>(value).Click();
}
/// <summary>
/// Gets a value indicating whether the CheckBox is checked.
/// </summary>
public bool IsChecked => this.Selected;
public CheckBox SetCheck(bool value = true, int msPreAction = 500, int msPostAction = 500)
{
if (this.IsChecked != value)
{
if (msPreAction > 0)
{
Task.Delay(msPreAction).Wait();
}
// Toggle the switch
this.Click();
if (msPostAction > 0)
{
Task.Delay(msPostAction).Wait();
}
}
return this;
}
}
}

View File

@@ -1,28 +0,0 @@
// 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.
namespace Microsoft.PowerToys.UITest
{
public class ComboBox : Element
{
private static readonly string ExpectedControlType = "ControlType.ComboBox";
/// <summary>
/// Initializes a new instance of the <see cref="ComboBox"/> class.
/// </summary>
public ComboBox()
{
this.TargetControlType = ComboBox.ExpectedControlType;
}
/// <summary>
/// Select the item of the ComboBox.
/// </summary>
/// <param name="value">The text to select from the list view.</param>
public void Select(string value)
{
this.Find<NavigationViewItem>(value).Click();
}
}
}

View File

@@ -1,74 +0,0 @@
// 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 System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.PowerToys.UITest
{
public class Custom : Element
{
private static readonly string ExpectedControlType = "ControlType.Custom";
/// <summary>
/// Initializes a new instance of the <see cref="Custom"/> class.
/// </summary>
public Custom()
{
this.TargetControlType = Custom.ExpectedControlType;
}
/// <summary>
/// Sends a combination of keys.
/// </summary>
/// <param name="keys">The keys to send.</param>
public void SendKeys(params Key[] keys)
{
PerformAction((actions, windowElement) =>
{
KeyboardHelper.SendKeys(keys);
});
}
/// <summary>
/// Drag element move offset.
/// </summary>
/// <param name="offsetX">The offsetX to move.</param>
/// <param name="offsetY">The offsetY to move.</param>
public void Drag(int offsetX, int offsetY)
{
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement).MoveByOffset(10, 10).ClickAndHold(windowElement).MoveByOffset(offsetX, offsetY).Release();
actions.Build().Perform();
});
}
/// <summary>
/// Drag element move to other element.
/// </summary>
/// <param name="element">Move to this element.</param>
public void Drag(Element element)
{
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement).ClickAndHold();
Assert.IsNotNull(element.WindowsElement, "element is null");
int dx = (element.WindowsElement.Rect.X - windowElement.Rect.X) / 10;
int dy = (element.WindowsElement.Rect.Y - windowElement.Rect.Y) / 10;
for (int i = 0; i < 10; i++)
{
actions.MoveByOffset(dx, dy);
}
actions.Release();
actions.Build().Perform();
});
}
}
}

View File

@@ -3,14 +3,8 @@
// See the LICENSE file in the project root for more information.
using System.Collections.ObjectModel;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Xml.Linq;
using ABI.Windows.Foundation;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
@@ -26,20 +20,8 @@ namespace Microsoft.PowerToys.UITest
{
private WindowsElement? windowsElement;
protected internal WindowsElement? WindowsElement
{
get => windowsElement;
set => windowsElement = value;
}
private WindowsDriver<WindowsElement>? driver;
protected internal WindowsDriver<WindowsElement>? Driver
{
get => driver;
set => driver = value;
}
protected string? TargetControlType { get; set; }
internal bool IsMatchingTarget()
@@ -81,14 +63,6 @@ namespace Microsoft.PowerToys.UITest
get { return this.windowsElement?.Selected ?? false; }
}
/// <summary>
/// Gets the Rect of the UI element.
/// </summary>
public Rectangle? Rect
{
get { return this.windowsElement?.Rect; }
}
/// <summary>
/// Gets the AutomationID of the UI element.
/// </summary>
@@ -125,12 +99,9 @@ namespace Microsoft.PowerToys.UITest
/// Click the UI element.
/// </summary>
/// <param name="rightClick">If true, performs a right-click; otherwise, performs a left-click. Default value is false</param>
/// <param name="msPreAction">Delay in milliseconds before performing the click action. Default is 500 ms.</param>
/// <param name="msPostAction">Delay in milliseconds after performing the click action. Default is 500 ms.</param>
public virtual void Click(bool rightClick = false, int msPreAction = 500, int msPostAction = 500)
public virtual void Click(bool rightClick = false)
{
PerformAction(
(actions, windowElement) =>
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement);
@@ -147,9 +118,7 @@ namespace Microsoft.PowerToys.UITest
}
actions.Build().Perform();
},
msPreAction,
msPostAction);
});
}
/// <summary>
@@ -169,23 +138,6 @@ namespace Microsoft.PowerToys.UITest
});
}
/// <summary>
/// Release action
/// </summary>
public void ReleaseAction()
{
var releaseAction = new Actions(driver);
releaseAction.Release().Perform();
}
/// <summary>
/// Release key
/// </summary>
public void ReleaseKey(Key key)
{
KeyboardHelper.ReleaseKey(key);
}
/// <summary>
/// Gets the attribute value of the UI element.
/// </summary>
@@ -205,7 +157,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="by">The selector to use for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>The found element.</returns>
public T Find<T>(By by, int timeoutMS = 5000)
public T Find<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method Find<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
@@ -213,7 +165,7 @@ namespace Microsoft.PowerToys.UITest
// leverage findAll to filter out mismatched elements
var collection = this.FindAll<T>(by, timeoutMS);
Assert.IsTrue(collection.Count > 0, $"UI-Element({typeof(T).Name}) not found using selector: {by}");
Assert.IsTrue(collection.Count > 0, $"Element not found using selector: {by}");
return collection[0];
}
@@ -226,7 +178,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="name">The name for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>The found element.</returns>
public T Find<T>(string name, int timeoutMS = 5000)
public T Find<T>(string name, int timeoutMS = 3000)
where T : Element, new()
{
return this.Find<T>(By.Name(name), timeoutMS);
@@ -239,7 +191,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="by">The selector to use for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>The found element.</returns>
public Element Find(By by, int timeoutMS = 5000)
public Element Find(By by, int timeoutMS = 3000)
{
return this.Find<Element>(by, timeoutMS);
}
@@ -251,7 +203,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="name">The name for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>The found element.</returns>
public Element Find(string name, int timeoutMS = 5000)
public Element Find(string name, int timeoutMS = 3000)
{
return this.Find<Element>(By.Name(name), timeoutMS);
}
@@ -263,23 +215,15 @@ namespace Microsoft.PowerToys.UITest
/// <param name="by">The selector to use for finding the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 5000)
public ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
var foundElements = FindHelper.FindAll<T, AppiumWebElement>(
() =>
{
if (by.GetIsAccessibilityId())
{
var elements = this.windowsElement.FindElementsByAccessibilityId(by.GetAccessibilityId());
return elements;
}
else
{
var elements = this.windowsElement.FindElements(by.ToSeleniumBy());
return elements;
}
var elements = this.windowsElement.FindElements(by.ToSeleniumBy());
return elements;
},
this.driver,
timeoutMS);
@@ -295,7 +239,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="name">The name for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<T> FindAll<T>(string name, int timeoutMS = 5000)
public ReadOnlyCollection<T> FindAll<T>(string name, int timeoutMS = 3000)
where T : Element, new()
{
return this.FindAll<T>(By.Name(name), timeoutMS);
@@ -308,7 +252,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="by">The selector to use for finding the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<Element> FindAll(By by, int timeoutMS = 5000)
public ReadOnlyCollection<Element> FindAll(By by, int timeoutMS = 3000)
{
return this.FindAll<Element>(by, timeoutMS);
}
@@ -320,23 +264,11 @@ namespace Microsoft.PowerToys.UITest
/// <param name="name">The name for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<Element> FindAll(string name, int timeoutMS = 5000)
public ReadOnlyCollection<Element> FindAll(string name, int timeoutMS = 3000)
{
return this.FindAll<Element>(By.Name(name), timeoutMS);
}
/// <summary>
/// Send Key of the element.
/// </summary>
/// <param name="key">The Key to Send.</param>
protected void SendKeys(string key)
{
PerformAction((actions, windowElement) =>
{
windowElement.SendKeys(key);
});
}
/// <summary>
/// Simulates a manual operation on the element.
/// </summary>
@@ -359,15 +291,5 @@ namespace Microsoft.PowerToys.UITest
Task.Delay(msPostAction).Wait();
}
}
/// <summary>
/// Save UI Element to a PNG file.
/// </summary>
/// <param name="path">the full path</param>
internal void SaveToPngFile(string path)
{
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method SaveToPngFile with parameter: path = {path}");
this.windowsElement.GetScreenshot().SaveAsFile(path);
}
}
}

View File

@@ -1,25 +0,0 @@
// 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 System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.PowerToys.UITest
{
public class Group : Element
{
private static readonly string ExpectedControlType = "ControlType.Group";
/// <summary>
/// Initializes a new instance of the <see cref="Group"/> class.
/// </summary>
public Group()
{
this.TargetControlType = Group.ExpectedControlType;
}
}
}

View File

@@ -24,12 +24,9 @@ namespace Microsoft.PowerToys.UITest
/// Click the ListItem element.
/// </summary>
/// <param name="rightClick">If true, performs a right-click; otherwise, performs a left-click. Default value is false</param>
/// <param name="msPreAction">Pre action delay in milliseconds. Default value is 500</param>
/// <param name="msPostAction">Post action delay in milliseconds. Default value is 500</param>
public override void Click(bool rightClick = false, int msPreAction = 500, int msPostAction = 500)
public override void Click(bool rightClick = false)
{
PerformAction(
(actions, windowElement) =>
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement, 10, 10);
@@ -43,36 +40,7 @@ namespace Microsoft.PowerToys.UITest
}
actions.Build().Perform();
},
msPreAction,
msPostAction);
}
/// <summary>
/// Click the center of the ListItem element.
/// </summary>
/// <param name="rightClick">If true, performs a right-click; otherwise, performs a left-click. Default value is false</param>
/// <param name="msPreAction">Pre action delay in milliseconds. Default value is 500</param>
/// <param name="msPostAction">Post action delay in milliseconds. Default value is 500</param>
public void ClickCenter(bool rightClick = false, int msPreAction = 500, int msPostAction = 500)
{
PerformAction(
(actions, windowElement) =>
{
actions.MoveToElement(windowElement);
if (rightClick)
{
actions.ContextClick();
}
else
{
actions.Click();
}
actions.Build().Perform();
},
msPreAction,
msPostAction);
});
}
/// <summary>

View File

@@ -1,101 +0,0 @@
// 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
namespace Microsoft.PowerToys.UITest
{
public class Pane : Element
{
private static readonly string ExpectedControlType = "ControlType.Pane";
/// <summary>
/// Initializes a new instance of the <see cref="Pane"/> class.
/// </summary>
public Pane()
{
this.TargetControlType = Pane.ExpectedControlType;
}
/// <summary>
/// Drag element move offset.
/// </summary>
/// <param name="offsetX">The offsetX to move.</param>
/// <param name="offsetY">The offsetY to move.</param>
public void Drag(int offsetX, int offsetY)
{
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement).MoveByOffset(10, 10).ClickAndHold(windowElement).MoveByOffset(offsetX, offsetY).Release();
actions.Build().Perform();
});
}
/// <summary>
/// Simulates holding when dragging to target position.
/// </summary>
/// <param name="offsetX">The offsetX to move.</param>
/// <param name="offsetY">The offsetY to move.</param>
public void DragAndHold(int offsetX, int offsetY)
{
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement).MoveByOffset(10, 10).ClickAndHold(windowElement).MoveByOffset(offsetX, offsetY);
actions.Build().Perform();
});
}
public void ReleaseDrag()
{
var releaseAction = new Actions(this.Driver);
releaseAction.Release().Perform();
}
/// <summary>
/// Simulates holding a key, clicking and dragging a UI element to the specified screen coordinates.
/// </summary>
/// <param name="key">The keyboard key to press and hold during the drag operation.</param>
/// <param name="targetX">The target X-coordinate to drag the element to.</param>
/// <param name="targetY">The target Y-coordinate to drag the element to.</param>
public void KeyDownAndDrag(Key key, int targetX, int targetY)
{
HoldShiftToDrag(key, targetX, targetY);
ReleaseAction();
ReleaseKey(key);
}
/// <summary>
/// Simulates holding a key, clicking and dragging a UI element to the specified screen coordinates.
/// </summary>
/// <param name="key">The keyboard key to press and hold during the drag operation.</param>
/// <param name="targetX">The target X-coordinate to drag the element to.</param>
/// <param name="targetY">The target Y-coordinate to drag the element to.</param>
public void HoldShiftToDrag(Key key, int targetX, int targetY)
{
PerformAction((actions, windowElement) =>
{
KeyboardHelper.PressKey(key);
actions.MoveToElement(WindowsElement)
.ClickAndHold()
.Perform();
int dx = targetX - windowElement.Rect.X;
int dy = targetY - windowElement.Rect.Y;
int stepCount = 10;
int stepX = dx / stepCount;
int stepY = dy / stepCount;
for (int i = 0; i < stepCount; i++)
{
var stepAction = new Actions(Driver);
stepAction.MoveByOffset(stepX, stepY).Perform();
}
});
}
}
}

View File

@@ -1,131 +0,0 @@
// 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
namespace Microsoft.PowerToys.UITest
{
public class Slider : Element
{
private static readonly string ExpectedControlType = "ControlType.Slider";
/// <summary>
/// Initializes a new instance of the <see cref="Slider"/> class.
/// </summary>
public Slider()
{
this.TargetControlType = Slider.ExpectedControlType;
}
/// <summary>
/// Gets the value of a Slider (WindowsElement)
/// </summary>
/// <returns>The integer value of the slider</returns>
public int GetValue()
{
return this.Text == string.Empty ? 0 : int.Parse(this.Text);
}
/// <summary>
/// Sends a combination of keys.
/// </summary>
/// <param name="keys">The keys to send.</param>
public void SendKeys(params Key[] keys)
{
PerformAction((actions, windowElement) =>
{
KeyboardHelper.SendKeys(keys);
});
}
/// <summary>
/// Sets the value of a Slider (WindowsElement) to the specified integer value.
/// Throws an exception if the value is out of the slider's valid range.
/// </summary>
/// <param name="targetValue">The target integer value to set</param>
public void SetValue(int targetValue)
{
// Read range and current value
int min = int.Parse(this.GetAttribute("RangeValue.Minimum"));
int max = int.Parse(this.GetAttribute("RangeValue.Maximum"));
int current = int.Parse(this.Text);
// Use Assert to check if the target value is within the valid range
Assert.IsTrue(
targetValue >= min && targetValue <= max,
$"Target value {targetValue} is out of range (min: {min}, max: {max}).");
// Compute difference
int diff = targetValue - current;
if (diff == 0)
{
return;
}
string key = diff > 0 ? OpenQA.Selenium.Keys.Right : OpenQA.Selenium.Keys.Left;
int steps = Math.Abs(diff);
for (int i = 0; i < steps; i++)
{
this.SendKeys(key);
// Thread.Sleep(2);
}
// Final check
int finalValue = int.Parse(this.Text);
Assert.AreEqual(
targetValue, finalValue, $"Slider value mismatch: expected {targetValue}, but got {finalValue}.");
}
/// <summary>
/// Sets the value of a Slider (WindowsElement) to the specified integer value.
/// Throws an exception if the value is out of the slider's valid range.
/// </summary>
/// <param name="targetValue">The target integer value to set</param>
public void QuickSetValue(int targetValue)
{
// Read range and current value
int min = int.Parse(this.GetAttribute("RangeValue.Minimum"));
int max = int.Parse(this.GetAttribute("RangeValue.Maximum"));
int current = int.Parse(this.Text);
// Use Assert to check if the target value is within the valid range
Assert.IsTrue(
targetValue >= min && targetValue <= max,
$"Target value {targetValue} is out of range (min: {min}, max: {max}).");
// Compute difference
int diff = targetValue - current;
if (diff == 0)
{
return;
}
string key = diff > 0 ? OpenQA.Selenium.Keys.Right : OpenQA.Selenium.Keys.Left;
int steps = Math.Abs(diff);
int maxKeysPerSend = 50;
int fullChunks = steps / maxKeysPerSend;
int remainder = steps % maxKeysPerSend;
for (int i = 0; i < fullChunks; i++)
{
SendKeys(new string(key[0], maxKeysPerSend));
Thread.Sleep(2);
}
if (remainder > 0)
{
SendKeys(new string(key[0], remainder));
Thread.Sleep(2);
}
// Final check
int finalValue = int.Parse(this.Text);
Assert.AreEqual(
targetValue, finalValue, $"Slider value mismatch: expected {targetValue}, but got {finalValue}.");
}
}
}

View File

@@ -1,67 +0,0 @@
// 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
namespace Microsoft.PowerToys.UITest
{
public class Tab : Element
{
private static readonly string ExpectedControlType = "ControlType.Tab";
/// <summary>
/// Initializes a new instance of the <see cref="Tab"/> class.
/// </summary>
public Tab()
{
this.TargetControlType = Tab.ExpectedControlType;
}
/// <summary>
/// Simulates holding a key, clicking and dragging a UI element to the specified screen coordinates.
/// </summary>
/// <param name="key">The keyboard key to press and hold during the drag operation.</param>
/// <param name="targetX">The target X-coordinate to drag the element to.</param>
/// <param name="targetY">The target Y-coordinate to drag the element to.</param>
public void KeyDownAndDrag(Key key, int targetX, int targetY)
{
HoldShiftToDrag(key, targetX, targetY);
ReleaseAction();
ReleaseKey(key);
}
/// <summary>
/// Simulates holding a key, clicking and dragging a UI element to the specified screen coordinates.
/// </summary>
/// <param name="key">The keyboard key to press and hold during the drag operation.</param>
/// <param name="targetX">The target X-coordinate to drag the element to.</param>
/// <param name="targetY">The target Y-coordinate to drag the element to.</param>
public void HoldShiftToDrag(Key key, int targetX, int targetY)
{
PerformAction((actions, windowElement) =>
{
KeyboardHelper.PressKey(key);
actions.MoveToElement(WindowsElement)
.ClickAndHold()
.Perform();
int dx = targetX - windowElement.Rect.X;
int dy = targetY - windowElement.Rect.Y;
int stepCount = 10;
int stepX = dx / stepCount;
int stepY = dy / stepCount;
for (int i = 0; i < stepCount; i++)
{
var stepAction = new Actions(Driver);
stepAction.MoveByOffset(stepX, stepY).Perform();
}
});
}
}
}

View File

@@ -2,6 +2,8 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using OpenQA.Selenium;
namespace Microsoft.PowerToys.UITest
{
/// <summary>
@@ -33,10 +35,9 @@ namespace Microsoft.PowerToys.UITest
PerformAction((actions, windowElement) =>
{
// select all text and delete it
windowElement.SendKeys(OpenQA.Selenium.Keys.Control + "a");
windowElement.SendKeys(OpenQA.Selenium.Keys.Delete);
windowElement.SendKeys(Keys.Control + "a");
windowElement.SendKeys(Keys.Delete);
});
Task.Delay(500).Wait();
}
PerformAction((actions, windowElement) =>

View File

@@ -1,36 +0,0 @@
// 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
namespace Microsoft.PowerToys.UITest
{
public class Thumb : Element
{
private static readonly string ExpectedControlType = "ControlType.Thumb";
/// <summary>
/// Initializes a new instance of the <see cref="Thumb"/> class.
/// </summary>
public Thumb()
{
this.TargetControlType = Thumb.ExpectedControlType;
}
/// <summary>
/// Drag element move offset.
/// </summary>
/// <param name="offsetX">The offsetX to move.</param>
/// <param name="offsetY">The offsetY to move.</param>
public void Drag(int offsetX, int offsetY)
{
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement).MoveByOffset(10, 10).ClickAndHold(windowElement).MoveByOffset(offsetX, offsetY).Release();
actions.Build().Perform();
});
}
}
}

View File

@@ -17,10 +17,10 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
internal static class FindHelper
{
public static ReadOnlyCollection<T>? FindAll<T, TW>(Func<IReadOnlyCollection<TW>> findElementsFunc, WindowsDriver<WindowsElement>? driver, int timeoutMS)
public static ReadOnlyCollection<T>? FindAll<T, TW>(Func<ReadOnlyCollection<TW>> findElementsFunc, WindowsDriver<WindowsElement>? driver, int timeoutMS)
where T : Element, new()
{
var items = FindElementsWithRetry(findElementsFunc, timeoutMS);
var items = findElementsFunc();
var res = items.Select(item =>
{
var element = item as WindowsElement;
@@ -30,32 +30,6 @@ namespace Microsoft.PowerToys.UITest
return new ReadOnlyCollection<T>(res);
}
private static ReadOnlyCollection<TW> FindElementsWithRetry<TW>(Func<IReadOnlyCollection<TW>> findElementsFunc, int timeoutMS)
{
var timeout = TimeSpan.FromMilliseconds(timeoutMS);
var retryIntervalMS = TimeSpan.FromMilliseconds(500);
DateTime startTime = DateTime.Now;
while (DateTime.Now - startTime < timeout)
{
try
{
var items = findElementsFunc();
if (items.Count > 0)
{
return new ReadOnlyCollection<TW>((IList<TW>)items);
}
Task.Delay(retryIntervalMS).Wait();
}
catch (Exception)
{
}
}
return new ReadOnlyCollection<TW>(new List<TW>());
}
public static T NewElement<T>(WindowsElement? element, WindowsDriver<WindowsElement>? driver, int timeoutMS)
where T : Element, new()
{
@@ -63,6 +37,11 @@ namespace Microsoft.PowerToys.UITest
Assert.IsNotNull(element, $"New Element {typeof(T).Name} error: element is null.");
T newElement = new T();
if (timeoutMS > 0)
{
// Only set timeout if it is positive value
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(timeoutMS);
}
newElement.SetSession(driver);
newElement.SetWindowsElement(element);

View File

@@ -1,475 +0,0 @@
// 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 System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// Represents keyboard keys.
/// </summary>
public enum Key
{
Ctrl,
LCtrl,
RCtrl,
Alt,
Shift,
Tab,
Esc,
Enter,
Win,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
Num0,
Num1,
Num2,
Num3,
Num4,
Num5,
Num6,
Num7,
Num8,
Num9,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
Space,
Backspace,
Delete,
Insert,
Home,
End,
PageUp,
PageDown,
Up,
Down,
Left,
Right,
Other,
}
/// <summary>
    /// Provides methods for simulating keyboard input.
    /// </summary>
internal static class KeyboardHelper
{
[DllImport("user32.dll")]
#pragma warning disable SA1300 // Element should begin with upper-case letter
private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
#pragma warning restore SA1300 // Element should begin with upper-case letter
#pragma warning disable SA1310 // Field names should not contain underscore
private const byte VK_LWIN = 0x5B;
private const uint KEYEVENTF_KEYDOWN = 0x0000;
private const uint KEYEVENTF_KEYUP = 0x0002;
#pragma warning restore SA1310 // Field names should not contain underscore
/// <summary>
/// Sends a combination of keys.
/// </summary>
/// <param name="keys">The keys to send.</param>
public static void SendKeys(params Key[] keys)
{
string keysToSend = string.Join(string.Empty, keys.Select(TranslateKey));
SendWinKeyCombination(keysToSend);
}
public static void PressKey(Key key)
{
PressVirtualKey(TranslateKeyHex(key));
}
public static void ReleaseKey(Key key)
{
ReleaseVirtualKey(TranslateKeyHex(key));
}
public static void SendKey(Key key)
{
PressVirtualKey(TranslateKeyHex(key));
ReleaseVirtualKey(TranslateKeyHex(key));
}
/// <summary>
        /// Translates a key to its corresponding SendKeys representation.
        /// </summary>
        /// <param name="key">The key to translate.</param>
        /// <returns>The SendKeys representation of the key.</returns>
private static string TranslateKey(Key key)
{
switch (key)
{
case Key.Ctrl:
return "^";
case Key.LCtrl:
return "^";
case Key.RCtrl:
return "^";
case Key.Alt:
return "%";
case Key.Shift:
return "+";
case Key.Tab:
return "{TAB}";
case Key.Esc:
return "{ESC}";
case Key.Enter:
return "{ENTER}";
case Key.Win:
return "{WIN}";
case Key.Space:
return " ";
case Key.Backspace:
return "{BACKSPACE}";
case Key.Delete:
return "{DELETE}";
case Key.Insert:
return "{INSERT}";
case Key.Home:
return "{HOME}";
case Key.End:
return "{END}";
case Key.PageUp:
return "{PGUP}";
case Key.PageDown:
return "{PGDN}";
case Key.Up:
return "{UP}";
case Key.Down:
return "{DOWN}";
case Key.Left:
return "{LEFT}";
case Key.Right:
return "{RIGHT}";
case Key.F1:
return "{F1}";
case Key.F2:
return "{F2}";
case Key.F3:
return "{F3}";
case Key.F4:
return "{F4}";
case Key.F5:
return "{F5}";
case Key.F6:
return "{F6}";
case Key.F7:
return "{F7}";
case Key.F8:
return "{F8}";
case Key.F9:
return "{F9}";
case Key.F10:
return "{F10}";
case Key.F11:
return "{F11}";
case Key.F12:
return "{F12}";
case Key.A:
return "a";
case Key.B:
return "b";
case Key.C:
return "c";
case Key.D:
return "d";
case Key.E:
return "e";
case Key.F:
return "f";
case Key.G:
return "g";
case Key.H:
return "h";
case Key.I:
return "i";
case Key.J:
return "j";
case Key.K:
return "k";
case Key.L:
return "l";
case Key.M:
return "m";
case Key.N:
return "n";
case Key.O:
return "o";
case Key.P:
return "p";
case Key.Q:
return "q";
case Key.R:
return "r";
case Key.S:
return "s";
case Key.T:
return "t";
case Key.U:
return "u";
case Key.V:
return "v";
case Key.W:
return "w";
case Key.X:
return "x";
case Key.Y:
return "y";
case Key.Z:
return "z";
case Key.Num0:
return "0";
case Key.Num1:
return "1";
case Key.Num2:
return "2";
case Key.Num3:
return "3";
case Key.Num4:
return "4";
case Key.Num5:
return "5";
case Key.Num6:
return "6";
case Key.Num7:
return "7";
case Key.Num8:
return "8";
case Key.Num9:
return "9";
default:
return string.Empty;
}
}
/// <summary>
/// map the virtual key codes to the corresponding keys.
/// </summary>
private static byte TranslateKeyHex(Key key)
{
switch (key)
{
case Key.Win:
return 0x5B; // Windows Key - 0x5B in hex
case Key.Ctrl:
return 0x11; // Ctrl Key - 0x11 in hex
case Key.Alt:
return 0x12; // Alt Key - 0x12 in hex
case Key.Shift:
return 0x10; // Shift Key - 0x10 in hex
case Key.LCtrl:
return 0xA2; // Left Ctrl Key - 0xA2 in hex
case Key.RCtrl:
return 0xA3; // Right Ctrl Key - 0xA3 in hex
case Key.A:
return 0x41; // A Key - 0x41 in hex
case Key.B:
return 0x42; // B Key - 0x42 in hex
case Key.C:
return 0x43; // C Key - 0x43 in hex
case Key.D:
return 0x44; // D Key - 0x44 in hex
case Key.E:
return 0x45; // E Key - 0x45 in hex
case Key.F:
return 0x46; // F Key - 0x46 in hex
case Key.G:
return 0x47; // G Key - 0x47 in hex
case Key.H:
return 0x48; // H Key - 0x48 in hex
case Key.I:
return 0x49; // I Key - 0x49 in hex
case Key.J:
return 0x4A; // J Key - 0x4A in hex
case Key.K:
return 0x4B; // K Key - 0x4B in hex
case Key.L:
return 0x4C; // L Key - 0x4C in hex
case Key.M:
return 0x4D; // M Key - 0x4D in hex
case Key.N:
return 0x4E; // N Key - 0x4E in hex
case Key.O:
return 0x4F; // O Key - 0x4F in hex
case Key.P:
return 0x50; // P Key - 0x50 in hex
case Key.Q:
return 0x51; // Q Key - 0x51 in hex
case Key.R:
return 0x52; // R Key - 0x52 in hex
case Key.S:
return 0x53; // S Key - 0x53 in hex
case Key.T:
return 0x54; // T Key - 0x54 in hex
case Key.U:
return 0x55; // U Key - 0x55 in hex
case Key.V:
return 0x56; // V Key - 0x56 in hex
case Key.W:
return 0x57; // W Key - 0x57 in hex
case Key.X:
return 0x58; // X Key - 0x58 in hex
case Key.Y:
return 0x59; // Y Key - 0x59 in hex
case Key.Z:
return 0x5A; // Z Key - 0x5A in hex
case Key.Num0:
return 0x30; // 0 Key - 0x30 in hex
case Key.Num1:
return 0x31; // 1 Key - 0x31 in hex
case Key.Num2:
return 0x32; // 2 Key - 0x32 in hex
case Key.Num3:
return 0x33; // 3 Key - 0x33 in hex
case Key.Num4:
return 0x34; // 4 Key - 0x34 in hex
case Key.Num5:
return 0x35; // 5 Key - 0x35 in hex
case Key.Num6:
return 0x36; // 6 Key - 0x36 in hex
case Key.Num7:
return 0x37; // 7 Key - 0x37 in hex
case Key.Num8:
return 0x38; // 8 Key - 0x38 in hex
case Key.Num9:
return 0x39; // 9 Key - 0x39 in hex
case Key.F1:
return 0x70; // F1 Key - 0x70 in hex
case Key.F2:
return 0x71; // F2 Key - 0x71 in hex
case Key.F3:
return 0x72; // F3 Key - 0x72 in hex
case Key.F4:
return 0x73; // F4 Key - 0x73 in hex
case Key.F5:
return 0x74; // F5 Key - 0x74 in hex
case Key.F6:
return 0x75; // F6 Key - 0x75 in hex
case Key.F7:
return 0x76; // F7 Key - 0x76 in hex
case Key.F8:
return 0x77; // F8 Key - 0x77 in hex
case Key.F9:
return 0x78; // F9 Key - 0x78 in hex
case Key.F10:
return 0x79; // F10 Key - 0x79 in hex
case Key.F11:
return 0x7A; // F11 Key - 0x7A in hex
case Key.F12:
return 0x7B; // F12 Key - 0x7B in hex
case Key.Up:
return 0x26; // Up Arrow Key - 0x26 in hex
case Key.Down:
return 0x28; // Down Arrow Key - 0x28 in hex
case Key.Left:
return 0x25; // Left Arrow Key - 0x25 in hex
case Key.Right:
return 0x27; // Right Arrow Key - 0x27 in hex
case Key.Home:
return 0x24; // Home Key - 0x24 in hex
case Key.End:
return 0x23; // End Key - 0x23 in hex
case Key.PageUp:
return 0x21; // Page Up Key - 0x21 in hex
case Key.PageDown:
return 0x22; // Page Down Key - 0x22 in hex
case Key.Space:
return 0x20; // Space Key - 0x20 in hex
case Key.Enter:
return 0x0D; // Enter Key - 0x0D in hex
case Key.Backspace:
return 0x08; // Backspace Key - 0x08 in hex
case Key.Tab:
return 0x09; // Tab Key - 0x09 in hex
case Key.Esc:
return 0x1B; // Escape Key - 0x1B in hex
case Key.Insert:
return 0x2D; // Insert Key - 0x2D in hex
case Key.Delete:
return 0x2E; // Delete Key - 0x2E in hex
default:
throw new ArgumentException($"Key {key} is not supported, Please add your key at TranslateKeyHex for translation to hex.");
}
}
/// <summary>
/// Sends a combination of keys, including the Windows key, to the system.
/// </summary>
/// <param name="keys">The keys to send.</param>
private static void SendWinKeyCombination(string keys)
{
bool winKeyDown = false;
if (keys.Contains("{WIN}"))
{
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYDOWN, UIntPtr.Zero);
winKeyDown = true;
keys = keys.Replace("{WIN}", string.Empty); // Remove {WIN} from the string
        }
System.Windows.Forms.SendKeys.SendWait(keys);
        // Release Windows key
if (winKeyDown)
{
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, UIntPtr.Zero);
}
}
/// <summary>
/// Just press the key.(no release)
/// </summary>
private static void PressVirtualKey(byte key)
{
keybd_event(key, 0, KEYEVENTF_KEYDOWN, UIntPtr.Zero);
}
/// <summary>
/// Release only the button (if pressed first)
/// </summary>
private static void ReleaseVirtualKey(byte key)
{
keybd_event(key, 0, KEYEVENTF_KEYUP, UIntPtr.Zero);
}
}
}

View File

@@ -29,49 +29,6 @@ namespace Microsoft.PowerToys.UITest
PowerToysSettings,
FancyZone,
Hosts,
Runner,
Workspaces,
}
/// <summary>
/// Represents the window size for the UI test.
/// </summary>
public enum WindowSize
{
/// <summary>
/// Unspecified window size, won't make any size change
/// </summary>
UnSpecified,
/// <summary>
/// Small window size, 640 * 480
/// </summary>
Small,
/// <summary>
/// Small window size, 480 * 640
/// </summary>
Small_Vertical,
/// <summary>
/// Medium window size, 1024 * 768
/// </summary>
Medium,
/// <summary>
/// Medium window size, 768 * 1024
/// </summary>
Medium_Vertical,
/// <summary>
/// Large window size, 1920 * 1080
/// </summary>
Large,
/// <summary>
/// Large window size, 1080 * 1920
/// </summary>
Large_Vertical,
}
internal class ModuleConfigData
@@ -95,8 +52,6 @@ namespace Microsoft.PowerToys.UITest
[PowerToysModule.PowerToysSettings] = "PowerToys Settings",
[PowerToysModule.FancyZone] = "FancyZones Layout",
[PowerToysModule.Hosts] = "Hosts File Editor",
[PowerToysModule.Runner] = "PowerToys",
[PowerToysModule.Workspaces] = "Workspaces Editor",
};
// Exe start path for the module if it exists.
@@ -105,8 +60,6 @@ namespace Microsoft.PowerToys.UITest
[PowerToysModule.PowerToysSettings] = @"\..\..\..\WinUI3Apps\PowerToys.Settings.exe",
[PowerToysModule.FancyZone] = @"\..\..\..\PowerToys.FancyZonesEditor.exe",
[PowerToysModule.Hosts] = @"\..\..\..\WinUI3Apps\PowerToys.Hosts.exe",
[PowerToysModule.Runner] = @"\..\..\..\PowerToys.exe",
[PowerToysModule.Workspaces] = @"\..\..\..\PowerToys.WorkspacesEditor.exe",
};
}

View File

@@ -1,37 +0,0 @@
// 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.Generic;
namespace Microsoft.PowerToys.UITest
{
public class MonitorInfoData
{
public MonitorInfoData()
{
}
public struct MonitorInfoDataWrapper
{
public string DeviceName { get; set; }
public string DeviceString { get; set; }
public string DeviceID { get; set; }
public string DeviceKey { get; set; }
public int PelsWidth { get; set; }
public int PelsHeight { get; set; }
public int DisplayFrequency { get; set; }
}
public struct ParamsWrapper
{
public List<MonitorInfoDataWrapper> Monitors { get; set; }
}
}
}

View File

@@ -1,217 +0,0 @@
// 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 System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.PowerToys.UITest
{
public enum MouseActionType
{
LeftClick,
RightClick,
MiddleClick,
LeftDoubleClick,
RightDoubleClick,
LeftDown,
LeftUp,
RightDown,
RightUp,
MiddleDown,
MiddleUp,
ScrollUp,
ScrollDown,
}
internal static class MouseHelper
{
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
[Flags]
internal enum MouseEvent
{
LeftDown = 0x0002,
LeftUp = 0x0004,
RightDown = 0x0008,
RightUp = 0x0010,
MiddleDown = 0x0020,
MiddleUp = 0x0040,
Wheel = 0x0800,
}
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out POINT lpPoint);
[DllImport("user32.dll")]
public static extern bool SetCursorPos(int x, int y);
[DllImport("user32.dll")]
#pragma warning disable SA1300 // Element should begin with upper-case letter
private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo);
#pragma warning restore SA1300 // Element should begin with upper-case letter
        /// <summary>
        /// Gets the current position of the mouse cursor as a tuple.
        /// </summary>
        /// <returns>A tuple containing the X and Y coordinates of the cursor.</returns>
public static Tuple<int, int> GetMousePosition()
{
GetCursorPos(out POINT point);
return Tuple.Create(point.X, point.Y);
}
/// <summary>
    /// Moves the mouse cursor to the specified screen coordinates.
    /// </summary>
    /// <param name="x">The new x-coordinate of the cursor.</param>
    /// <param name="y">The new y-coordinate of the cursor.</param
public static void MoveMouseTo(int x, int y)
{
SetCursorPos(x, y);
}
/// <summary>
/// The delay in milliseconds between mouse down and up events to simulate a click.
/// </summary>
private const int ClickDelay = 100;
/// <summary>
/// The amount of scroll units to simulate a single mouse wheel tick.
/// </summary>
private const int ScrollAmount = 120;
/// <summary>
/// Simulates a left mouse click (press and release).
/// </summary>
public static void LeftClick()
{
LeftDown();
Thread.Sleep(ClickDelay);
LeftUp();
}
/// <summary>
/// Simulates a right mouse click (press and release).
/// </summary>
public static void RightClick()
{
RightDown();
Thread.Sleep(ClickDelay);
RightUp();
}
/// <summary>
/// Simulates a middle mouse click (press and release).
/// </summary>
public static void MiddleClick()
{
MiddleDown();
Thread.Sleep(ClickDelay);
MiddleUp();
}
/// <summary>
/// Simulates a left mouse double-click.
/// </summary>
public static void LeftDoubleClick()
{
LeftClick();
Thread.Sleep(ClickDelay);
LeftClick();
}
/// <summary>
/// Simulates a right mouse double-click.
/// </summary>
public static void RightDoubleClick()
{
RightClick();
Thread.Sleep(ClickDelay);
RightClick();
}
/// <summary>
/// Simulates pressing the left mouse button down.
/// </summary>
public static void LeftDown()
{
mouse_event((uint)MouseEvent.LeftDown, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// Simulates pressing the right mouse button down.
/// </summary>
public static void RightDown()
{
mouse_event((uint)MouseEvent.RightDown, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// Simulates pressing the middle mouse button down.
/// </summary>
public static void MiddleDown()
{
mouse_event((uint)MouseEvent.MiddleDown, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// Simulates releasing the left mouse button.
/// </summary>
public static void LeftUp()
{
mouse_event((uint)MouseEvent.LeftUp, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// Simulates releasing the right mouse button.
/// </summary>
public static void RightUp()
{
mouse_event((uint)MouseEvent.RightUp, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// Simulates releasing the middle mouse button.
/// </summary>
public static void MiddleUp()
{
mouse_event((uint)MouseEvent.MiddleUp, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// Simulates a mouse scroll wheel action by a specified amount.
/// Positive values scroll up, negative values scroll down.
/// </summary>
/// <param name="amount">The scroll amount. Typically 120 or -120 per tick.</param>
public static void ScrollWheel(int amount)
{
mouse_event((uint)MouseEvent.Wheel, 0, 0, (uint)amount, UIntPtr.Zero);
}
/// <summary>
/// Simulates scrolling the mouse wheel up by one tick.
/// </summary>
public static void ScrollUp()
{
ScrollWheel(ScrollAmount);
}
/// <summary>
/// Simulates scrolling the mouse wheel down by one tick.
/// </summary>
public static void ScrollDown()
{
ScrollWheel(-ScrollAmount);
}
}
}

View File

@@ -1,133 +0,0 @@
// 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 System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// Provides methods for capturing the screen with the mouse cursor.
/// </summary>
internal static class ScreenCapture
{
[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("gdi32.dll")]
private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[DllImport("user32.dll")]
private static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
private static extern bool GetCursorInfo(out CURSORINFO pci);
[DllImport("user32.dll")]
private static extern bool DrawIconEx(IntPtr hdc, int x, int y, IntPtr hIcon, int cx, int cy, int istepIfAniCur, IntPtr hbrFlickerFreeDraw, int diFlags);
/// <summary>
/// Represents a point with X and Y coordinates.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
/// <summary>
/// Contains information about the cursor.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct CURSORINFO
{
/// <summary>
/// Gets or sets the size of the structure.
/// </summary>
public int CbSize;
/// <summary>
/// Gets or sets the cursor state.
/// </summary>
public int Flags;
/// <summary>
/// Gets or sets the handle to the cursor.
/// </summary>
public IntPtr HCursor;
/// <summary>
/// Gets or sets the screen position of the cursor.
/// </summary>
public POINT PTScreenPos;
}
private const int CURSORSHOWING = 0x00000001;
private const int DESKTOPHORZRES = 118;
private const int DESKTOPVERTRES = 117;
private const int DINORMAL = 0x0003;
/// <summary>
/// Captures the screen with the mouse cursor and saves it to the specified file path.
/// </summary>
/// <param name="filePath">The file path to save the captured image.</param>
private static void CaptureScreenWithMouse(string filePath)
{
IntPtr hdc = GetDC(IntPtr.Zero);
int screenWidth = GetDeviceCaps(hdc, DESKTOPHORZRES);
int screenHeight = GetDeviceCaps(hdc, DESKTOPVERTRES);
ReleaseDC(IntPtr.Zero, hdc);
Rectangle bounds = new Rectangle(0, 0, screenWidth, screenHeight);
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap))
{
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
CURSORINFO cursorInfo;
cursorInfo.CbSize = Marshal.SizeOf<CURSORINFO>();
if (GetCursorInfo(out cursorInfo) && cursorInfo.Flags == CURSORSHOWING)
{
using (System.Drawing.Graphics gIcon = System.Drawing.Graphics.FromImage(bitmap))
{
IntPtr hdcDest = gIcon.GetHdc();
DrawIconEx(hdcDest, cursorInfo.PTScreenPos.X, cursorInfo.PTScreenPos.Y, cursorInfo.HCursor, 0, 0, 0, IntPtr.Zero, DINORMAL);
gIcon.ReleaseHdc(hdcDest);
}
}
}
bitmap.Save(filePath, ImageFormat.Png);
}
}
/// <summary>
/// Captures a screenshot and saves it to the specified directory.
/// </summary>
/// <param name="directory">The directory to save the screenshot.</param>
private static void CaptureScreenshot(string directory)
{
string filePath = Path.Combine(directory, $"screenshot_{DateTime.Now:yyyyMMdd_HHmmssfff}.png");
CaptureScreenWithMouse(filePath);
}
/// <summary>
/// Timer callback method to capture a screenshot.
/// </summary>
/// <param name="state">The state object passed to the callback method.</param>
public static void TimerCallback(object? state)
{
string directory = (string)state!;
CaptureScreenshot(directory);
}
}
}

View File

@@ -1,17 +1,13 @@
// Copyright (c) Microsoft Corporation
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
using static Microsoft.PowerToys.UITest.WindowHelper;
namespace Microsoft.PowerToys.UITest
{
@@ -20,70 +16,35 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
public class Session
{
public WindowsDriver<WindowsElement> Root { get; set; }
private WindowsDriver<WindowsElement> Root { get; set; }
private WindowsDriver<WindowsElement> WindowsDriver { get; set; }
private List<IntPtr> windowHandlers = new List<IntPtr>();
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(nint hWnd);
private Window? MainWindow { get; set; }
/// <summary>
/// Gets Main Window Handler
/// </summary>
public IntPtr MainWindowHandler { get; private set; }
/// <summary>
/// Gets Init Scope
/// </summary>
public PowerToysModule InitScope { get; private set; }
/// <summary>
/// Gets the RunAsAdmin flag.
/// If true, the session is running as admin.
/// If false, the session is not running as admin.
/// If null, no information is available.
/// </summary>
public bool? IsElevated { get; private set; }
public Session(WindowsDriver<WindowsElement> pRoot, WindowsDriver<WindowsElement> pDriver, PowerToysModule scope, WindowSize size)
public Session(WindowsDriver<WindowsElement> root, WindowsDriver<WindowsElement> windowsDriver)
{
this.MainWindowHandler = IntPtr.Zero;
this.Root = pRoot;
this.WindowsDriver = pDriver;
this.InitScope = scope;
if (size != WindowSize.UnSpecified)
{
// Attach to the scope & reset MainWindowHandler
this.Attach(scope, size);
}
this.Root = root;
this.WindowsDriver = windowsDriver;
}
/// <summary>
/// Cleans up the Session Exe.
/// </summary>
public void Cleanup()
{
windowHandlers.Clear();
}
/// <summary>
/// Finds an Element or its derived class by selector.
/// Finds an element by selector.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
public T Find<T>(By by, int timeoutMS = 5000, bool global = false)
public T Find<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
Assert.IsNotNull(this.WindowsDriver, $"WindowsElement is null in method Find<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
// leverage findAll to filter out mismatched elements
var collection = this.FindAll<T>(by, timeoutMS, global);
var collection = this.FindAll<T>(by, timeoutMS);
Assert.IsTrue(collection.Count > 0, $"UI-Element({typeof(T).Name}) not found using selector: {by}");
Assert.IsTrue(collection.Count > 0, $"Element not found using selector: {by}");
return collection[0];
}
@@ -93,159 +54,54 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
public T Find<T>(string name, int timeoutMS = 5000, bool global = false)
public T Find<T>(string name, int timeoutMS = 3000)
where T : Element, new()
{
return this.Find<T>(By.Name(name), timeoutMS, global);
return this.Find<T>(By.Name(name), timeoutMS);
}
/// <summary>
/// Shortcut for this.Find<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
public Element Find(By by, int timeoutMS = 5000, bool global = false)
public Element Find(By by, int timeoutMS = 3000)
{
return this.Find<Element>(by, timeoutMS, global);
return this.Find<Element>(by, timeoutMS);
}
/// <summary>
/// Shortcut for this.Find<Element>(By.Name(name), timeoutMS)
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
public Element Find(string name, int timeoutMS = 5000, bool global = false)
public Element Find(string name, int timeoutMS = 3000)
{
return this.Find<Element>(By.Name(name), timeoutMS, global);
return this.Find<Element>(By.Name(name), timeoutMS);
}
/// <summary>
/// Has only one Element or its derived class by selector.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne<T>(By by, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.FindAll<T>(by, timeoutMS, global).Count == 1;
}
/// <summary>
/// Shortcut for this.HasOne<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne(By by, int timeoutMS = 5000, bool global = false)
{
return this.HasOne<Element>(by, timeoutMS, global);
}
/// <summary>
/// Shortcut for this.HasOne<T>(By.Name(name), timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne<T>(string name, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.HasOne<T>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Shortcut for this.HasOne<Element>(name, timeoutMS)
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne(string name, int timeoutMS = 5000, bool global = false)
{
return this.HasOne<Element>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Has one or more Element or its derived class by selector.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has<T>(By by, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.FindAll<T>(by, timeoutMS, global).Count >= 1;
}
/// <summary>
/// Shortcut for this.Has<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has(By by, int timeoutMS = 5000, bool global = false)
{
return this.Has<Element>(by, timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Has<T>(By.Name(name), timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has<T>(string name, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.Has<T>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Has<Element>(name, timeoutMS)
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has(string name, int timeoutMS = 5000, bool global = false)
{
return this.Has<Element>(name, timeoutMS, global);
}
/// <summary>
/// Finds all Element or its derived class by selector.
/// Finds all elements by selector.
/// </summary>
/// <typeparam name="T">The class of the elements, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 5000, bool global = false)
public ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
var driver = global ? this.Root : this.WindowsDriver;
Assert.IsNotNull(driver, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
Assert.IsNotNull(this.WindowsDriver, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
var foundElements = FindHelper.FindAll<T, WindowsElement>(
() =>
{
if (by.GetIsAccessibilityId())
{
var elements = driver.FindElementsByAccessibilityId(by.GetAccessibilityId());
return elements;
}
else
{
var elements = driver.FindElements(by.ToSeleniumBy());
return elements;
}
var elements = this.WindowsDriver.FindElements(by.ToSeleniumBy());
return elements;
},
driver,
this.WindowsDriver,
timeoutMS);
return foundElements ?? new ReadOnlyCollection<T>([]);
@@ -257,12 +113,12 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <typeparam name="T">The class of the elements, should be Element or its derived class.</typeparam>
/// <param name="name">The name to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<T> FindAll<T>(string name, int timeoutMS = 5000, bool global = false)
public ReadOnlyCollection<T> FindAll<T>(string name, int timeoutMS = 3000)
where T : Element, new()
{
return this.FindAll<T>(By.Name(name), timeoutMS, global);
return this.FindAll<T>(By.Name(name), timeoutMS);
}
/// <summary>
@@ -270,11 +126,11 @@ namespace Microsoft.PowerToys.UITest
/// Shortcut for this.FindAll<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<Element> FindAll(By by, int timeoutMS = 5000, bool global = false)
public ReadOnlyCollection<Element> FindAll(By by, int timeoutMS = 3000)
{
return this.FindAll<Element>(by, timeoutMS, global);
return this.FindAll<Element>(by, timeoutMS);
}
/// <summary>
@@ -282,186 +138,22 @@ namespace Microsoft.PowerToys.UITest
/// Shortcut for this.FindAll<Element>(By.Name(name), timeoutMS)
/// </summary>
/// <param name="name">The name to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<Element> FindAll(string name, int timeoutMS = 5000, bool global = false)
public ReadOnlyCollection<Element> FindAll(string name, int timeoutMS = 3000)
{
return this.FindAll<Element>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Close the main window.
/// </summary>
public void CloseMainWindow()
{
if (MainWindow != null)
{
MainWindow.Close();
MainWindow = null;
}
}
/// <summary>
/// Sends a combination of keys.
/// </summary>
/// <param name="keys">The keys to send.</param>
public void SendKeys(params Key[] keys)
{
PerformAction(() =>
{
KeyboardHelper.SendKeys(keys);
});
}
/// <summary>
/// release the key (after the hold key and drag is completed.)
/// </summary>
/// <param name="key">The key release.</param>
public void PressKey(Key key)
{
PerformAction(() =>
{
KeyboardHelper.PressKey(key);
});
}
/// <summary>
/// press and hold the specified key.
/// </summary>
/// <param name="key">The key to press and hold .</param>
public void ReleaseKey(Key key)
{
PerformAction(() =>
{
KeyboardHelper.ReleaseKey(key);
});
}
/// <summary>
/// press and hold the specified key.
/// </summary>
/// <param name="key">The key to press and release .</param>
public void SendKey(Key key, int msPreAction = 500, int msPostAction = 500)
{
PerformAction(
() =>
{
KeyboardHelper.SendKey(key);
},
msPreAction,
msPostAction);
}
/// <summary>
/// Sends a sequence of keys.
/// </summary>
/// <param name="keys">An array of keys to send.</param>
public void SendKeySequence(params Key[] keys)
{
PerformAction(() =>
{
foreach (var key in keys)
{
KeyboardHelper.SendKeys(key);
}
});
}
        /// <summary>
        /// Gets the current position of the mouse cursor as a tuple.
        /// </summary>
        /// <returns>A tuple containing the X and Y coordinates of the cursor.</returns>
public Tuple<int, int> GetMousePosition()
{
return MouseHelper.GetMousePosition();
}
/// <summary>
    /// Moves the mouse cursor to the specified screen coordinates.
    /// </summary>
    /// <param name="x">The new x-coordinate of the cursor.</param>
    /// <param name="y">The new y-coordinate of the cursor.</param
public void MoveMouseTo(int x, int y, int msPreAction = 500, int msPostAction = 500)
{
PerformAction(
() =>
{
MouseHelper.MoveMouseTo(x, y);
},
msPreAction,
msPostAction);
}
/// <summary>
/// Performs a mouse action based on the specified action type.
/// </summary>
/// <param name="action">The mouse action to perform.</param>
/// <param name="msPreAction">Pre-action delay in milliseconds.</param>
/// <param name="msPostAction">Post-action delay in milliseconds.</param>
public void PerformMouseAction(MouseActionType action, int msPreAction = 500, int msPostAction = 500)
{
PerformAction(
() =>
{
switch (action)
{
case MouseActionType.LeftClick:
MouseHelper.LeftClick();
break;
case MouseActionType.RightClick:
MouseHelper.RightClick();
break;
case MouseActionType.MiddleClick:
MouseHelper.MiddleClick();
break;
case MouseActionType.LeftDoubleClick:
MouseHelper.LeftDoubleClick();
break;
case MouseActionType.RightDoubleClick:
MouseHelper.RightDoubleClick();
break;
case MouseActionType.LeftDown:
MouseHelper.LeftDown();
break;
case MouseActionType.LeftUp:
MouseHelper.LeftUp();
break;
case MouseActionType.RightDown:
MouseHelper.RightDown();
break;
case MouseActionType.RightUp:
MouseHelper.RightUp();
break;
case MouseActionType.MiddleDown:
MouseHelper.MiddleDown();
break;
case MouseActionType.MiddleUp:
MouseHelper.MiddleUp();
break;
case MouseActionType.ScrollUp:
MouseHelper.ScrollUp();
break;
case MouseActionType.ScrollDown:
MouseHelper.ScrollDown();
break;
default:
throw new ArgumentException("Unsupported mouse action.", nameof(action));
}
},
msPreAction,
msPostAction);
return this.FindAll<Element>(By.Name(name), timeoutMS);
}
/// <summary>
/// Attaches to an existing PowerToys module.
/// </summary>
/// <param name="module">The PowerToys module to attach to.</param>
/// <param name="size">The window size to set. Default is no change to window size</param>
/// <returns>The attached session.</returns>
public Session Attach(PowerToysModule module, WindowSize size = WindowSize.UnSpecified)
public Session Attach(PowerToysModule module)
{
string windowName = ModuleConfigData.Instance.GetModuleWindowName(module);
return this.Attach(windowName, size);
return this.Attach(windowName);
}
/// <summary>
@@ -469,190 +161,33 @@ namespace Microsoft.PowerToys.UITest
/// The session should be attached when a new app is started.
/// </summary>
/// <param name="windowName">The window name to attach to.</param>
/// <param name="size">The window size to set. Default is no change to window size</param>
/// <returns>The attached session.</returns>
public Session Attach(string windowName, WindowSize size = WindowSize.UnSpecified)
public Session Attach(string windowName)
{
this.IsElevated = null;
this.MainWindowHandler = IntPtr.Zero;
if (this.Root != null)
{
// search window handler by window title (admin and non-admin titles)
var timeout = TimeSpan.FromMinutes(2);
var retryInterval = TimeSpan.FromSeconds(5);
DateTime startTime = DateTime.Now;
List<(IntPtr HWnd, string Title)>? matchingWindows = null;
while (DateTime.Now - startTime < timeout)
{
matchingWindows = WindowHelper.ApiHelper.FindDesktopWindowHandler(
new[] { windowName, WindowHelper.AdministratorPrefix + windowName });
if (matchingWindows.Count > 0 && matchingWindows[0].HWnd != IntPtr.Zero)
{
break;
}
Task.Delay(retryInterval).Wait();
}
if (matchingWindows == null || matchingWindows.Count == 0 || matchingWindows[0].HWnd == IntPtr.Zero)
{
Assert.Fail($"Failed to attach. Window '{windowName}' not found after {timeout.TotalSeconds} seconds.");
}
// pick one from matching windows
this.MainWindowHandler = matchingWindows[0].HWnd;
this.IsElevated = matchingWindows[0].Title.StartsWith(WindowHelper.AdministratorPrefix);
ApiHelper.SetForegroundWindow(this.MainWindowHandler);
var hexWindowHandle = this.MainWindowHandler.ToInt64().ToString("x");
var window = this.Root.FindElementByName(windowName);
Assert.IsNotNull(window, $"Failed to attach. Window '{windowName}' not found");
var windowHandle = new nint(int.Parse(window.GetAttribute("NativeWindowHandle")));
SetForegroundWindow(windowHandle);
var hexWindowHandle = windowHandle.ToString("x");
var appCapabilities = new AppiumOptions();
appCapabilities.AddAdditionalCapability("appTopLevelWindow", hexWindowHandle);
appCapabilities.AddAdditionalCapability("deviceName", "WindowsPC");
this.WindowsDriver = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), appCapabilities);
Assert.IsNotNull(this.WindowsDriver, "Attach WindowsDriver is null");
this.windowHandlers.Add(this.MainWindowHandler);
if (size != WindowSize.UnSpecified)
{
WindowHelper.SetWindowSize(this.MainWindowHandler, size);
}
// Set MainWindow
MainWindow = Find<Window>(matchingWindows[0].Title);
// Set implicit timeout to make element search retry every 500 ms
this.WindowsDriver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(3);
}
else
{
Assert.IsNotNull(this.Root, $"Failed to attach to the window '{windowName}'. Root driver is null");
}
Task.Delay(3000).Wait();
return this;
}
/// <summary>
/// Sets the main window size.
/// </summary>
/// <param name="size">WindowSize enum</param>
public void SetMainWindowSize(WindowSize size)
{
if (this.MainWindowHandler == IntPtr.Zero)
{
// Attach to the scope & reset MainWindowHandler
this.Attach(this.InitScope);
}
WindowHelper.SetWindowSize(this.MainWindowHandler, size);
}
/// <summary>
/// Gets the main window center coordinates.
/// </summary>
/// <returns>(x, y)</returns>
public (int CenterX, int CenterY) GetMainWindowCenter()
{
return WindowHelper.GetWindowCenter(this.MainWindowHandler);
}
/// <summary>
/// Gets the main window center coordinates.
/// </summary>
/// <returns>(int Left, int Top, int Right, int Bottom)</returns>
public (int Left, int Top, int Right, int Bottom) GetMainWindowRect()
{
return WindowHelper.GetWindowRect(this.MainWindowHandler);
}
/// <summary>
/// Launches the specified executable with optional arguments and simulates a delay before and after execution.
/// </summary>
/// <param name="executablePath">The full path to the executable to launch.</param>
/// <param name="arguments">Optional command-line arguments to pass to the executable.</param>
/// <param name="msPreAction">The number of milliseconds to wait before launching the executable. Default is 0 ms.</param>
/// <param name="msPostAction">The number of milliseconds to wait after launching the executable. Default is 2000 ms.</param>
public void StartExe(string executablePath, string arguments = "", int msPreAction = 0, int msPostAction = 2000)
{
PerformAction(
() =>
{
StartExeInternal(executablePath, arguments);
},
msPreAction,
msPostAction);
}
private void StartExeInternal(string executablePath, string arguments = "")
{
var processInfo = new ProcessStartInfo
{
FileName = executablePath,
Arguments = arguments,
UseShellExecute = true,
};
Process.Start(processInfo);
}
/// <summary>
/// Terminates all running processes that match the specified process name.
/// Waits for each process to exit after sending the kill signal.
/// </summary>
/// <param name="processName">The name of the process to terminate (without extension, e.g., "notepad").</param>
public void KillAllProcessesByName(string processName)
{
foreach (var process in Process.GetProcessesByName(processName))
{
process.Kill();
process.WaitForExit();
}
}
/// <summary>
/// Simulates a manual operation on the element.
/// </summary>
/// <param name="action">The action to perform on the element.</param>
/// <param name="msPreAction">The number of milliseconds to wait before the action. Default value is 500 ms</param>
/// <param name="msPostAction">The number of milliseconds to wait after the action. Default value is 500 ms</param>
protected void PerformAction(Action<Actions, WindowsDriver<WindowsElement>> action, int msPreAction = 500, int msPostAction = 500)
{
if (msPreAction > 0)
{
Task.Delay(msPreAction).Wait();
}
var windowsDriver = this.WindowsDriver;
Actions actions = new Actions(this.WindowsDriver);
action(actions, windowsDriver);
if (msPostAction > 0)
{
Task.Delay(msPostAction).Wait();
}
}
/// <summary>
/// Simulates a manual operation on the element.
/// </summary>
/// <param name="action">The action to perform on the element.</param>
/// <param name="msPreAction">The number of milliseconds to wait before the action. Default value is 500 ms</param>
/// <param name="msPostAction">The number of milliseconds to wait after the action. Default value is 500 ms</param>
protected void PerformAction(Action action, int msPreAction = 500, int msPostAction = 500)
{
if (msPreAction > 0)
{
Task.Delay(msPreAction).Wait();
}
action();
if (msPostAction > 0)
{
Task.Delay(msPostAction).Wait();
}
}
}
}

View File

@@ -2,7 +2,6 @@
// 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.Diagnostics.CodeAnalysis;
using System.Reflection;
@@ -15,61 +14,52 @@ namespace Microsoft.PowerToys.UITest
/// <summary>
/// Nested class for test initialization.
/// </summary>
public class SessionHelper
internal class SessionHelper
{
// Default session path is PowerToys settings dashboard
private readonly string sessionPath = ModuleConfigData.Instance.GetModulePath(PowerToysModule.PowerToysSettings);
private readonly string runnerPath = ModuleConfigData.Instance.GetModulePath(PowerToysModule.Runner);
private string? locationPath;
private WindowsDriver<WindowsElement> Root { get; set; }
private WindowsDriver<WindowsElement>? Driver { get; set; }
private Process? appDriver;
private Process? runner;
private PowerToysModule scope;
[UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", Justification = "<Pending>")]
public SessionHelper(PowerToysModule scope)
{
this.scope = scope;
this.sessionPath = ModuleConfigData.Instance.GetModulePath(scope);
this.locationPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
this.StartWindowsAppDriverApp();
var runnerProcessInfo = new ProcessStartInfo
var winAppDriverProcessInfo = new ProcessStartInfo
{
FileName = locationPath + this.runnerPath,
FileName = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe",
Verb = "runas",
};
if (scope == PowerToysModule.PowerToysSettings)
{
this.ExitExe(runnerProcessInfo.FileName);
this.runner = Process.Start(runnerProcessInfo);
}
this.appDriver = Process.Start(winAppDriverProcessInfo);
var desktopCapabilities = new AppiumOptions();
desktopCapabilities.AddAdditionalCapability("app", "Root");
this.Root = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), desktopCapabilities);
// Set default timeout to 5 seconds
this.Root.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
}
/// <summary>
/// Initializes the test environment.
/// </summary>
/// <param name="scope">The PowerToys module to start.</param>
[UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", Justification = "<Pending>")]
public SessionHelper Init()
{
this.ExitExe(this.locationPath + this.sessionPath);
this.StartExe(this.locationPath + this.sessionPath);
string? path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
this.StartExe(path + this.sessionPath);
Assert.IsNotNull(this.Driver, $"Failed to initialize the test environment. Driver is null.");
// Set default timeout to 5 seconds
this.Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
return this;
}
@@ -78,16 +68,9 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
public void Cleanup()
{
ExitScopeExe();
try
{
appDriver?.Kill();
appDriver?.WaitForExit(); // Optional: Wait for the process to exit
if (this.scope == PowerToysModule.PowerToysSettings)
{
runner?.Kill();
runner?.WaitForExit(); // Optional: Wait for the process to exit
}
}
catch (Exception ex)
{
@@ -96,30 +79,6 @@ namespace Microsoft.PowerToys.UITest
}
}
/// <summary>
/// Exit a exe.
/// </summary>
/// <param name="appPath">The path to the application executable.</param>
public void ExitExe(string appPath)
{
// Exit Exe
string exeName = Path.GetFileNameWithoutExtension(appPath);
Process[] processes = Process.GetProcessesByName(exeName);
foreach (Process process in processes)
{
try
{
process.Kill();
process.WaitForExit(); // Optional: Wait for the process to exit
}
catch (Exception ex)
{
Assert.Fail($"Failed to terminate process {process.ProcessName} (ID: {process.Id}): {ex.Message}");
}
}
}
/// <summary>
/// Starts a new exe and takes control of it.
/// </summary>
@@ -128,62 +87,7 @@ namespace Microsoft.PowerToys.UITest
{
var opts = new AppiumOptions();
opts.AddAdditionalCapability("app", appPath);
this.Driver = NewWindowsDriver(opts);
}
/// <summary>
/// Starts a new exe and takes control of it.
/// </summary>
/// <param name="info">The path to the application executable.</param>
private WindowsDriver<WindowsElement> NewWindowsDriver(AppiumOptions info)
{
// Create driver with retry
TimeSpan timeout = TimeSpan.FromMinutes(2);
DateTime startTime = DateTime.Now;
int retryCount = 0;
while (true)
{
try
{
var res = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), info);
return res;
}
catch (Exception ex)
{
if (DateTime.Now - startTime > timeout)
{
Console.WriteLine("Timeout reached. Throwing exception.");
throw;
}
TimeSpan retryInterval = retryCount < 2
? TimeSpan.FromSeconds(5)
: TimeSpan.FromSeconds(20);
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Exception occurred: {ex.Message}, Retrying after {retryInterval.TotalSeconds} seconds...");
Task.Delay(retryInterval).Wait();
retryCount++;
}
}
}
/// <summary>
/// Exit now exe.
/// </summary>
public void ExitScopeExe()
{
ExitExe(sessionPath);
}
/// <summary>
/// Restarts now exe and takes control of it.
/// </summary>
public void RestartScopeExe()
{
ExitScopeExe();
StartExe(locationPath + sessionPath);
this.Driver = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), opts);
}
public WindowsDriver<WindowsElement> GetRoot() => this.Root;
@@ -193,17 +97,5 @@ namespace Microsoft.PowerToys.UITest
Assert.IsNotNull(this.Driver, $"Failed to get driver. Driver is null.");
return this.Driver;
}
private void StartWindowsAppDriverApp()
{
var winAppDriverProcessInfo = new ProcessStartInfo
{
FileName = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe",
Verb = "runas",
};
this.ExitExe(winAppDriverProcessInfo.FileName);
this.appDriver = Process.Start(winAppDriverProcessInfo);
}
}
}

View File

@@ -8,18 +8,12 @@
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
<TargetFramework>net9.0-windows10.0.22621.0</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Appium.WebDriver" />
<PackageReference Include="MSTest" />
<PackageReference Include="System.IO.Abstractions" />
<PackageReference Include="System.Net.Http" />
<PackageReference Include="System.Private.Uri" />
<PackageReference Include="System.Text.RegularExpressions" />
</ItemGroup>
</Project>

View File

@@ -6,16 +6,9 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using Windows.Devices.Display.Core;
using Windows.Foundation.Metadata;
using static Microsoft.PowerToys.UITest.UITestBase.NativeMethods;
using static Microsoft.PowerToys.UITest.WindowHelper;
namespace Microsoft.PowerToys.UITest
{
@@ -23,37 +16,24 @@ namespace Microsoft.PowerToys.UITest
/// Base class that should be inherited by all Test Classes.
/// </summary>
[TestClass]
public class UITestBase : IDisposable
public class UITestBase
{
public required TestContext TestContext { get; set; }
public Session Session { get; set; }
public required Session Session { get; set; }
public bool IsInPipeline { get; }
public static MonitorInfoData.ParamsWrapper MonitorInfoData { get; set; } = new MonitorInfoData.ParamsWrapper() { Monitors = new List<MonitorInfoData.MonitorInfoDataWrapper>() };
private readonly SessionHelper sessionHelper;
private readonly PowerToysModule scope;
private readonly WindowSize size;
private SessionHelper? sessionHelper;
private System.Threading.Timer? screenshotTimer;
private string? screenshotDirectory;
public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings, WindowSize size = WindowSize.UnSpecified)
public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings)
{
this.IsInPipeline = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("platform"));
Console.WriteLine($"Running tests on platform: {Environment.GetEnvironmentVariable("platform")}");
if (IsInPipeline)
{
NativeMethods.ChangeDisplayResolution(1920, 1080);
NativeMethods.GetMonitorInfo();
// Escape Popups before starting
System.Windows.Forms.SendKeys.SendWait("{ESC}");
}
this.scope = scope;
this.size = size;
this.sessionHelper = new SessionHelper(scope).Init();
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver());
}
~UITestBase()
{
this.sessionHelper.Cleanup();
}
/// <summary>
@@ -62,22 +42,6 @@ namespace Microsoft.PowerToys.UITest
[TestInitialize]
public void TestInit()
{
CloseOtherApplications();
if (IsInPipeline)
{
screenshotDirectory = Path.Combine(this.TestContext.TestResultsDirectory ?? string.Empty, "UITestScreenshots_" + Guid.NewGuid().ToString());
Directory.CreateDirectory(screenshotDirectory);
// Take screenshot every 1 second
screenshotTimer = new System.Threading.Timer(ScreenCapture.TimerCallback, screenshotDirectory, TimeSpan.Zero, TimeSpan.FromMilliseconds(1000));
// Escape Popups before starting
System.Windows.Forms.SendKeys.SendWait("{ESC}");
}
this.sessionHelper = new SessionHelper(scope).Init();
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver(), scope, size);
if (this.scope == PowerToysModule.PowerToysSettings)
{
// close Debug warning dialog if any
@@ -89,178 +53,53 @@ namespace Microsoft.PowerToys.UITest
}
}
/// <summary>
/// Cleanups the test.
/// </summary>
[TestCleanup]
public void TestCleanup()
{
if (IsInPipeline)
{
screenshotTimer?.Change(Timeout.Infinite, Timeout.Infinite);
Dispose();
if (TestContext.CurrentTestOutcome is UnitTestOutcome.Failed
or UnitTestOutcome.Error
or UnitTestOutcome.Unknown)
{
Task.Delay(1000).Wait();
AddScreenShotsToTestResultsDirectory();
}
}
this.Session.Cleanup();
this.sessionHelper!.Cleanup();
}
public void Dispose()
{
screenshotTimer?.Dispose();
GC.SuppressFinalize(this);
}
/// <summary>
/// Finds an element by selector.
/// Shortcut for this.Session.Find<T>(by, timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
protected T Find<T>(By by, int timeoutMS = 5000, bool global = false)
protected T Find<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
return this.Session.Find<T>(by, timeoutMS, global);
return this.Session.Find<T>(by, timeoutMS);
}
/// <summary>
/// Shortcut for this.Session.Find<Element>(name, timeoutMS)
/// Shortcut for this.Session.Find<Element>(By.Name(name), timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
protected T Find<T>(string name, int timeoutMS = 5000, bool global = false)
protected T Find<T>(string name, int timeoutMS = 3000)
where T : Element, new()
{
return this.Session.Find<T>(By.Name(name), timeoutMS, global);
return this.Session.Find<T>(By.Name(name), timeoutMS);
}
/// <summary>
/// Shortcut for this.Session.Find<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
protected Element Find(By by, int timeoutMS = 5000, bool global = false)
protected Element Find(By by, int timeoutMS = 3000)
{
return this.Session.Find(by, timeoutMS, global);
return this.Session.Find(by, timeoutMS);
}
/// <summary>
/// Shortcut for this.Session.Find<Element>(name, timeoutMS)
/// Shortcut for this.Session.Find<Element>(By.Name(name), timeoutMS)
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
protected Element Find(string name, int timeoutMS = 5000, bool global = false)
protected Element Find(string name, int timeoutMS = 3000)
{
return this.Session.Find(name, timeoutMS, global);
}
/// <summary>
/// Has only one Element or its derived class by selector.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne<T>(By by, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.FindAll<T>(by, timeoutMS, global).Count == 1;
}
/// <summary>
/// Shortcut for this.Session.HasOne<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne(By by, int timeoutMS = 5000, bool global = false)
{
return this.Session.HasOne<Element>(by, timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Session.HasOne<T>(name, timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne<T>(string name, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.Session.HasOne<T>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Session.HasOne<Element>(name, timeoutMS)
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne(string name, int timeoutMS = 5000, bool global = false)
{
return this.Session.HasOne<Element>(name, timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Session.Has<T>(by, timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has<T>(By by, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.Session.FindAll<T>(by, timeoutMS, global).Count >= 1;
}
/// <summary>
/// Shortcut for this.Session.Has<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has(By by, int timeoutMS = 5000, bool global = false)
{
return this.Session.Has<Element>(by, timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Session.Has<T>(By.Name(name), timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has<T>(string name, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.Session.Has<T>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Session.Has<Element>(name, timeoutMS)
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has(string name, int timeoutMS = 5000, bool global = false)
{
return this.Session.Has<Element>(name, timeoutMS, global);
return this.Session.Find(name, timeoutMS);
}
/// <summary>
@@ -269,12 +108,12 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <typeparam name="T">The class of the elements, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
protected ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 5000, bool global = false)
protected ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
return this.Session.FindAll<T>(by, timeoutMS, global);
return this.Session.FindAll<T>(by, timeoutMS);
}
/// <summary>
@@ -283,12 +122,12 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <typeparam name="T">The class of the elements, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
protected ReadOnlyCollection<T> FindAll<T>(string name, int timeoutMS = 5000, bool global = false)
protected ReadOnlyCollection<T> FindAll<T>(string name, int timeoutMS = 3000)
where T : Element, new()
{
return this.Session.FindAll<T>(By.Name(name), timeoutMS, global);
return this.Session.FindAll<T>(By.Name(name), timeoutMS);
}
/// <summary>
@@ -296,11 +135,11 @@ namespace Microsoft.PowerToys.UITest
/// Shortcut for this.Session.FindAll<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
protected ReadOnlyCollection<Element> FindAll(By by, int timeoutMS = 5000, bool global = false)
protected ReadOnlyCollection<Element> FindAll(By by, int timeoutMS = 3000)
{
return this.Session.FindAll<Element>(by, timeoutMS, global);
return this.Session.FindAll<Element>(by, timeoutMS);
}
/// <summary>
@@ -308,342 +147,11 @@ namespace Microsoft.PowerToys.UITest
/// Shortcut for this.Session.FindAll<Element>(By.Name(name), timeoutMS)
/// </summary>
/// <param name="name">The name of the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
protected ReadOnlyCollection<Element> FindAll(string name, int timeoutMS = 5000, bool global = false)
protected ReadOnlyCollection<Element> FindAll(string name, int timeoutMS = 3000)
{
return this.Session.FindAll<Element>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Scrolls the page
/// </summary>
/// <param name="scrollCount">The number of scroll attempts.</param>
/// <param name="direction">The direction to scroll.</param>
/// <param name="msPreAction">Pre-action delay in milliseconds.</param>
/// <param name="msPostAction">Post-action delay in milliseconds.</param>
public void Scroll(int scrollCount = 5, string direction = "Up", int msPreAction = 500, int msPostAction = 500)
{
MouseActionType mouseAction = direction == "Up" ? MouseActionType.ScrollUp : MouseActionType.ScrollDown;
for (int i = 0; i < scrollCount; i++)
{
Session.PerformMouseAction(mouseAction, msPreAction, msPostAction); // Ensure settings are visible
}
}
/// <summary>
/// Captures the last screenshot when the test fails.
/// </summary>
protected void CaptureLastScreenshot()
{
// Implement your screenshot capture logic here
// For example, save a screenshot to a file and return the file path
string screenshotPath = Path.Combine(this.TestContext.TestResultsDirectory ?? string.Empty, "last_screenshot.png");
this.Session.Root.GetScreenshot().SaveAsFile(screenshotPath, ScreenshotImageFormat.Png);
// Save screenshot to screenshotPath & upload to test attachment
this.TestContext.AddResultFile(screenshotPath);
}
/// <summary>
/// Retrieves the color of the pixel at the specified screen coordinates.
/// </summary>
/// <param name="x">The X coordinate on the screen.</param>
/// <param name="y">The Y coordinate on the screen.</param>
/// <returns>The color of the pixel at the specified coordinates.</returns>
public Color GetPixelColor(int x, int y)
{
return WindowHelper.GetPixelColor(x, y);
}
/// <summary>
/// Retrieves the color of the pixel at the specified screen coordinates as a string.
/// </summary>
/// <param name="x">The X coordinate on the screen.</param>
/// <param name="y">The Y coordinate on the screen.</param>
/// <returns>The color of the pixel at the specified coordinates.</returns>
public string GetPixelColorString(int x, int y)
{
return WindowHelper.GetPixelColorString(x, y);
}
/// <summary>
/// Gets the size of the display.
/// </summary>
/// <returns>
/// A tuple containing the width and height of the display.
/// </returns
public Tuple<int, int> GetDisplaySize()
{
return WindowHelper.GetDisplaySize();
}
/// <summary>
/// Sends a combination of keys.
/// </summary>
/// <param name="keys">The keys to send.</param>
public void SendKeys(params Key[] keys)
{
this.Session.SendKeys(keys);
}
/// <summary>
/// Sends a sequence of keys.
/// </summary>
/// <param name="keys">An array of keys to send.</param>
public void SendKeySequence(params Key[] keys)
{
this.Session.SendKeySequence(keys);
}
/// <summary>
        /// Gets the current position of the mouse cursor as a tuple.
        /// </summary>
        /// <returns>A tuple containing the X and Y coordinates of the cursor.</returns>
public Tuple<int, int> GetMousePosition()
{
return this.Session.GetMousePosition();
}
/// <summary>
/// Gets the screen center coordinates.
/// </summary>
/// <returns>(x, y)</returns>
public (int CenterX, int CenterY) GetScreenCenter()
{
return WindowHelper.GetScreenCenter();
}
public bool IsWindowOpen(string windowName)
{
return WindowHelper.IsWindowOpen(windowName);
}
/// <summary>
    /// Moves the mouse cursor to the specified screen coordinates.
    /// </summary>
    /// <param name="x">The new x-coordinate of the cursor.</param>
    /// <param name="y">The new y-coordinate of the cursor.</param
public void MoveMouseTo(int x, int y)
{
        this.Session.MoveMouseTo(x, y);
}
protected void AddScreenShotsToTestResultsDirectory()
{
if (screenshotDirectory != null)
{
foreach (string file in Directory.GetFiles(screenshotDirectory))
{
this.TestContext.AddResultFile(file);
}
}
}
/// <summary>
/// Restart scope exe.
/// </summary>
public void RestartScopeExe()
{
this.sessionHelper!.RestartScopeExe();
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver(), this.scope, this.size);
return;
}
/// <summary>
/// Restart scope exe.
/// </summary>
public void ExitScopeExe()
{
this.sessionHelper!.ExitScopeExe();
return;
}
private void CloseOtherApplications()
{
// Close other applications
var processNamesToClose = new List<string>
{
"PowerToys",
"PowerToys.Settings",
"PowerToys.FancyZonesEditor",
};
foreach (var processName in processNamesToClose)
{
foreach (var process in Process.GetProcessesByName(processName))
{
process.Kill();
process.WaitForExit();
}
}
}
public class NativeMethods
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DISPLAY_DEVICE
{
public int cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString;
public int StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey;
}
[DllImport("user32.dll")]
private static extern int EnumDisplaySettings(IntPtr deviceName, int modeNum, ref DEVMODE devMode);
[DllImport("user32.dll")]
private static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE devMode);
[DllImport("user32.dll", CharSet = CharSet.Ansi)]
private static extern bool EnumDisplayDevices(IntPtr lpDevice, int iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, int dwFlags);
[DllImport("user32.dll")]
private static extern int ChangeDisplaySettings(ref DEVMODE devMode, int flags);
[DllImport("user32.dll", CharSet = CharSet.Ansi)]
private static extern int ChangeDisplaySettingsEx(IntPtr lpszDeviceName, ref DEVMODE lpDevMode, IntPtr hwnd, uint dwflags, IntPtr lParam);
private const int DM_PELSWIDTH = 0x80000;
private const int DM_PELSHEIGHT = 0x100000;
public const int ENUM_CURRENT_SETTINGS = -1;
public const int CDS_TEST = 0x00000002;
public const int CDS_UPDATEREGISTRY = 0x01;
public const int DISP_CHANGE_SUCCESSFUL = 0;
public const int DISP_CHANGE_RESTART = 1;
public const int DISP_CHANGE_FAILED = -1;
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DmDeviceName;
public short DmSpecVersion;
public short DmDriverVersion;
public short DmSize;
public short DmDriverExtra;
public int DmFields;
public int DmPositionX;
public int DmPositionY;
public int DmDisplayOrientation;
public int DmDisplayFixedOutput;
public short DmColor;
public short DmDuplex;
public short DmYResolution;
public short DmTTOption;
public short DmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DmFormName;
public short DmLogPixels;
public int DmBitsPerPel;
public int DmPelsWidth;
public int DmPelsHeight;
public int DmDisplayFlags;
public int DmDisplayFrequency;
public int DmICMMethod;
public int DmICMIntent;
public int DmMediaType;
public int DmDitherType;
public int DmReserved1;
public int DmReserved2;
public int DmPanningWidth;
public int DmPanningHeight;
}
public static void GetMonitorInfo()
{
int deviceIndex = 0;
DISPLAY_DEVICE d = default(DISPLAY_DEVICE);
d.cb = Marshal.SizeOf(d);
Console.WriteLine("monitor list :");
while (EnumDisplayDevices(IntPtr.Zero, deviceIndex, ref d, 0))
{
Console.WriteLine($"monitor {deviceIndex + 1}:");
Console.WriteLine($" name: {d.DeviceName}");
Console.WriteLine($"  string: {d.DeviceString}");
Console.WriteLine($"  ID: {d.DeviceID}");
Console.WriteLine($"  key: {d.DeviceKey}");
Console.WriteLine();
DEVMODE dm = default(DEVMODE);
dm.DmSize = (short)Marshal.SizeOf<DEVMODE>();
int modeNum = 0;
while (EnumDisplaySettings(d.DeviceName, modeNum, ref dm) > 0)
{
MonitorInfoData.Monitors.Add(new MonitorInfoData.MonitorInfoDataWrapper()
{
DeviceName = d.DeviceName,
DeviceString = d.DeviceString,
DeviceID = d.DeviceID,
DeviceKey = d.DeviceKey,
PelsWidth = dm.DmPelsWidth,
PelsHeight = dm.DmPelsHeight,
DisplayFrequency = dm.DmDisplayFrequency,
});
Console.WriteLine($"  mode {modeNum}: {dm.DmPelsWidth}x{dm.DmPelsHeight} @ {dm.DmDisplayFrequency}Hz");
modeNum++;
}
deviceIndex++;
d.cb = Marshal.SizeOf(d); // Reset the size for the next device
}
}
public static void ChangeDisplayResolution(int PelsWidth, int PelsHeight)
{
Screen screen = Screen.PrimaryScreen!;
if (screen.Bounds.Width == PelsWidth && screen.Bounds.Height == PelsHeight)
{
return;
}
DEVMODE devMode = default(DEVMODE);
devMode.DmDeviceName = new string(new char[32]);
devMode.DmFormName = new string(new char[32]);
devMode.DmSize = (short)Marshal.SizeOf<DEVMODE>();
int modeNum = 0;
while (EnumDisplaySettings(IntPtr.Zero, modeNum, ref devMode) > 0)
{
Console.WriteLine($"Mode {modeNum}: {devMode.DmPelsWidth}x{devMode.DmPelsHeight} @ {devMode.DmDisplayFrequency}Hz");
modeNum++;
}
devMode.DmPelsWidth = PelsWidth;
devMode.DmPelsHeight = PelsHeight;
int result = NativeMethods.ChangeDisplaySettings(ref devMode, NativeMethods.CDS_TEST);
if (result == DISP_CHANGE_SUCCESSFUL)
{
result = ChangeDisplaySettings(ref devMode, CDS_UPDATEREGISTRY);
if (result == DISP_CHANGE_SUCCESSFUL)
{
Console.WriteLine($"Changing display resolution to {devMode.DmPelsWidth}x{devMode.DmPelsHeight}");
}
else
{
Console.WriteLine($"Failed to change display resolution. Error code: {result}");
}
}
else if (result == DISP_CHANGE_RESTART)
{
Console.WriteLine($"Changing display resolution to {devMode.DmPelsWidth}x{devMode.DmPelsHeight} requires a restart");
}
else
{
Console.WriteLine($"Failed to change display resolution. Error code: {result}");
}
}
return this.Session.FindAll<Element>(By.Name(name), timeoutMS);
}
}
}

View File

@@ -1,160 +0,0 @@
// 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;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.PowerToys.UITest
{
public static class VisualAssert
{
/// <summary>
/// Asserts current visual state of the element is equal with base line image.
/// To use this VisualAssert, you need to set Window Theme to Light-Mode to avoid Theme color difference in baseline image.
/// Such limitation could be removed either Auto-generate baseline image for both Light & Dark mode
/// </summary>
/// <param name="testContext">TestContext object</param>
/// <param name="element">Element object</param>
/// <param name="scenarioSubname">additional scenario name if two or more scenarios in one test</param>
[RequiresUnreferencedCode("This method uses reflection which may not be compatible with trimming.")]
public static void AreEqual(TestContext? testContext, Element element, string scenarioSubname = "")
{
var pipelinePlatform = Environment.GetEnvironmentVariable("platform");
// Perform visual validation only in the pipeline
if (string.IsNullOrEmpty(pipelinePlatform))
{
Console.WriteLine("Skip visual validation in the local run.");
return;
}
if (element == null)
{
Assert.Fail("Element object is null or invalid");
}
var stackTrace = new StackTrace();
var callerFrame = stackTrace.GetFrame(1);
var callerMethod = callerFrame?.GetMethod();
var callerName = callerMethod?.Name;
var callerClassName = callerMethod?.DeclaringType?.Name;
if (string.IsNullOrEmpty(callerName) || string.IsNullOrEmpty(callerClassName))
{
Assert.Fail("Unable to determine the caller method and class name.");
}
if (string.IsNullOrWhiteSpace(scenarioSubname))
{
scenarioSubname = string.Join("_", callerClassName, callerName, pipelinePlatform);
}
else
{
scenarioSubname = string.Join("_", callerClassName, callerName, scenarioSubname.Trim(), pipelinePlatform);
}
var baselineImageResourceName = callerMethod!.DeclaringType!.Assembly.GetManifestResourceNames().Where(name => name.Contains(scenarioSubname)).FirstOrDefault();
var tempTestImagePath = GetTempFilePath(scenarioSubname, "test", ".png");
element.SaveToPngFile(tempTestImagePath);
if (string.IsNullOrEmpty(baselineImageResourceName)
|| !Path.GetFileNameWithoutExtension(baselineImageResourceName).EndsWith(scenarioSubname))
{
testContext?.AddResultFile(tempTestImagePath);
Assert.Fail($"Baseline image for scenario {scenarioSubname} can't be found, test image saved in file://{tempTestImagePath.Replace('\\', '/')}");
}
var tempBaselineImagePath = GetTempFilePath(scenarioSubname, "baseline", Path.GetExtension(baselineImageResourceName));
bool isSame = false;
using (var stream = callerMethod!.DeclaringType!.Assembly.GetManifestResourceStream(baselineImageResourceName))
{
if (stream == null)
{
Assert.Fail($"Resource stream '{baselineImageResourceName}' is null.");
}
using (var baselineImage = new Bitmap(stream))
{
using (var testImage = new Bitmap(tempTestImagePath))
{
isSame = VisualAssert.AreEqual(baselineImage, testImage);
if (!isSame)
{
// Copy baseline image to temp folder as well
baselineImage.Save(tempBaselineImagePath);
}
}
}
}
if (!isSame)
{
if (testContext != null)
{
testContext.AddResultFile(tempBaselineImagePath);
testContext.AddResultFile(tempTestImagePath);
}
Assert.Fail($"Fail to validate visual result for scenario {scenarioSubname}, baseline image can be found file://{tempBaselineImagePath.Replace('\\', '/')}, and test image can be found file://{tempTestImagePath.Replace('\\', '/')}");
}
}
/// <summary>
/// Get temp file path
/// </summary>
/// <param name="scenario">scenario name</param>
/// <param name="imageType">baseline or test image</param>
/// <param name="extension">image file extension</param>
/// <returns>full temp file path</returns>
private static string GetTempFilePath(string scenario, string imageType, string extension)
{
var tempFileFullName = $"{scenario}_{imageType}{extension}";
// Remove invalid filename character if any
Path.GetInvalidFileNameChars().ToList().ForEach(c => tempFileFullName = tempFileFullName.Replace(c, '-'));
return Path.Combine(Path.GetTempPath(), tempFileFullName);
}
/// <summary>
/// Test if two images are equal bit-by-bit
/// </summary>
/// <param name="baselineImage">baseline image</param>
/// <param name="testImage">test image</param>
/// <returns>true if are equal,otherwise false</returns>
private static bool AreEqual(Bitmap baselineImage, Bitmap testImage)
{
if (baselineImage.Width != testImage.Width || baselineImage.Height != testImage.Height)
{
return false;
}
// WinAppDriver sometimes adds a border to the screenshot (around 2 pix width), and it is not always consistent.
// So we exclude the border when comparing the images, and usually it is the edge of the windows, won't affect the comparison.
int excludeBorderWidth = 5, excludeBorderHeight = 5;
for (int x = excludeBorderWidth; x < baselineImage.Width - excludeBorderWidth; x++)
{
for (int y = excludeBorderHeight; y < baselineImage.Height - excludeBorderHeight; y++)
{
if (!VisualHelper.PixIsSame(baselineImage.GetPixel(x, y), testImage.GetPixel(x, y)))
{
return false;
}
}
}
return true;
}
}
}

View File

@@ -1,33 +0,0 @@
// 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 System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.PowerToys.UITest
{
internal static class VisualHelper
{
/// <summary>
/// Compare two pixels with a fuzz factor
/// </summary>
/// <param name="c1">base color</param>
/// <param name="c2">test color</param>
/// <param name="fuzz">fuzz factor, default is 10</param>
/// <returns>true if same, otherwise is false</returns>
public static bool PixIsSame(Color c1, Color c2, int fuzz = 10)
{
return Math.Abs(c1.A - c2.A) <= fuzz && Math.Abs(c1.R - c2.R) <= fuzz && Math.Abs(c1.G - c2.G) <= fuzz && Math.Abs(c1.B - c2.B) <= fuzz;
}
}
}

View File

@@ -1,331 +0,0 @@
// 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 System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.PowerToys.UITest
{
internal static class WindowHelper
{
internal const string AdministratorPrefix = "Administrator: ";
/// <summary>
/// Sets the main window size.
/// </summary>
/// <param name="size">WindowSize enum</param>
public static void SetWindowSize(IntPtr windowHandler, WindowSize size)
{
if (size == WindowSize.UnSpecified)
{
return;
}
int width = 0, height = 0;
switch (size)
{
case WindowSize.Small:
width = 640;
height = 480;
break;
case WindowSize.Small_Vertical:
width = 480;
height = 640;
break;
case WindowSize.Medium:
width = 1024;
height = 768;
break;
case WindowSize.Medium_Vertical:
width = 768;
height = 1024;
break;
case WindowSize.Large:
width = 1920;
height = 1080;
break;
case WindowSize.Large_Vertical:
width = 1080;
height = 1920;
break;
}
if (width > 0 && height > 0)
{
WindowHelper.SetMainWindowSize(windowHandler, width, height);
}
}
/// <summary>
/// Gets the main window center coordinates.
/// </summary>
/// <returns>(x, y)</returns>
public static (int CenterX, int CenterY) GetWindowCenter(IntPtr windowHandler)
{
if (windowHandler == IntPtr.Zero)
{
return (0, 0);
}
else
{
var rect = ApiHelper.GetWindowCenter(windowHandler);
return (rect.CenterX, rect.CenterY);
}
}
/// <summary>
/// Gets the main window center coordinates.
/// </summary>
/// <returns>(x, y)</returns>
public static (int Left, int Top, int Right, int Bottom) GetWindowRect(IntPtr windowHandler)
{
if (windowHandler == IntPtr.Zero)
{
return (0, 0, 0, 0);
}
else
{
var rect = ApiHelper.GetWindowRect(windowHandler);
return (rect.Left, rect.Top, rect.Right, rect.Bottom);
}
}
/// <summary>
/// Gets the screen center coordinates.
/// </summary>
/// <returns>(x, y)</returns>
public static (int CenterX, int CenterY) GetScreenCenter()
{
return ApiHelper.GetScreenCenter();
}
/// <summary>
/// Sets the main window size based on Width and Height.
/// </summary>
/// <param name="width">the width in pixel</param>
/// <param name="height">the height in pixel</param>
public static void SetMainWindowSize(IntPtr windowHandler, int width, int height)
{
if (windowHandler == IntPtr.Zero
|| width <= 0
|| height <= 0)
{
return;
}
ApiHelper.SetWindowPos(windowHandler, IntPtr.Zero, 0, 0, width, height, ApiHelper.SetWindowPosNoZorder | ApiHelper.SetWindowPosShowWindow);
// Wait for 1000ms after resize
Task.Delay(1000).Wait();
}
/// <summary>
/// Retrieves the color of the pixel at the specified screen coordinates.
/// </summary>
/// <param name="x">The X coordinate on the screen.</param>
/// <param name="y">The Y coordinate on the screen.</param>
/// <returns>The color of the pixel at the specified coordinates.</returns>
public static Color GetPixelColor(int x, int y)
{
IntPtr hdc = ApiHelper.GetDC(IntPtr.Zero);
uint pixel = ApiHelper.GetPixel(hdc, x, y);
_ = ApiHelper.ReleaseDC(IntPtr.Zero, hdc);
int r = (int)(pixel & 0x000000FF);
int g = (int)((pixel & 0x0000FF00) >> 8);
int b = (int)((pixel & 0x00FF0000) >> 16);
return Color.FromArgb(r, g, b);
}
/// <summary>
/// Retrieves the color of the pixel at the specified screen coordinates as a string.
/// </summary>
/// <param name="x">The X coordinate on the screen.</param>
/// <param name="y">The Y coordinate on the screen.</param>
/// <returns>The color of the pixel at the specified coordinates.</returns>
public static string GetPixelColorString(int x, int y)
{
Color color = WindowHelper.GetPixelColor(x, y);
return $"#{color.R:X2}{color.G:X2}{color.B:X2}";
}
/// <summary>
/// Gets the size of the display.
/// </summary>
/// <returns>
/// A tuple containing the width and height of the display.
/// </returns
public static Tuple<int, int> GetDisplaySize()
{
IntPtr hdc = ApiHelper.GetDC(IntPtr.Zero);
int screenWidth = ApiHelper.GetDeviceCaps(hdc, ApiHelper.DESKTOPHORZRES);
int screenHeight = ApiHelper.GetDeviceCaps(hdc, ApiHelper.DESKTOPVERTRES);
_ = ApiHelper.ReleaseDC(IntPtr.Zero, hdc);
return Tuple.Create(screenWidth, screenHeight);
}
public static bool IsWindowOpen(string windowName)
{
var matchingWindows = ApiHelper.FindDesktopWindowHandler([windowName, AdministratorPrefix + windowName]);
return matchingWindows.Count > 0;
}
internal static class ApiHelper
{
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
public const uint SetWindowPosNoMove = 0x0002;
public const uint SetWindowPosNoZorder = 0x0004;
public const uint SetWindowPosShowWindow = 0x0040;
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
// Delegate for the EnumWindows callback function
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
// P/Invoke declaration for EnumWindows
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
// P/Invoke declaration for GetWindowTextLength
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetWindowTextLength(IntPtr hWnd);
// P/Invoke declaration for GetWindowText
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("gdi32.dll")]
public static extern uint GetPixel(IntPtr hdc, int x, int y);
[DllImport("gdi32.dll")]
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
public const int DESKTOPHORZRES = 118;
public const int DESKTOPVERTRES = 117;
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
// Define the Win32 RECT structure
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; // X coordinate of the left edge of the window
public int Top; // Y coordinate of the top edge of the window
public int Right; // X coordinate of the right edge of the window
public int Bottom; // Y coordinate of the bottom edge of the window
}
// Import GetWindowRect API to retrieve window's screen coordinates
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
public static List<(IntPtr HWnd, string Title)> FindDesktopWindowHandler(string[] matchingWindowsTitles)
{
var windows = new List<(IntPtr HWnd, string Title)>();
_ = EnumWindows(
(hWnd, lParam) =>
{
int length = GetWindowTextLength(hWnd);
if (length > 0)
{
var builder = new StringBuilder(length + 1);
_ = GetWindowText(hWnd, builder, builder.Capacity);
var title = builder.ToString();
if (matchingWindowsTitles.Contains(title))
{
windows.Add((hWnd, title));
}
}
return true; // Continue enumeration
},
IntPtr.Zero);
return windows;
}
/// <summary>
/// Get the center point coordinates of a specified window (in screen coordinates)
/// </summary>
/// <param name="hWnd">The window handle</param>
/// <returns>The center point (x, y)</returns>
public static (int CenterX, int CenterY) GetWindowCenter(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero)
{
throw new ArgumentException("Invalid window handle");
}
if (GetWindowRect(hWnd, out RECT rect))
{
int width = rect.Right - rect.Left;
int height = rect.Bottom - rect.Top;
int centerX = rect.Left + (width / 2);
int centerY = rect.Top + (height / 2);
return (centerX, centerY);
}
else
{
throw new InvalidOperationException("Failed to retrieve window coordinates");
}
}
public static (int Left, int Top, int Right, int Bottom) GetWindowRect(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero)
{
throw new ArgumentException("Invalid window handle");
}
if (GetWindowRect(hWnd, out RECT rect))
{
return (rect.Left, rect.Top, rect.Right, rect.Bottom);
}
else
{
throw new InvalidOperationException("Failed to retrieve window coordinates");
}
}
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(int nIndex);
public enum SystemMetric
{
ScreenWidth = 0, // Width of the primary screen in pixels (SM_CXSCREEN)
ScreenHeight = 1, // Height of the primary screen in pixels (SM_CYSCREEN)
VirtualScreenWidth = 78, // Width of the virtual screen that includes all monitors (SM_CXVIRTUALSCREEN)
VirtualScreenHeight = 79, // Height of the virtual screen that includes all monitors (SM_CYVIRTUALSCREEN)
MonitorCount = 80, // Number of display monitors (SM_CMONITORS, available on Windows XP+)
}
public static (int CenterX, int CenterY) GetScreenCenter()
{
int width = GetSystemMetrics((int)SystemMetric.ScreenWidth);
int height = GetSystemMetrics((int)SystemMetric.ScreenHeight);
return (width / 2, height / 2);
}
}
}
}

View File

@@ -187,8 +187,4 @@ namespace winrt::PowerToys::Interop::implementation
{
return CommonSharedConstants::TERMINATE_SETTINGS_SHARED_EVENT;
}
hstring Constants::ShowCmdPalEvent()
{
return CommonSharedConstants::CMDPAL_SHOW_EVENT;
}
}

Some files were not shown because too many files have changed in this diff Show More