Merge branch 'main' into pr35600

This commit is contained in:
Jaime Bernardo 2024-11-22 15:35:42 +00:00
commit 5135506244
171 changed files with 3205 additions and 1091 deletions

View File

@ -56,7 +56,6 @@ damienleroy
davidegiacometti
debian
Deibisu
Deibisu
Delimarsky
Deondre
DHowett
@ -87,7 +86,6 @@ jefflord
Jordi
jyuwono
Kairu
Kairu
Kamra
Kantarci
Karthick
@ -123,7 +121,6 @@ Quriz
randyrants
ricardosantos
riri
riri
ritchielawrence
robmikh
Rutkas
@ -147,6 +144,7 @@ TBM
tilovell
Triet
waaverecords
Whuihuan
Xpg
ycv
Yuniardi
@ -157,6 +155,8 @@ Zykova
# OTHERS
Bilibili
BVID
cmdow
Controlz
cortana

View File

@ -1,7 +1,6 @@
# FALSE POSITIVES
## "PackagemanagerWrapper.cs" should be "PackageManagerWrapper.cs"
## NOTICE.MD > MOZILLA PUBLIC LICENSE v1.1
aaaa
abcdefghjkmnpqrstuvxyz
abgr
@ -43,7 +42,6 @@ AMPROPSETID
amr
ANDSCANS
animatedvisuals
anr
ansicolor
ANull
AOC
@ -71,11 +69,9 @@ AQS
ARandom
ARCHITEW
ARemapped
ari
ARPINSTALLLOCATION
ARPPRODUCTICON
ARRAYSIZE
arw
asf
AShortcut
ASingle
@ -153,6 +149,7 @@ CALG
callbackptr
calpwstr
Cangjie
caniuse
CANRENAME
CAPTUREBLT
CAPTURECHANGED
@ -261,7 +258,7 @@ CRH
critsec
Crossdevice
CRSEL
crw
crx
CSearch
CSettings
cso
@ -303,8 +300,8 @@ DCOM
dcommon
dcomp
DComposition
dcr
dcs
DCR
DCs
ddd
DDEIf
DDevice
@ -374,7 +371,6 @@ DRAWCLIPBOARD
DRAWFRAME
drawingcolor
dreamsofameaningfullife
drf
drivedetectionwarning
dshow
DSTINVERT
@ -418,7 +414,6 @@ editkeyboardwindow
EDITSHORTCUTS
editshortcutswindow
EFile
eip
ekus
emmintrin
Emoji
@ -592,7 +587,7 @@ Hiberboot
HIBYTE
hicon
HIDEWINDOW
hif
Hif
HIMAGELIST
himl
hinst
@ -641,6 +636,7 @@ HWNDLAST
HWNDNEXT
HWNDPREV
hyjiacan
IApp
IBeam
ICapture
IClass
@ -667,7 +663,6 @@ IGNOREUNKNOWN
IGraphics
iid
Iindex
iiq
IJson
Ijwhost
IKs
@ -732,15 +727,14 @@ ith
ITHUMBNAIL
IUI
IUnknown
IUse
IWbem
IWeb
IWIC
iwr
IYUV
jfi
jfif
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
jif
jjw
jobject
jpe
@ -749,7 +743,6 @@ Jsons
jsonval
junja
jxr
kdc
keybd
KEYBDDATA
KEYBDINPUT
@ -874,7 +867,6 @@ MAXIMIZEBOX
MAXSHORTCUTSIZE
maxversiontested
MBR
mdc
MDICHILD
MDL
mdtext
@ -882,7 +874,6 @@ mdtxt
mdwn
MEDIASUBTYPE
mediatype
mef
MENUITEMINFO
MENUITEMINFOW
MERGECOPY
@ -940,7 +931,6 @@ mpmc
MRM
MRT
mru
mrw
msc
mscorlib
msdata
@ -1063,7 +1053,6 @@ NOZORDER
NPH
npmjs
NResize
nrw
nsunt
NTAPI
ntdll
@ -1093,7 +1082,6 @@ opensource
openxmlformats
OPTIMIZEFORINVOKE
ORAW
ori
ORPHANEDDIALOGTITLE
ORSCANS
oss
@ -1142,7 +1130,7 @@ pdo
pdto
pdtobj
pdw
Peb
peb
pef
PElems
Pels
@ -1214,6 +1202,7 @@ prgms
pri
PRINTCLIENT
printmanagement
privacystatement
prm
proactively
PROCESSENTRY
@ -1271,7 +1260,6 @@ QUERYENDSESSION
QUERYOPEN
QUEUESYNC
QUNS
raf
RAII
RAlt
Rasterize
@ -1375,8 +1363,6 @@ runtimes
ruuid
rvm
rwin
rwl
rwz
sacl
safeprojectname
SAMEKEYPREVIOUSLYMAPPED
@ -1510,7 +1496,6 @@ Srch
SRCINVERT
SRCPAINT
SResize
srf
srme
srre
srw
@ -1585,6 +1570,8 @@ SYSLIB
SYSMENU
SYSTEMAPPS
SYSTEMTIME
SYSTEMWOW
tailwindcss
tapp
TApplication
TApplied
@ -1628,6 +1615,7 @@ tkconverters
TLayout
tlb
tlbimp
tlhelp
TMPVAR
TNP
Toolhelp

View File

@ -201,6 +201,7 @@
"PowerToys.WorkspacesLauncherUI.exe",
"PowerToys.WorkspacesLauncherUI.dll",
"PowerToys.WorkspacesModuleInterface.dll",
"PowerToys.WorkspacesCsharpLibrary.dll",
"WinUI3Apps\\PowerToys.RegistryPreviewExt.dll",
"WinUI3Apps\\PowerToys.RegistryPreviewUILib.dll",
@ -321,6 +322,10 @@
"WinUI3Apps\\ReverseMarkdown.dll",
"WinUI3Apps\\SharpCompress.dll",
"WinUI3Apps\\ZstdSharp.dll",
"TestableIO.System.IO.Abstractions.dll",
"WinUI3Apps\\TestableIO.System.IO.Abstractions.dll",
"TestableIO.System.IO.Abstractions.Wrappers.dll",
"WinUI3Apps\\TestableIO.System.IO.Abstractions.Wrappers.dll",
"ColorCode.Core.dll",
"ColorCode.UWP.dll",
"UnitsNet.dll",

View File

@ -37,6 +37,10 @@ parameters:
type: boolean
displayName: "Run Tests"
default: true
- name: useVSPreview
type: boolean
displayName: "Build Using Visual Studio Preview"
default: false
extends:
template: templates/pipeline-ci-build.yml
@ -44,3 +48,4 @@ extends:
buildPlatforms: ${{ parameters.buildPlatforms }}
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
runTests: ${{ parameters.runTests }}
useVSPreview: ${{ parameters.useVSPreview }}

View File

@ -33,6 +33,11 @@ parameters:
- x64
- arm64
- name: useVSPreview
type: boolean
displayName: "Build Using Visual Studio Preview"
default: false
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
extends:
@ -42,7 +47,10 @@ extends:
- 1ES.PT.ViaStartRight
pool:
name: SHINE-INT-S
image: SHINE-VS17-Latest
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS17-Preview
${{ else }}:
image: SHINE-VS17-Latest
os: windows
sdl:
tsa:
@ -58,7 +66,10 @@ extends:
parameters:
pool:
name: SHINE-INT-L
image: SHINE-VS17-Latest
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS17-Preview
${{ else }}:
image: SHINE-VS17-Latest
os: windows
variables:
IsPipeline: 1 # The installer uses this to detect whether it should pick up localizations

View File

@ -50,7 +50,9 @@ parameters:
- name: runTests
type: boolean
default: true
- name: useVSPreview
type: boolean
default: false
- name: versionNumber
type: string
default: '0.0.1'
@ -135,7 +137,7 @@ jobs:
- template: steps-ensure-dotnet-version.yml
parameters:
sdk: true
version: '8.0'
version: '9.0'
- ${{ if eq(parameters.runTests, true) }}:
- task: VisualStudioTestPlatformInstaller@1
@ -181,6 +183,9 @@ jobs:
- pwsh: |-
& "$(build.sourcesdirectory)\.pipelines\verifyAndSetLatestVCToolsVersion.ps1"
displayName: Work around DD-1541167 (VCToolsVersion)
${{ if eq(parameters.useVSPreview, true) }}:
env:
VCWhereExtraVersionTarget: '-prerelease'
- pwsh: |-
& "$(build.sourcesdirectory)\.pipelines\installWiX.ps1"
@ -228,7 +233,10 @@ jobs:
inputs:
solution: '**\HostsUILib.csproj'
vsVersion: 17.0
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-hosts.binlog
${{ if eq(parameters.useVSPreview, true) }}:
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-hosts.binlog /p:NoWarn=NU5104
${{ else }}:
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-hosts.binlog
configuration: $(BuildConfiguration)
msbuildArchitecture: x64
maximumCpuCount: true
@ -241,7 +249,10 @@ jobs:
inputs:
solution: '**\EnvironmentVariablesUILib.csproj'
vsVersion: 17.0
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-env-var-editor.binlog
${{ if eq(parameters.useVSPreview, true) }}:
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-env-var-editor.binlog /p:NoWarn=NU5104
${{ else }}:
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-env-var-editor.binlog
configuration: $(BuildConfiguration)
msbuildArchitecture: x64
maximumCpuCount: true
@ -254,7 +265,10 @@ jobs:
inputs:
solution: '**\RegistryPreviewUILib.csproj'
vsVersion: 17.0
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-registry-preview.binlog
${{ if eq(parameters.useVSPreview, true) }}:
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-registry-preview.binlog /p:NoWarn=NU5104
${{ else }}:
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-registry-preview.binlog
configuration: $(BuildConfiguration)
msbuildArchitecture: x64
maximumCpuCount: true
@ -376,6 +390,16 @@ jobs:
msbuildArchitecture: x64
maximumCpuCount: true
### HACK: On ARM64 builds, building an app with Windows App SDK copies the x64 WebView2 dll instead of the ARM64 one. This task makes sure the right dll is used.
- task: CopyFiles@2
displayName: HACK Copy core WebView2 ARM64 dll to output directory
condition: eq(variables['BuildPlatform'],'arm64')
inputs:
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 deps.json files don't reference different dll versions.
- pwsh: |-
& '.pipelines/verifyDepsJsonLibraryVersions.ps1' -targetDir '$(build.sourcesdirectory)\$(BuildPlatform)\$(BuildConfiguration)'

View File

@ -45,7 +45,7 @@ jobs:
- template: steps-ensure-dotnet-version.yml
parameters:
sdk: true
version: '8.0'
version: '9.0'
- task: VisualStudioTestPlatformInstaller@1
displayName: Ensure VSTest Platform

View File

@ -19,6 +19,9 @@ parameters:
- name: runTests
type: boolean
default: true
- name: useVSPreview
type: boolean
default: false
stages:
# Allow manual builds to skip pre-check
@ -43,12 +46,15 @@ stages:
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: ${{ parameters.runTests }}
useVSPreview: ${{ parameters.useVSPreview }}
- ${{ if eq(parameters.runTests, true) }}:
- stage: Test_${{ platform }}

View File

@ -1,7 +1,7 @@
parameters:
- name: version
type: string
default: "8.0"
default: "9.0"
- name: sdk
type: boolean
default: false

View File

@ -1,4 +1,4 @@
$LatestVCToolsVersion = (([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml)).instances.instance.packages.package | ? { $_.id -eq "Microsoft.VisualCpp.CRT.Source" }).version;
$LatestVCToolsVersion = (([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest $env:VCWhereExtraVersionTarget -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml)).instances.instance.packages.package | ? { $_.id -eq "Microsoft.VisualCpp.CRT.Source" }).version;
Write-Output "Latest VCToolsVersion: $LatestVCToolsVersion"
Write-Output "Updating VCToolsVersion environment variable for job"

View File

@ -1,53 +1,53 @@
# PowerToys Contributor's Guide
Below is our guidance for how to report issues, propose new features, and submit contributions via Pull Requests (PRs). Our philosophy is heavily based around understanding the problem and scenarios first, this is why we follow this pattern before work has started.
Below is our guidance for reporting issues, proposing new features, and submitting contributions via Pull Requests (PRs). Our philosophy is to understand the problem and scenarios first, which is why we follow this pattern before work starts.
1. There is an issue
2. There has been a conversation
3. There is agreement on the problem, the fit for PowerToys, and the solution to the problem (implementation)
1. There is an issue.
2. There has been a conversation.
3. There is agreement on the problem, the fit for PowerToys, and the solution to the problem (implementation).
## Filing an issue
## Filing an Issue
Please follow this simple rule to help us eliminate any unnecessary wasted effort & frustration, and ensure an efficient and effective use of everyone's time - yours, ours, and other community members':
**Importance of Filing an Issue First**
> 👉 If you have a question, think you've discovered an issue, would like to propose a new feature, etc., then find/file an issue **BEFORE** starting work to fix/implement it.
Please follow this rule to help eliminate wasted effort and frustration, and to ensure an efficient and effective use of everyones time:
When requesting new features / enhancements, understanding problem and scenario around it is extremely important. Having additional evidence, data, tweets, blog posts, research, ... anything is extremely helpful. This information provides context to the scenario that may otherwise be lost.
> 👉 If you have a question, think you've discovered an issue, or would like to propose a new feature, please find/file an issue **BEFORE** starting work to fix/implement it.
* Don't know whether you're reporting an issue or requesting a feature? File an issue
* Have a question that you don't see answered in docs, videos, etc.? File an issue
* Want to know if we're planning on building a particular feature? File an issue
* Got a great idea for a new utility or feature? File an issue/request/idea
* Don't understand how to do something? File an issue/Community Guidance Request
* Found an existing issue that describes yours? Great - upvote and add additional commentary / info / repro-steps / etc.
When requesting new features or enhancements, providing additional evidence, data, tweets, blog posts, or research is extremely helpful. This information gives context to the scenario that may otherwise be lost.
A quick search before filing an issue also could be helpful. It is likely someone else has found the problem you're seeing, and someone may be working on or have already contributed a fix!
* Unsure whether its an issue or feature request? File an issue.
* Have a question that isn't answered in the docs, videos, etc.? File an issue.
* Want to know if were planning a particular feature? File an issue.
* Got a great idea for a new utility or feature? File an issue/request/idea.
* Dont understand how to do something? File an issue/Community Guidance Request.
* Found an existing issue that describes yours? Great! Upvote and add additional commentary, info, or repro steps.
### How to tell the PowerToys team this is an interesting thing to focus on
A quick search before filing an issue could be helpful. Its likely someone else has found the same problem, and they may even be working on or have already contributed a fix!
Upvote the original issue by clicking its [+😊] button and hitting 👍 (+1) icon or a different one. This way allows us to measure how impactful different issues are compared to others. The issue with comments like "+1", "me too", or similar is they actually make it harder to have a conversation and harder to quickly determine trending important requests.
### Indicating Interest in Issues
To let the team know which issues are important, upvote by clicking the [+😊] button and the 👍 icon on the original issue post. Avoid comments like "+1" or "me too" as they clutter the discussion and make it harder to prioritize requests.
---
## Contributing fixes / features
## Contributing Fixes/Features
Please comment on [our "Would you like to contribute to PowerToys?" thread](https://github.com/microsoft/PowerToys/issues/28769) to let us know you're interested in working on something before you start the work. Not only does this avoid multiple people unexpectedly working on the same thing at the same time but it enables us to make sure everyone is clear on what should be done to implement any new functionality. It's less work for everyone, in the long run, to establish this up front.
Please comment on our ["Would you like to contribute to PowerToys?"](https://github.com/microsoft/PowerToys/issues/28769) thread to let us know you're interested in working on something before you start. This helps avoid multiple people unexpectedly working on the same thing and ensures everyone is clear on what should be done. It's less work for everyone to establish this up front.
### Localization issues
### Localization Issues
Please file localization issues, so our internal localization team can identify and fix them. However we currently don't accept community Pull Requests fixing localization issues. Localization is handled by the internal Microsoft team only.
For localization issues, please file an issue to notify our internal localization team, as community PRs for localization aren't accepted. Localization is handled exclusively by the internal Microsoft team.
### To Spec or not to Spec
### To Spec or Not to Spec
A key point is for everyone to understand the approach that will be taken. We want to be sure if anyone does work, we will accept it in. Items that are larger in scope we'll want some type of spec to understand what is being planned and have a discussion. Specs help collaborators discuss different approaches to solve a problem, describe how the feature will behave, how the feature will impact the user, what happens if something goes wrong, etc. Driving towards agreement in a spec, before any code is written, often results in simpler code, and less wasted effort in the long run.
A key point is for everyone to understand the approach that will be taken. We want to be sure that any work done will be accepted. Larger-scope items will require a spec to outline the approach and allow for discussion. Specs help collaborators consider different solutions, describe feature behavior, and plan for errors. Achieving agreement in a spec before writing code often results in simpler code and less wasted effort.
For such scenarios, once a team member has agreed with your approach, skip ahead to the section headed "Development" section below.
Team members will be happy to help review specs and guide them to completion.
Once a team member has agreed with your approach, proceed to the "Development" section below. Team members are happy to help review specs and guide them to completion.
### Help Wanted
Once the team has approved an issue/spec approach to solving, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/PowerToys/labels/Help%20Wanted).
Once the team has approved an issue/spec approach, development can proceed. If no developers are immediately available, the spec may be parked and labeled "Help Wanted," ready for a developer to get started. For development opportunities, visit [Issues labeled Help Wanted](https://github.com/microsoft/PowerToys/labels/Help%20Wanted).
---
@ -55,18 +55,18 @@ Once the team has approved an issue/spec approach to solving, development can pr
Follow the [development guidelines](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md).
### Naming of features and functionality
### Naming Features and Functionality
Naming should be descriptive and straight forward. We want names to be clear about functionality and usefulness moving forward.
Names should be descriptive and straightforward, clearly reflecting functionality and usefulness.
### How can I become a collaborator on the PowerToys team
### Becoming a Collaborator on the PowerToys Team
Be a great community member. Just help out a lot and make useful additions, filing bugs/suggestions, help develop fixes and features, code reviews, and always, docs. Lets continue to make the PowerToys repository a great spot to learn and make a great set of utilities.
Be an active community member! Make helpful contributions by filing bugs, offering suggestions, developing fixes and features, conducting code reviews, and updating documentation.
When the time comes, Microsoft will reach out and help make you a formal team member. Just make sure they can reach out to you :)
When the time comes, Microsoft will reach out to you about becoming a formal team member. Just make sure they have a way to contact you. 😊
---
## Thank you
## Thank You
Thank you in advance for your contribution!
Thank you in advance for your contribution! We appreciate your help in making PowerToys a better tool for everyone.

1020
DATA_AND_PRIVACY.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -26,19 +26,19 @@
<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="2.5.187" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.7" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0-preview.24508.2" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
<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="8.0.0" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="8.0.10" />
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.0" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.0" />
<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. -->
<!--
@ -63,24 +63,26 @@
<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="8.0.0" />
<PackageVersion Include="System.CodeDom" Version="9.0.0" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.ComponentModel.Composition" Version="8.0.0" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="8.0.1" />
<PackageVersion Include="System.Data.OleDb" Version="8.0.1" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.0" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.0" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.0" />
<!-- 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="8.0.1" />
<PackageVersion Include="System.Drawing.Common" Version="8.0.7" />
<PackageVersion Include="System.IO.Abstractions" Version="17.2.3" />
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="17.2.3" />
<PackageVersion Include="System.Management" Version="8.0.0" />
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.0" />
<!-- 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.0" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.0" />
<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.0" />
<PackageVersion Include="System.Reactive" Version="6.0.1" />
<PackageVersion Include="System.Runtime.Caching" Version="8.0.1" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="8.0.1" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.0" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.0" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.0" />
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
<PackageVersion Include="UnitsNet" Version="5.56.0" />
<PackageVersion Include="UTF.Unknown" Version="2.5.1" />
@ -92,4 +94,4 @@
<PackageVersion Include="Microsoft.VariantAssignment.Client" Version="2.4.17140001" />
<PackageVersion Include="Microsoft.VariantAssignment.Contract" Version="3.0.16990001" />
</ItemGroup>
</Project>
</Project>

View File

@ -1318,18 +1318,18 @@ EXHIBIT A -Mozilla Public License.
- Mages 2.0.2
- Markdig.Signed 0.34.0
- MessagePack 2.5.187
- Microsoft.CodeAnalysis.NetAnalyzers 8.0.0
- Microsoft.Data.Sqlite 8.0.7
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0-preview.24508.2
- Microsoft.Data.Sqlite 9.0.0
- Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16
- Microsoft.Extensions.DependencyInjection 8.0.0
- Microsoft.Extensions.Hosting 8.0.0
- Microsoft.Extensions.Hosting.WindowsServices 8.0.0
- Microsoft.Extensions.Logging 8.0.0
- Microsoft.Extensions.Logging.Abstractions 8.0.0
- Microsoft.Extensions.DependencyInjection 9.0.0
- Microsoft.Extensions.Hosting 9.0.0
- Microsoft.Extensions.Hosting.WindowsServices 9.0.0
- Microsoft.Extensions.Logging 9.0.0
- Microsoft.Extensions.Logging.Abstractions 9.0.0
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
- Microsoft.Web.WebView2 1.0.2739.15
- Microsoft.Win32.SystemEvents 8.0.0
- Microsoft.Windows.Compatibility 8.0.10
- Microsoft.Win32.SystemEvents 9.0.0
- Microsoft.Windows.Compatibility 9.0.0
- Microsoft.Windows.CsWin32 0.2.46-beta
- Microsoft.Windows.CsWinRT 2.1.5
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
@ -1346,22 +1346,23 @@ EXHIBIT A -Mozilla Public License.
- SharpCompress 0.37.2
- StreamJsonRpc 2.19.27
- StyleCop.Analyzers 1.2.0-beta.556
- System.CodeDom 8.0.0
- System.CodeDom 9.0.0
- System.CommandLine 2.0.0-beta4.22272.1
- System.ComponentModel.Composition 8.0.0
- System.Configuration.ConfigurationManager 8.0.1
- System.Data.OleDb 8.0.1
- System.ComponentModel.Composition 9.0.0
- System.Configuration.ConfigurationManager 9.0.0
- System.Data.OleDb 9.0.0
- System.Data.SqlClient 4.8.6
- System.Diagnostics.EventLog 8.0.1
- System.Drawing.Common 8.0.7
- System.IO.Abstractions 17.2.3
- System.IO.Abstractions.TestingHelpers 17.2.3
- System.Management 8.0.0
- System.Diagnostics.EventLog 9.0.0
- System.Diagnostics.PerformanceCounter 9.0.0
- System.Drawing.Common 9.0.0
- System.IO.Abstractions 21.0.29
- System.IO.Abstractions.TestingHelpers 21.0.29
- System.Management 9.0.0
- System.Reactive 6.0.1
- System.Runtime.Caching 8.0.1
- System.ServiceProcess.ServiceController 8.0.1
- System.Text.Encoding.CodePages 8.0.0
- System.Text.Json 8.0.5
- System.Runtime.Caching 9.0.0
- System.ServiceProcess.ServiceController 9.0.0
- System.Text.Encoding.CodePages 9.0.0
- System.Text.Json 9.0.0
- UnicodeInformation 2.6.0
- UnitsNet 5.56.0
- UTF.Unknown 2.5.1

View File

@ -630,6 +630,8 @@ 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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesCsharpLibrary", "src\modules\Workspaces\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj", "{89D0E199-B17A-418C-B2F8-7375B6708357}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@ -2246,6 +2248,30 @@ Global
{8A08D663-4995-40E3-B42C-3F910625F284}.Release|x64.Build.0 = Release|x64
{8A08D663-4995-40E3-B42C-3F910625F284}.Release|x86.ActiveCfg = Release|x64
{8A08D663-4995-40E3-B42C-3F910625F284}.Release|x86.Build.0 = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.ActiveCfg = Debug|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.Build.0 = Debug|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.ActiveCfg = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.Build.0 = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.ActiveCfg = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.Build.0 = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.ActiveCfg = Release|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.Build.0 = Release|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.ActiveCfg = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.Build.0 = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.ActiveCfg = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.Build.0 = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.Build.0 = Debug|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.ActiveCfg = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.Build.0 = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.ActiveCfg = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.Build.0 = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.ActiveCfg = Release|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.Build.0 = Release|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.ActiveCfg = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.Build.0 = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.ActiveCfg = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.Build.0 = Release|x64
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|ARM64.Build.0 = Debug|ARM64
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|x64.ActiveCfg = Debug|x64
@ -2646,30 +2672,6 @@ Global
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.Build.0 = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.ActiveCfg = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.Build.0 = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.ActiveCfg = Debug|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.Build.0 = Debug|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.ActiveCfg = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.Build.0 = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.ActiveCfg = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.Build.0 = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.ActiveCfg = Release|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.Build.0 = Release|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.ActiveCfg = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.Build.0 = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.ActiveCfg = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.Build.0 = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.Build.0 = Debug|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.ActiveCfg = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.Build.0 = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.ActiveCfg = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.Build.0 = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.ActiveCfg = Release|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.Build.0 = Release|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.ActiveCfg = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.Build.0 = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.ActiveCfg = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.Build.0 = Release|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.ActiveCfg = Debug|ARM64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.Build.0 = Debug|ARM64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|x64.ActiveCfg = Debug|x64
@ -2778,6 +2780,18 @@ Global
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.Build.0 = Release|x64
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.ActiveCfg = Release|x64
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.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
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x64.Build.0 = Debug|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x86.ActiveCfg = Debug|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x86.Build.0 = Debug|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|ARM64.ActiveCfg = Release|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|ARM64.Build.0 = Release|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x64.ActiveCfg = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x64.Build.0 = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.ActiveCfg = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -2956,6 +2970,8 @@ Global
{B5EB9FE9-37EF-47C3-B8B8-81AD3C2972C2} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
{A663E672-B26D-4EC0-BEAB-FE2E424AC46F} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
{8A08D663-4995-40E3-B42C-3F910625F284} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{923DF87C-CA99-4D1C-B1D2-959174E95BFA} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{D962A009-834F-4EEC-AABB-430DF8F98E39} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{9873BA05-4C41-4819-9283-CF45D795431B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{FC373B24-3293-453C-AAF5-CF2909DCEE6A} = {9873BA05-4C41-4819-9283-CF45D795431B}
@ -2995,8 +3011,6 @@ Global
{8ACB33D9-C95B-47D4-8363-9731EE0930A0} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
{CA716AE6-FE5C-40AC-BB8F-2C87912687AC} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{F055103B-F80B-4D0C-BF48-057C55620033} = {5A7818A8-109C-4E1C-850D-1A654E234B0E}
{923DF87C-CA99-4D1C-B1D2-959174E95BFA} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{A2221D7E-55E7-4BEA-90D1-4F162D670BBF} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{BE126CBB-AE12-406A-9837-A05ACFCA57A7} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{14CB58B7-D280-4A7A-95DE-4B2DF14EA000} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
@ -3009,6 +3023,7 @@ 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}
{89D0E199-B17A-418C-B2F8-7375B6708357} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

146
README.md
View File

@ -34,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.86%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.85%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.1/PowerToysUserSetup-0.85.1-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.1/PowerToysUserSetup-0.85.1-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.1/PowerToysSetup-0.85.1-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.1/PowerToysSetup-0.85.1-arm64.exe
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.87%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.86%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.86.0/PowerToysUserSetup-0.86.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.86.0/PowerToysUserSetup-0.86.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.86.0/PowerToysSetup-0.86.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.86.0/PowerToysSetup-0.86.0-arm64.exe
| Description | Filename | sha256 hash |
|----------------|----------|-------------|
| Per user - x64 | [PowerToysUserSetup-0.85.1-x64.exe][ptUserX64] | 5F287C34BF68972C55D7C26585EA5C449B0DBA7D458BF7039CFF448E1D7B732B |
| Per user - ARM64 | [PowerToysUserSetup-0.85.1-arm64.exe][ptUserArm64] | 6D5C3B24156E6E66FD38AD15076B8442F0A1C5CFCBBDC33AD478FB27E5E086AE |
| Machine wide - x64 | [PowerToysSetup-0.85.1-x64.exe][ptMachineX64] | 1CDD3C9602F6E5DDC19C66A4FDFE4231389C08E6A037DD22C0A6471F10C7BE02 |
| Machine wide - ARM64 | [PowerToysSetup-0.85.1-arm64.exe][ptMachineArm64] | 6F4DC0217495973B974B7AC1099FD01A2A0FCEE96E8719074EC97FBBC0ECAC4A |
| Per user - x64 | [PowerToysUserSetup-0.86.0-x64.exe][ptUserX64] | CFB9608B28B8FF12C9A7C9814A6EF981636EB5AB261DC278C28EC93FD959CCE2 |
| Per user - ARM64 | [PowerToysUserSetup-0.86.0-arm64.exe][ptUserArm64] | 861CEDBFDCDA993D1D1056E3280319D5EA45D142CA3C737AB1FB4FABD651A5F5 |
| Machine wide - x64 | [PowerToysSetup-0.86.0-x64.exe][ptMachineX64] | 857DE9DC5938D9602F82DFD6183DB5E6823B875A412AEC59B4BE93617E27E9CD |
| Machine wide - ARM64 | [PowerToysSetup-0.86.0-arm64.exe][ptMachineArm64] | 6F37192534C195A02A80AAE1E449DF61C894C50763096A06195581801943FA31 |
This is our preferred method.
@ -92,113 +92,103 @@ 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.85 - September 2024 Update
### 0.86 - October 2024 Update
In this release, we focused on new features, stability, and improvements.
**Highlights**
- New utility: New+ - allows setting a personalized set of templates to quickly create files and folders from a File Explorer context menu. Thanks [@cgaarden](https://github.com/cgaarden)!
- Language selection - it's now possible to select which UI language should be used by PowerToys utilities.
- Lots of quality fixes for Workspaces, improving the number of supported applications.
- Reduced Peek memory usage by fixing image leaks. Thanks [@daverayment](https://github.com/daverayment)!
- Advanced Paste has new abilities: Image to text, and paste to file (text / png / html).
- In settings, we've adjusted the left navigation to group the utilities. As the number of utilities shipped with PowerToys keeps growing, we felt this was a needed adjustment. Thanks everyone for your feedback!
- Workspaces received many bug fixes, including the proper launching of many instances of the same application in the same workspace. Note, we are still actively looking at how to properly handle PWA detection.
- We've added a diagnostic data (telemetry) opt-in option in the Settings General tab. As it is off-by-default, we encourage users to turn it on as that helps direct our development efforts and their journeys. More information about the data we collect can be found in the [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation) and what each event does.
### General
- Added a general setting to select which UI language should be used in PowerToys utilities.
- Fixed internal code of some policies for Group Policy Objects, that were reading registry entries using the wrong internal functions, and structured code better to avoid future mistakes of the same kind. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added a setting for diagnostic data (telemetry) opt-in (off by default, however, see above for why we encourage you to opt-in!) and user controls to view data.
- Improved exception logging by adding the type of Exception and InnerException. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
### Advanced Paste
- Fixed some telemetry calls to signal Advanced Paste activation on the cases where a direct shortcut is being used without showing the UI.
- User-defined custom actions can only be used with AI turned on, so custom actions were disabled on Settings when AI is disabled and were hidden from the Advanced Paste UI.
- Added new built-in actions: Image to text, and paste txt, png or html as a file.
### Awake
### Mouse Jump
- Fixed tray icon behaviors, not appearing and showing incorrect time. Thanks [@dend](https://github.com/dend)!
- Refactored the common classes into a separate project. Thanks [@mikeclayton](https://github.com/mikeclayton)!
- Brought back the telemetry events that were deleted across previous refactoring efforts.
### Environment Variables Editor
### Mouse Without Borders
- Added the `_NT_SYMBOL_PATH`, `_NT_ALT_SYMBOL_PATH` and `_NT_SYMCACHE_PATH` as variables that are shown as lists. Thanks [@chwarr](https://github.com/chwarr)!
### FancyZones
- Allow snapping applications that were launched by Workspaces.
### File Locksmith
- Fixed an issue causing File Locksmith to be triggered by unrelated verbs in the context menu.
### Mouse Pointer Crosshairs
- Allow crosshairs radius to be 0 pixels. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)!
- Refactored the Logger common classes. Thanks [@mikeclayton](https://github.com/mikeclayton)!
### New+
- New utility - Allows setting a personalized set of templates to quickly create files and folders from a File Explorer context menu. Thanks [@cgaarden](https://github.com/cgaarden)!
- Added missing entry for New+ policy state reporting in the Bug Report tool. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added a policy for enabling/disabling whether filename extensions should be shown. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed the telemetry event for when the modules is enabled or disabled. (This was a hotfix for 0.85)
- Fixed bug when creating folders or files that contain Unicode characters. Thanks [@cgaarden](https://github.com/cgaarden)!
- Fixed bug when the name of a new folder collided with an already existing folder. Thanks [@cgaarden](https://github.com/cgaarden)!
- Updated the New+ icons to the fluent style.
### Peek
- Properly show file's modified date instead of creation date in the file previewer. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed memory leak caused by unmanaged bitmap images not being freed. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed an issue causing Peek to not be displayed the first time when using a preview handler to display files. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Prevent tooltip in file previewer from overlapping with title bar controls. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed memory leaks in thumbnails and refactored image previewer. Thanks [@daverayment](https://github.com/daverayment)!
- Folder preview enumeration of size and number of files is now more responsive and faster. Thanks [@daverayment](https://github.com/daverayment)!
### PowerToys Run
- Improved the message boxes to be more specific when PowerToys Run failed to initialize itself or any plugin. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Use capital letters when showing degree results in the Unit Converter plugin. Thanks [@PesBandi](https://github.com/PesBandi)!
- Handled a culture not found error when checking for right-to-left languages.
- Fixed the WebSearch plugin results title being trimmed in the UI. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)!
- The Unit Converter plugin will now show more significant digits. Thanks [@PesBandi](https://github.com/PesBandi)!
- Improved error handling when copying to the clipboard results in an error. Thanks [@PesBandi](https://github.com/PesBandi)!
### Quick Accent
- Add the Middle Eastern Romanization character set. Thanks [@PesBandi](https://github.com/PesBandi)!
- Add the degree sign, integral and vertical ellipsis when "All Languages" is selected. Thanks [@rddunphy](https://github.com/rddunphy)!
- Added support for the Serbian Cyrillic character set. Thanks [@Sirozha1337](https://github.com/Sirozha1337)!
### Registry Preview
- Adopted the Monaco Editor as the UI text editor. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
### Settings
- Fixed the link to the Workspaces documentation. (This was a hotfix for 0.84)
- Fixed flyout issues after the Windows App SDK upgrade. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed initialization for the New+ settings page. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed enabled state of a control on the New+ settings page if the module is enabled by policy. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed a crash when cancelling the template folder selection in the New+ settings page.
- Fixed a crash when trying to access a non-existing templates folder from the New+ page. (This was a hotfix for 0.85)
- Added a navigation tree to group utilities in the left navigation menu.
- Sorted the list of languages in the language selection combo box in the General tab. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed the state of the info bar about templates not being backed up to not close and react to the module's enabled state in the New+ page. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed a crash caused by a dangling thread.
- Clicking a notification about there being an update available should now correctly open the Settings application in the General tab.
- Fixed a UI freeze when trying to access the Diagnostic Data Viewer files. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
### Workspaces
- Fixed detecting and snapping applications like Discord. (This was a hotfix for 0.84)
- Fixed detecting and snapping applications like Steam. (This was a hotfix for 0.84)
- Fixed button visibility in the UI. (This was a hotfix for 0.84)
- Fixed an issue launching the wrong project when the editor was closed without saving or cancelling a new project.
- Properly handle repositioning windows running as administrator.
- Properly handle cases where the monitor where a workspace was saved is no longer present.
- Fixed the workspace launcher restarting itself in a loop without success.
- Properly handle standalone applications.
- Fixed issues causing icons to not show.
- Fixed launching the incorrect workspace when launching many workspaces quickly through shortcuts. (This was a hotfix for 0.85)
- Fixed launching many instances of the same application in a workspace.
- Fixed a crash when a previously captured monitor ID no longer existed.
- Fixed an issue causing the wrong coordinates to be saved for minimized applications.
- Fixed an issue causing a crash when stress testing workspace launching.
- Fixed application launching when UAC is off and every application always runs elevated.
### Documentation
- Fixed the thirdPartyRunPlugins.md entry for the RDP plugin. Thanks [@YisroelTech](https://github.com/YisroelTech)!
- Added HackMD plugin mention to thirdPartyRunPlugins.md. Thanks [@8LWXpg](https://github.com/8LWXpg)!
- Added SSH plugin mention to thirdPartyRunPlugins.md. Thanks [@8LWXpg](https://github.com/8LWXpg)!
- Added the [Data and Privacy documentation](https://github.com/microsoft/PowerToys/blob/main/DATA_AND_PRIVACY.md) to the repo.
### Development
- Upgraded Windows App SDK to 1.6.
- Upgraded the Target Platform Version to 10.0.22621.0.
- Added a bot trigger to automatically add a label to Workspaces issues. Thanks [@plante-msft](https://github.com/plante-msft)!
- Fixed a regular expression in the bot triggers for wanting to submit community contributions. Thanks [@PesBandi](https://github.com/PesBandi)!
- Fixed analyzer errors after the Visual Studio 17.12 update. Thanks [@snickler](https://github.com/snickler)!
- Fixed the TSA configuration for release CI builds.
- Refactored automated file component generation during installer builds.
- Rewrote the Azure Devops build system to be more modular and share more definitions between PR CI and Release CI.
- Fixed debugging of the New+ page of the Settings application when a settings file was not present.
- Fixed setting the version of the App Manifest in the File Locksmith and New+ context menu app packages.
- Fixed abstracted UI library nuget package signing on release CI.
- Removed build status from GitHub README.
- Fixed the CI precheck action to take into account the recent changes in CI actions.
- Added the new Microsoft org issue types to the issue templates. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
- Updated System.Text.Json to 8.0.5 and System.Runtime.Caching to 8.0.1 and related dependencies to the latest to address security reports. Thanks [@snickler](https://github.com/snickler)!
- Updated WinAppSDK to 1.6.1 and CsWinRT to 2.1.5. Thanks [@snickler](https://github.com/snickler)!
- Upgraded the WpfUI dependency to 3.0.5.
- Updated MessagePack to 2.5.187 and StreamJsonRpc to 2.19.27 to address security reports.
- Removed some of the hacks that are no longer needed that tried to force same dependency versions in .csproj files.
- Removed the Markdown file exclusions from the conditions that trigger a full CI test.
- CI fails again when there are XAML style errors in a PR.
- Fixed CI actions that were not failing when one of the powershell scripts they tried to run was failing.
- Fixed analyzer violations to allow fully building PowerToys on Visual Studio 17.12. Thanks [@snickler](https://github.com/snickler)!
#### What is being planned for version 0.86
#### What is being planned for version 0.87
For [v0.86][github-next-release-work], we'll work on the items below:
For [v0.87][github-next-release-work], we'll work on the items below:
- Stability / bug fixes
- New module: File Actions Menu
@ -214,7 +204,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct][oss-conduct
## Privacy Statement
The application logs basic telemetry. Our Telemetry Data page (Coming Soon) has the trends from the telemetry. Please read the [Microsoft privacy statement][privacy-link] for more information.
The application logs basic diagnostic data (telemetry). For more information on privacy and what we collect, see our [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation).
[oss-CLA]: https://cla.opensource.microsoft.com
[oss-conduct-code]: CODE_OF_CONDUCT.md

View File

@ -39,6 +39,8 @@ Contact the developers of a plugin directly for assistance with a specific plugi
| [GitHubRepo](https://github.com/8LWXpg/PowerToysRun-GitHubRepo) | [8LWXpg](https://github.com/8LWXpg) | Search and open GitHub repositories |
| [ProcessKiller](https://github.com/8LWXpg/PowerToysRun-ProcessKiller) | [8LWXpg](https://github.com/8LWXpg) | Search and kill processes |
| [ChatGPT](https://github.com/ferraridavide/ChatGPTPowerToys) | [ferraridavide](https://github.com/ferraridavide) | Ask a question to ChatGPT |
| [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 |
## Extending software plugins
@ -58,3 +60,4 @@ Below are community created plugins that target a website or software. They are
| [PowerSearch for 1Password](https://github.com/KairuDeibisu/PowerToysRunPlugin1Password) | [KairuDeibisu](https://github.com/KairuDeibisu) | An unofficial plugin for searching 1Password for usernames and passwords |
| [HackMD](https://github.com/8LWXpg/PowerToysRun-HackMD) | [8LWXpg](https://github.com/8LWXpg) | Open HackMD notes |
| [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 |

View File

@ -3,7 +3,7 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WindowsSdkPackageVersion>10.0.22621.48</WindowsSdkPackageVersion>
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
<TargetFramework>net9.0-windows10.0.22621.0</TargetFramework>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
@ -14,7 +14,7 @@
<WarningLevel>4</WarningLevel>
<NoWarn></NoWarn>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<WarningsNotAsErrors>CA1720</WarningsNotAsErrors>
<WarningsNotAsErrors>CA1720;CA1859;CA2263;CA2022</WarningsNotAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">

View File

@ -4,6 +4,7 @@
<PropertyGroup>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<AssemblyName>PowerToys.Common.UI</AssemblyName>
</PropertyGroup>

View File

@ -4,7 +4,6 @@
using System;
using System.IO;
using System.IO.Abstractions;
using System.Text.Json;
using System.Text.Json.Serialization;
@ -23,15 +22,14 @@ namespace ManagedCommon
public static string LoadLanguage()
{
FileSystem fileSystem = new FileSystem();
var localAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var file = localAppDataDir + SettingsFilePath + SettingsFile;
if (fileSystem.File.Exists(file))
if (File.Exists(file))
{
try
{
Stream inputStream = fileSystem.File.Open(file, FileMode.Open);
var inputStream = File.Open(file, FileMode.Open);
StreamReader reader = new StreamReader(inputStream);
string data = reader.ReadToEnd();
inputStream.Close();

View File

@ -5,7 +5,7 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO.Abstractions;
using System.IO;
using System.Reflection;
using PowerToys.Interop;
@ -14,7 +14,6 @@ namespace ManagedCommon
{
public static class Logger
{
private static readonly IFileSystem _fileSystem = new FileSystem();
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
private static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location).ProductVersion;
@ -41,12 +40,12 @@ namespace ManagedCommon
applicationLogPath = Constants.AppDataPath() + applicationLogPath + "\\" + Version;
}
if (!_fileSystem.Directory.Exists(applicationLogPath))
if (!Directory.Exists(applicationLogPath))
{
_fileSystem.Directory.CreateDirectory(applicationLogPath);
Directory.CreateDirectory(applicationLogPath);
}
var logFilePath = _fileSystem.Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
var logFilePath = Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));

View File

@ -15,7 +15,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IO.Abstractions" />
<PackageReference Include="System.Management" />
</ItemGroup>

View File

@ -26,7 +26,7 @@ namespace Microsoft.PowerToys.Telemetry
private readonly bool telemetryEnabled = DataDiagnosticsSettings.GetEnabledValue(); // This is the global telemetry setting on whether to log events
private readonly bool telemetryRecordingEnabled = DataDiagnosticsSettings.GetViewEnabledValue(); // This is the setting for recording telemetry events to disk for viewing
private readonly string etwFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\PowerToys\", "etw");
private string etwFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\PowerToys\", "etw");
private bool disposedValue;
private string sessionName;
private string etwFilePath;
@ -49,6 +49,18 @@ namespace Microsoft.PowerToys.Telemetry
/// Initializes a new instance of the <see cref="ETWTrace"/> class.
/// </summary>
public ETWTrace()
{
Init();
}
public ETWTrace(string etwPath)
{
this.etwFolderPath = etwPath;
Init();
}
private void Init()
{
if (File.Exists(etwFolderPath))
{

View File

@ -178,7 +178,8 @@ void notifications::show_toast_with_activations(std::wstring message,
std::wstring title,
std::wstring_view background_handler_id,
std::vector<action_t> actions,
toast_params params)
toast_params params,
std::wstring launch_uri)
{
// DO NOT LOCALIZE any string in this function, because they're XML tags and a subject to
// https://learn.microsoft.com/windows/uwp/design/shell/tiles-and-notifications/toast-xml-schema
@ -189,7 +190,18 @@ void notifications::show_toast_with_activations(std::wstring message,
// We must set toast's title and contents immediately, because some of the toasts we send could be snoozed.
// Windows instantiates the snoozed toast from scratch before showing it again, so all bindings that were set
// using NotificationData would be empty.
toast_xml += LR"(<?xml version="1.0"?><toast><visual><binding template="ToastGeneric">)";
// Add the launch attribute if launch_uri is provided, otherwise omit it
toast_xml += LR"(<?xml version="1.0"?>)";
if (!launch_uri.empty())
{
toast_xml += LR"(<toast launch=")" + launch_uri + LR"(" activationType="protocol">)"; // Use the launch URI if provided
}
else
{
toast_xml += LR"(<toast>)"; // No launch attribute if empty
}
toast_xml += LR"(<visual><binding template="ToastGeneric">)";
toast_xml += LR"(<text id="1">)";
toast_xml += std::move(title);
toast_xml += LR"(</text>)";

View File

@ -56,7 +56,7 @@ namespace notifications
using action_t = std::variant<link_button, background_activated_button, snooze_button>;
void show_toast(std::wstring plaintext_message, std::wstring title, toast_params params = {});
void show_toast_with_activations(std::wstring plaintext_message, std::wstring title, std::wstring_view background_handler_id, std::vector<action_t> actions, toast_params params = {});
void show_toast_with_activations(std::wstring plaintext_message, std::wstring title, std::wstring_view background_handler_id, std::vector<action_t> actions, toast_params params = {}, std::wstring launch_uri = L"");
void update_toast_progress_bar(std::wstring_view tag, progress_bar_params params);
void remove_toasts_by_tag(std::wstring_view tag);
void remove_all_scheduled_toasts();

View File

@ -21,7 +21,7 @@ namespace AdvancedPaste.Settings
private readonly SettingsUtils _settingsUtils;
private readonly TaskScheduler _taskScheduler;
private readonly IFileSystemWatcher _watcher;
private readonly object _loadingSettingsLock = new();
private readonly Lock _loadingSettingsLock = new();
private readonly List<PasteFormats> _additionalActions;
private readonly List<AdvancedPasteCustomAction> _customActions;

View File

@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
<TargetFramework>net9.0-windows10.0.22621.0</TargetFramework>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<PublishDir>$(PowerToysRoot)\$(Platform)\$(Configuration)\WinUI3Apps</PublishDir>

View File

@ -276,7 +276,7 @@ namespace Hosts.Tests
service.RemoveReadOnlyAttribute();
var readOnly = fileSystem.FileInfo.FromFileName(service.HostsFilePath).Attributes.HasFlag(FileAttributes.ReadOnly);
var readOnly = fileSystem.FileInfo.New(service.HostsFilePath).Attributes.HasFlag(FileAttributes.ReadOnly);
Assert.IsFalse(readOnly);
}
@ -295,7 +295,7 @@ namespace Hosts.Tests
await service.WriteAsync("# Empty hosts file", Enumerable.Empty<Entry>());
var hidden = fileSystem.FileInfo.FromFileName(service.HostsFilePath).Attributes.HasFlag(FileAttributes.Hidden);
var hidden = fileSystem.FileInfo.New(service.HostsFilePath).Attributes.HasFlag(FileAttributes.Hidden);
Assert.IsTrue(hidden);
}
}

View File

@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
@ -9,7 +10,7 @@ using System.IO.Abstractions;
namespace Hosts.Tests.Mocks
{
public class MockFileSystemWatcher : FileSystemWatcherBase
public partial class MockFileSystemWatcher : FileSystemWatcherBase
{
public override bool IncludeSubdirectories { get; set; }
@ -27,26 +28,35 @@ namespace Hosts.Tests.Mocks
public override ISynchronizeInvoke SynchronizingObject { get; set; }
public override Collection<string> Filters => throw new System.NotImplementedException();
public override Collection<string> Filters => throw new NotImplementedException();
public override WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) => default;
public override IFileSystem FileSystem => throw new NotImplementedException();
public override WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) => default;
public override IContainer Container => throw new NotImplementedException();
public MockFileSystemWatcher(string path) => Path = path;
public override void BeginInit() => throw new NotImplementedException();
public override void EndInit() => throw new NotImplementedException();
public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout) => throw new NotImplementedException();
public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) => throw new NotImplementedException();
public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) => throw new NotImplementedException();
public MockFileSystemWatcher()
{
}
public MockFileSystemWatcher(string path)
{
Path = path;
}
public MockFileSystemWatcher(string path, string filter)
{
Path = path;
Filter = filter;
}
public override void BeginInit()
{
}
public override void EndInit()
{
}
}
}

View File

@ -2,18 +2,22 @@
// 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.IO;
using System.IO.Abstractions;
namespace Hosts.Tests.Mocks
{
public class MockFileSystemWatcherFactory : IFileSystemWatcherFactory
{
public IFileSystemWatcher CreateNew() => new MockFileSystemWatcher(null);
public IFileSystem FileSystem => throw new NotImplementedException();
public IFileSystemWatcher CreateNew(string path) => new MockFileSystemWatcher(path);
public IFileSystemWatcher New() => new MockFileSystemWatcher();
public IFileSystemWatcher CreateNew(string path, string filter) => new MockFileSystemWatcher(path, filter);
public IFileSystemWatcher New(string path) => new MockFileSystemWatcher(path);
public IFileSystemWatcher FromPath(string path) => new MockFileSystemWatcher(path);
public IFileSystemWatcher New(string path, string filter) => new MockFileSystemWatcher(path, filter);
public IFileSystemWatcher Wrap(FileSystemWatcher fileSystemWatcher) => throw new NotImplementedException();
}
}

View File

@ -14,7 +14,7 @@ namespace Hosts.Helpers
public static void WaitForEventLoop(string eventName, Action callback)
{
var dispatcherQueue = DispatcherQueue.GetForCurrentThread();
new Thread(() =>
var t = new Thread(() =>
{
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
while (true)
@ -24,7 +24,10 @@ namespace Hosts.Helpers
dispatcherQueue.TryEnqueue(() => callback());
}
}
}).Start();
});
t.IsBackground = true;
t.Start();
}
}
}

View File

@ -20,7 +20,7 @@ namespace Hosts.Settings
private readonly SettingsUtils _settingsUtils;
private readonly IFileSystemWatcher _watcher;
private readonly object _loadingSettingsLock = new object();
private readonly Lock _loadingSettingsLock = new Lock();
public bool ShowStartupWarning { get; private set; }

View File

@ -216,6 +216,12 @@ public:
m_hShowAdminEvent = nullptr;
}
if (m_hTerminateEvent)
{
CloseHandle(m_hTerminateEvent);
m_hTerminateEvent = nullptr;
}
delete this;
}
@ -280,6 +286,7 @@ public:
SetEvent(m_hTerminateEvent);
WaitForSingleObject(m_hProcess, 1500);
TerminateProcess(m_hProcess, 1);
ResetEvent(m_hTerminateEvent);
}
m_enabled = false;

View File

@ -52,7 +52,7 @@ namespace HostsUILib.Helpers
_hostsFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"System32\drivers\etc\hosts");
_fileSystemWatcher = _fileSystem.FileSystemWatcher.CreateNew();
_fileSystemWatcher = _fileSystem.FileSystemWatcher.New();
_fileSystemWatcher.Path = _fileSystem.Path.GetDirectoryName(HostsFilePath);
_fileSystemWatcher.Filter = _fileSystem.Path.GetFileName(HostsFilePath);
_fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
@ -130,7 +130,7 @@ namespace HostsUILib.Helpers
throw new NotRunningElevatedException();
}
if (_fileSystem.FileInfo.FromFileName(HostsFilePath).IsReadOnly)
if (_fileSystem.FileInfo.New(HostsFilePath).IsReadOnly)
{
throw new ReadOnlyHostsException();
}
@ -200,7 +200,7 @@ namespace HostsUILib.Helpers
}
// FileMode.OpenOrCreate is necessary to prevent UnauthorizedAccessException when the hosts file is hidden
using var stream = _fileSystem.FileStream.Create(HostsFilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, _defaultBufferSize, FileOptions.Asynchronous);
using var stream = _fileSystem.FileStream.New(HostsFilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, _defaultBufferSize, FileOptions.Asynchronous);
using var writer = new StreamWriter(stream, Encoding);
foreach (var line in lines)
{
@ -305,7 +305,7 @@ namespace HostsUILib.Helpers
public void RemoveReadOnlyAttribute()
{
var fileInfo = _fileSystem.FileInfo.FromFileName(HostsFilePath);
var fileInfo = _fileSystem.FileInfo.New(HostsFilePath);
if (fileInfo.IsReadOnly)
{
fileInfo.IsReadOnly = false;

View File

@ -3,13 +3,14 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Threading;
using System.Windows.Threading;
namespace MouseJumpUI.Helpers;
internal sealed class ThrottledActionInvoker
{
private readonly object _invokerLock = new();
private readonly Lock _invokerLock = new();
private readonly DispatcherTimer _timer;
private Action? _actionToRun;

View File

@ -368,7 +368,7 @@ namespace MouseWithoutBorders
}
}
private static readonly object ClipboardThreadOldLock = new();
private static readonly Lock ClipboardThreadOldLock = new();
private static System.Threading.Thread clipboardThreadOld;
internal static void GetRemoteClipboard(string postAction)

View File

@ -32,7 +32,7 @@ namespace MouseWithoutBorders
{
public class Thread
{
private static readonly object ThreadsLock = new();
private static readonly Lock ThreadsLock = new();
private static List<System.Threading.Thread> threads;
private readonly System.Threading.Thread thread;

View File

@ -43,7 +43,7 @@ namespace MouseWithoutBorders
internal partial class Common
{
private static readonly object McMatrixLock = new();
private static readonly Lock McMatrixLock = new();
internal const byte MAX_MACHINE = 4;
internal const byte MAX_SOCKET = MAX_MACHINE * 2;

View File

@ -474,7 +474,7 @@ namespace MouseWithoutBorders
}
}
private static readonly object InputSimulationLock = new();
private static readonly Lock InputSimulationLock = new();
internal static void DoSomethingInTheInputSimulationThread(ThreadStart target)
{

View File

@ -10,6 +10,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace MouseWithoutBorders.Class
{
@ -36,12 +37,12 @@ namespace MouseWithoutBorders.Class
/// </remarks>
internal class MachinePool
{
private readonly object @lock;
private readonly Lock @lock;
private readonly List<MachineInf> list;
public MachinePool()
{
@lock = new object();
@lock = new Lock();
list = new List<MachineInf>();
}

View File

@ -13,6 +13,7 @@ using System.IO.Abstractions;
using System.Linq;
using System.Security.Cryptography;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@ -44,7 +45,7 @@ namespace MouseWithoutBorders.Class
internal bool Changed;
private readonly SettingsUtils _settingsUtils;
private readonly object _loadingSettingsLock = new object();
private readonly Lock _loadingSettingsLock = new Lock();
private readonly IFileSystemWatcher _watcher;
private MouseWithoutBordersProperties _properties;

View File

@ -826,7 +826,7 @@ namespace MouseWithoutBorders.Class
}
private static readonly Dictionary<string, List<IPAddress>> BadIPs = new();
private static readonly object BadIPsLock = new();
private static readonly Lock BadIPsLock = new();
private static bool IsBadIP(string machineName, IPAddress ip)
{

View File

@ -19,6 +19,7 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("The thickness of the border around the field")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int BorderSize
{
get => _borderSize;
@ -33,6 +34,7 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("The color of the border around the field")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Color BorderColor
{
get => _borderColor;
@ -47,6 +49,7 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("The color of the border around the field when it has focus")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Color FocusColor
{
get => _focusColor;
@ -59,12 +62,14 @@ namespace MouseWithoutBorders
[Category("Behavior")]
[Description("The maximum number of characters that can be typed in the field")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int MaximumLength
{
get => InnerField.MaxLength;
set => InnerField.MaxLength = value;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override string Text
{
get => InnerField.Text;

View File

@ -19,12 +19,14 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("Image to show when Mouse is pressed on button")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image DownImage { get; set; }
private Image _normalImage;
[Category("Appearance")]
[Description("Image to show when button is in normal state")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image NormalImage
{
get => _normalImage;
@ -37,10 +39,12 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("Image to show when Mouse hovers over button")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image HoverImage { get; set; }
[Category("Appearance")]
[Description("Image to show when button is disabled")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image DisabledImage { get; set; }
private bool _hovering;

View File

@ -16,6 +16,7 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("The bounding rectangle of the check image in local co-ordinates")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Point ImageLocation
{
get => _imageLocation;
@ -28,6 +29,7 @@ namespace MouseWithoutBorders
private Point _textLocation;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Point TextLocation
{
get => _textLocation;
@ -47,6 +49,7 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("Image to show when Mouse is pressed on button")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image CheckedImage
{
get => _checkedImage;
@ -61,6 +64,7 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("Image to show when button is in normal state")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image UncheckedImage
{
get => _uncheckedImage;

View File

@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Windows.Forms;
// <summary>
@ -34,12 +35,14 @@ namespace MouseWithoutBorders
MachineEnabled = false;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal string MachineName
{
get => textBoxName.Text;
set => textBoxName.Text = value;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal bool MachineEnabled
{
get => checkBoxEnabled.Checked;
@ -52,6 +55,7 @@ namespace MouseWithoutBorders
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
internal bool Editable
{
set => textBoxName.Enabled = value;
@ -59,6 +63,7 @@ namespace MouseWithoutBorders
// get { return textBoxName.Enabled; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
internal bool CheckAble
{
set
@ -73,6 +78,7 @@ namespace MouseWithoutBorders
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
internal bool LocalHost
{
// get { return localhost; }
@ -165,6 +171,7 @@ namespace MouseWithoutBorders
return rv;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal SocketStatus StatusClient
{
get => statusClient;
@ -182,6 +189,7 @@ namespace MouseWithoutBorders
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal SocketStatus StatusServer
{
get => statusServer;

View File

@ -12,6 +12,7 @@ using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;
// <summary>
@ -30,7 +31,7 @@ namespace MouseWithoutBorders.Core;
internal static class Logger
{
internal static readonly string[] AllLogs = new string[MAX_LOG];
private static readonly object AllLogsLock = new();
private static readonly Lock AllLogsLock = new();
internal static readonly ConcurrentDictionary<string, int> LogCounter = new();
private const int MAX_LOG = 10000;
private static int allLogsIndex;

View File

@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
@ -32,6 +33,7 @@ namespace MouseWithoutBorders
private int _animationFrame;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool ReturnToSettings { get; set; }
public SetupPage3a()

View File

@ -58,12 +58,16 @@ namespace MouseWithoutBorders
private Timer helperTimer;
#pragma warning restore CA2213
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal int CurIcon { get; set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal NotifyIcon NotifyIcon { get; set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal System.Windows.Forms.ToolStripMenuItem MenuAllPC { get; set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal System.Windows.Forms.ContextMenuStrip MainMenu { get; set; }
internal FrmScreen()

View File

@ -19,7 +19,7 @@ namespace MouseWithoutBorders
public partial class FormHelper : System.Windows.Forms.Form
{
private readonly List<FocusArea> focusZone = new();
private readonly object bmScreenLock = new();
private readonly Lock bmScreenLock = new();
private long lastClipboardEventTime;
private IClipboardHelper remoteClipboardHelper;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

After

Width:  |  Height:  |  Size: 186 KiB

View File

@ -4,6 +4,7 @@
using System;
using System.ComponentModel.Composition;
using System.Threading;
using System.Windows.Threading;
namespace PowerOCR.Helpers;
@ -11,7 +12,7 @@ namespace PowerOCR.Helpers;
[Export(typeof(IThrottledActionInvoker))]
public sealed class ThrottledActionInvoker : IThrottledActionInvoker
{
private object _invokerLock = new object();
private Lock _invokerLock = new Lock();
private Action? _actionToRun;
private DispatcherTimer _timer;

View File

@ -24,7 +24,7 @@ namespace PowerOCR.Settings
private const int SettingsReadOnChangeDelayInMs = 300;
private readonly IFileSystemWatcher _watcher;
private readonly object _loadingSettingsLock = new();
private readonly Lock _loadingSettingsLock = new();
[ImportingConstructor]
public UserSettings(Helpers.IThrottledActionInvoker throttledActionInvoker)

View File

@ -0,0 +1,217 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Windows.Media.Imaging;
using Windows.Management.Deployment;
namespace WorkspacesCsharpLibrary.Models
{
public class BaseApplication : INotifyPropertyChanged, IDisposable
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
public string PwaAppId { get; set; }
public string AppPath { get; set; }
private bool _isNotFound;
public string PackagedId { get; set; }
public string PackagedName { get; set; }
public string PackagedPublisherID { get; set; }
public string Aumid { get; set; }
[JsonIgnore]
public bool IsNotFound
{
get
{
return _isNotFound;
}
set
{
if (_isNotFound != value)
{
_isNotFound = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsNotFound)));
}
}
}
private Icon _icon;
public Icon Icon
{
get
{
if (_icon == null)
{
try
{
if (IsPackagedApp)
{
Uri uri = GetAppLogoByPackageFamilyName();
var bitmap = new Bitmap(uri.LocalPath);
var iconHandle = bitmap.GetHicon();
_icon = Icon.FromHandle(iconHandle);
}
else if (IsEdge || IsChrome)
{
string iconFilename = PwaHelper.GetPwaIconFilename(PwaAppId);
if (!string.IsNullOrEmpty(iconFilename))
{
Bitmap bitmap;
if (iconFilename.EndsWith("ico", StringComparison.InvariantCultureIgnoreCase))
{
bitmap = new Bitmap(iconFilename);
}
else
{
bitmap = (Bitmap)Image.FromFile(iconFilename);
}
var iconHandle = bitmap.GetHicon();
_icon = Icon.FromHandle(iconHandle);
}
}
if (_icon == null)
{
_icon = Icon.ExtractAssociatedIcon(AppPath);
}
}
catch (Exception)
{
IsNotFound = true;
_icon = new Icon(@"Assets\Workspaces\DefaultIcon.ico");
}
}
return _icon;
}
}
private BitmapImage _iconBitmapImage;
public BitmapImage IconBitmapImage
{
get
{
if (_iconBitmapImage == null)
{
try
{
Bitmap previewBitmap = new Bitmap(32, 32);
using (Graphics graphics = Graphics.FromImage(previewBitmap))
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawIcon(Icon, new Rectangle(0, 0, 32, 32));
}
using (var memory = new MemoryStream())
{
previewBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
_iconBitmapImage = bitmapImage;
}
}
catch (Exception)
{
}
}
return _iconBitmapImage;
}
}
public bool IsEdge
{
get => AppPath.EndsWith("edge.exe", StringComparison.InvariantCultureIgnoreCase);
}
public bool IsChrome
{
get => AppPath.EndsWith("chrome.exe", StringComparison.InvariantCultureIgnoreCase);
}
public Uri GetAppLogoByPackageFamilyName()
{
var pkgManager = new PackageManager();
var pkg = pkgManager.FindPackagesForUser(string.Empty, PackagedId).FirstOrDefault();
if (pkg == null)
{
return null;
}
return pkg.Logo;
}
private bool? _isPackagedApp;
public bool IsPackagedApp
{
get
{
if (_isPackagedApp == null)
{
if (!AppPath.StartsWith("C:\\Program Files\\WindowsApps\\", StringComparison.InvariantCultureIgnoreCase))
{
_isPackagedApp = false;
}
else
{
string appPath = AppPath.Replace("C:\\Program Files\\WindowsApps\\", string.Empty);
Regex packagedAppPathRegex = new Regex(@"(?<APPID>[^_]*)_\d+.\d+.\d+.\d+_x64__(?<PublisherID>[^\\]*)", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
Match match = packagedAppPathRegex.Match(appPath);
_isPackagedApp = match.Success;
if (match.Success)
{
PackagedName = match.Groups["APPID"].Value;
PackagedPublisherID = match.Groups["PublisherID"].Value;
PackagedId = $"{PackagedName}_{PackagedPublisherID}";
Aumid = $"{PackagedId}!App";
}
}
}
return _isPackagedApp.Value;
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
}

View File

@ -0,0 +1,15 @@
// 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 WorkspacesCsharpLibrary
{
public class PwaApp
{
public required string Name { get; set; }
public required string IconFilename { get; set; }
public required string AppId { get; set; }
}
}

View File

@ -0,0 +1,90 @@
// 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.IO;
using System.Linq;
namespace WorkspacesCsharpLibrary
{
public class PwaHelper
{
private const string ChromeBase = "Google\\Chrome\\User Data\\Default\\Web Applications";
private const string EdgeBase = "Microsoft\\Edge\\User Data\\Default\\Web Applications";
private const string ResourcesDir = "Manifest Resources";
private const string IconsDir = "Icons";
private const string PwaDirIdentifier = "_CRX_";
private static List<PwaApp> pwaApps = new List<PwaApp>();
public PwaHelper()
{
InitPwaData(EdgeBase);
InitPwaData(ChromeBase);
}
private void InitPwaData(string p_baseDir)
{
var baseFolderName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), p_baseDir);
if (Directory.Exists(baseFolderName))
{
foreach (string subDir in Directory.GetDirectories(baseFolderName))
{
string dirName = Path.GetFileName(subDir);
if (!dirName.StartsWith(PwaDirIdentifier, StringComparison.InvariantCultureIgnoreCase))
{
continue;
}
string appId = dirName.Substring(PwaDirIdentifier.Length, dirName.Length - PwaDirIdentifier.Length).Trim('_');
foreach (string iconFile in Directory.GetFiles(subDir, "*.ico"))
{
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(iconFile);
pwaApps.Add(new PwaApp() { Name = filenameWithoutExtension, IconFilename = iconFile, AppId = appId });
break;
}
}
string resourcesDir = Path.Combine(baseFolderName, ResourcesDir);
if (Directory.Exists(resourcesDir))
{
foreach (string subDir in Directory.GetDirectories(resourcesDir))
{
string dirName = Path.GetFileName(subDir);
if (pwaApps.Any(app => app.AppId == dirName))
{
continue;
}
string iconsDir = Path.Combine(subDir, IconsDir);
if (Directory.Exists(iconsDir))
{
foreach (string iconFile in Directory.GetFiles(iconsDir, "*.png"))
{
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(iconFile);
pwaApps.Add(new PwaApp() { Name = filenameWithoutExtension, IconFilename = iconFile, AppId = dirName });
break;
}
}
}
}
}
}
public static string GetPwaIconFilename(string pwaAppId)
{
var candidates = pwaApps.Where(x => x.AppId == pwaAppId).ToList();
if (candidates.Count > 0)
{
return candidates.First().IconFilename;
}
return string.Empty;
}
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<AssemblyTitle>PowerToys.WorkspacesCsharpLibrary</AssemblyTitle>
<AssemblyDescription>PowerToys Workspaces Csharp Library</AssemblyDescription>
<Description>PowerToys Workspaces Csharp Library</Description>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
<AssemblyName>PowerToys.WorkspacesCsharpLibrary</AssemblyName>
</PropertyGroup>
</Project>

View File

@ -37,6 +37,8 @@ namespace WorkspacesEditor.Data
public string AppUserModelId { get; set; }
public string PwaAppId { get; set; }
public string CommandLineArguments { get; set; }
public bool IsElevated { get; set; }

View File

@ -13,18 +13,17 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Windows.Media.Imaging;
using ManagedCommon;
using Windows.Management.Deployment;
using WorkspacesCsharpLibrary;
using WorkspacesCsharpLibrary.Models;
namespace WorkspacesEditor.Models
{
public class Application : INotifyPropertyChanged, IDisposable
public class Application : BaseApplication, IDisposable
{
private bool _isInitialized;
public event PropertyChangedEventHandler PropertyChanged;
public Application()
{
}
@ -37,6 +36,7 @@ namespace WorkspacesEditor.Models
AppTitle = other.AppTitle;
PackageFullName = other.PackageFullName;
AppUserModelId = other.AppUserModelId;
PwaAppId = other.PwaAppId;
CommandLineArguments = other.CommandLineArguments;
IsElevated = other.IsElevated;
CanLaunchElevated = other.CanLaunchElevated;
@ -100,8 +100,6 @@ namespace WorkspacesEditor.Models
public string AppName { get; set; }
public string AppPath { get; set; }
public string AppTitle { get; set; }
public string PackageFullName { get; set; }
@ -187,26 +185,6 @@ namespace WorkspacesEditor.Models
public bool IsAppMainParamVisible { get => !string.IsNullOrWhiteSpace(_appMainParams); }
private bool _isNotFound;
[JsonIgnore]
public bool IsNotFound
{
get
{
return _isNotFound;
}
set
{
if (_isNotFound != value)
{
_isNotFound = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsNotFound)));
}
}
}
[JsonIgnore]
public bool IsHighlighted { get; set; }
@ -222,100 +200,6 @@ namespace WorkspacesEditor.Models
}
}
[JsonIgnore]
private Icon _icon = null;
[JsonIgnore]
public Icon Icon
{
get
{
if (_icon == null)
{
try
{
if (IsPackagedApp)
{
Uri uri = GetAppLogoByPackageFamilyName();
var bitmap = new Bitmap(uri.LocalPath);
var iconHandle = bitmap.GetHicon();
_icon = Icon.FromHandle(iconHandle);
}
else
{
_icon = Icon.ExtractAssociatedIcon(AppPath);
}
}
catch (Exception)
{
Logger.LogWarning($"Icon not found on app path: {AppPath}. Using default icon");
IsNotFound = true;
_icon = new Icon(@"Assets\Workspaces\DefaultIcon.ico");
}
}
return _icon;
}
}
public Uri GetAppLogoByPackageFamilyName()
{
var pkgManager = new PackageManager();
var pkg = pkgManager.FindPackagesForUser(string.Empty, PackagedId).FirstOrDefault();
if (pkg == null)
{
return null;
}
return pkg.Logo;
}
private BitmapImage _iconBitmapImage;
public BitmapImage IconBitmapImage
{
get
{
if (_iconBitmapImage == null)
{
try
{
Bitmap previewBitmap = new Bitmap(32, 32);
using (Graphics graphics = Graphics.FromImage(previewBitmap))
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawIcon(Icon, new Rectangle(0, 0, 32, 32));
}
using (var memory = new MemoryStream())
{
previewBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
_iconBitmapImage = bitmapImage;
}
}
catch (Exception e)
{
Logger.LogError($"Exception while drawing icon for app with path: {AppPath}. Exception message: {e.Message}");
}
}
return _iconBitmapImage;
}
}
private WindowPosition _position;
public WindowPosition Position
@ -367,56 +251,11 @@ namespace WorkspacesEditor.Models
}
}
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
public void InitializationFinished()
{
_isInitialized = true;
}
private bool? _isPackagedApp;
public string PackagedId { get; set; }
public string PackagedName { get; set; }
public string PackagedPublisherID { get; set; }
public string Aumid { get; set; }
public bool IsPackagedApp
{
get
{
if (_isPackagedApp == null)
{
if (!AppPath.StartsWith("C:\\Program Files\\WindowsApps\\", StringComparison.InvariantCultureIgnoreCase))
{
_isPackagedApp = false;
}
else
{
string appPath = AppPath.Replace("C:\\Program Files\\WindowsApps\\", string.Empty);
Regex packagedAppPathRegex = new Regex(@"(?<APPID>[^_]*)_\d+.\d+.\d+.\d+_x64__(?<PublisherID>[^\\]*)", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
Match match = packagedAppPathRegex.Match(appPath);
_isPackagedApp = match.Success;
if (match.Success)
{
PackagedName = match.Groups["APPID"].Value;
PackagedPublisherID = match.Groups["PublisherID"].Value;
PackagedId = $"{PackagedName}_{PackagedPublisherID}";
Aumid = $"{PackagedId}!App";
}
}
}
return _isPackagedApp.Value;
}
}
private bool _isExpanded;
public bool IsExpanded
@ -454,11 +293,6 @@ namespace WorkspacesEditor.Models
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
internal void CommandLineTextChanged(string newCommandLineValue)
{
CommandLineArguments = newCommandLineValue;

View File

@ -251,10 +251,11 @@ namespace WorkspacesEditor.Models
{
Models.Application newApp = new Models.Application()
{
Id = app.Id != null ? app.Id : $"{{{Guid.NewGuid().ToString()}}}",
Id = string.IsNullOrEmpty(app.Id) ? $"{{{Guid.NewGuid().ToString()}}}" : app.Id,
AppName = app.Application,
AppPath = app.ApplicationPath,
AppTitle = app.Title,
PwaAppId = string.IsNullOrEmpty(app.PwaAppId) ? string.Empty : app.PwaAppId,
PackageFullName = app.PackageFullName,
AppUserModelId = app.AppUserModelId,
Parent = this,

View File

@ -75,16 +75,16 @@ namespace WorkspacesEditor.Utils
foreach (Application app in appsIncluded)
{
if (repeatCounter.TryGetValue(app.AppPath, out int value))
if (repeatCounter.TryGetValue(app.AppPath + app.AppTitle, out int value))
{
repeatCounter[app.AppPath] = ++value;
repeatCounter[app.AppPath + app.AppTitle] = ++value;
}
else
{
repeatCounter.Add(app.AppPath, 1);
repeatCounter.Add(app.AppPath + app.AppTitle, 1);
}
app.RepeatIndex = repeatCounter[app.AppPath];
app.RepeatIndex = repeatCounter[app.AppPath + app.AppTitle];
}
foreach (Application app in project.Applications.Where(x => !x.IsIncluded))

View File

@ -31,7 +31,7 @@ namespace WorkspacesEditor.Utils
{
try
{
using (Stream inputStream = _fileSystem.File.Open(fileName, FileMode.Open))
using (FileSystemStream inputStream = _fileSystem.File.Open(fileName, FileMode.Open))
using (StreamReader reader = new StreamReader(inputStream))
{
string data = reader.ReadToEnd();

View File

@ -103,6 +103,7 @@ namespace WorkspacesEditor.Utils
Title = app.AppTitle,
PackageFullName = app.PackageFullName,
AppUserModelId = app.AppUserModelId,
PwaAppId = app.PwaAppId,
CommandLineArguments = app.CommandLineArguments,
IsElevated = app.IsElevated,
CanLaunchElevated = app.CanLaunchElevated,

View File

@ -14,10 +14,10 @@ using System.Linq;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry;
using WorkspacesCsharpLibrary;
using WorkspacesEditor.Data;
using WorkspacesEditor.Models;
using WorkspacesEditor.Telemetry;
@ -39,6 +39,7 @@ namespace WorkspacesEditor.ViewModels
private MainWindow _mainWindow;
private Timer lastUpdatedTimer;
private WorkspacesSettings settings;
private PwaHelper _pwaHelper;
public ObservableCollection<Project> Workspaces { get; set; } = new ObservableCollection<Project>();
@ -147,6 +148,7 @@ namespace WorkspacesEditor.ViewModels
settings = Utils.Settings.ReadSettings();
_orderByIndex = (int)settings.Properties.SortBy;
_workspacesEditorIO = workspacesEditorIO;
_pwaHelper = new PwaHelper();
lastUpdatedTimer = new System.Timers.Timer();
lastUpdatedTimer.Interval = 1000;
lastUpdatedTimer.Elapsed += LastUpdatedTimerElapsed;

View File

@ -65,7 +65,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ControlzEx" />
<PackageReference Include="ModernWpfUI" />
<PackageReference Include="System.IO.Abstractions" />
</ItemGroup>
@ -77,6 +76,7 @@
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
<ProjectReference Include="..\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">

View File

@ -80,7 +80,7 @@
Margin="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{Binding IconBitmapImage}" />
Source="{Binding IconBitmapImage, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock
Grid.Column="2"
Width="20"

View File

@ -18,6 +18,15 @@ using namespace Windows::Management::Deployment;
namespace AppLauncher
{
namespace NonLocalizable
{
const std::wstring EdgeFilename = L"msedge.exe";
const std::wstring EdgePwaFilename = L"msedge_proxy.exe";
const std::wstring ChromeFilename = L"chrome.exe";
const std::wstring ChromePwaFilename = L"chrome_proxy.exe";
const std::wstring PwaCommandLineAddition = L"--profile-directory=Default --app-id=";
}
Result<SHELLEXECUTEINFO, std::wstring> LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated)
{
std::wstring dir = std::filesystem::path(appPath).parent_path();
@ -133,30 +142,50 @@ namespace AppLauncher
launched = LaunchPackagedApp(app.packageFullName, launchErrors);
}
std::wstring appPathFinal;
std::wstring commandLineArgsFinal;
appPathFinal = app.path;
commandLineArgsFinal = app.commandLineArgs;
if (!launched && !app.pwaAppId.empty())
{
std::filesystem::path appPath(app.path);
if (appPath.filename() == NonLocalizable::EdgeFilename)
{
appPathFinal = appPath.parent_path() / NonLocalizable::EdgePwaFilename;
commandLineArgsFinal = NonLocalizable::PwaCommandLineAddition + app.pwaAppId + L" " + app.commandLineArgs;
}
if (appPath.filename() == NonLocalizable::ChromeFilename)
{
appPathFinal = appPath.parent_path() / NonLocalizable::ChromePwaFilename;
commandLineArgsFinal = NonLocalizable::PwaCommandLineAddition + app.pwaAppId + L" " + app.commandLineArgs;
}
}
if (!launched)
{
Logger::trace(L"Launching {} at {}", app.name, app.path);
Logger::trace(L"Launching {} at {}", app.name, appPathFinal);
DWORD dwAttrib = GetFileAttributesW(app.path.c_str());
DWORD dwAttrib = GetFileAttributesW(appPathFinal.c_str());
if (dwAttrib == INVALID_FILE_ATTRIBUTES)
{
Logger::error(L"File not found at {}", app.path);
launchErrors.push_back({ std::filesystem::path(app.path).filename(), L"File not found" });
Logger::error(L"File not found at {}", appPathFinal);
launchErrors.push_back({ std::filesystem::path(appPathFinal).filename(), L"File not found" });
return false;
}
auto res = LaunchApp(app.path, app.commandLineArgs, app.isElevated);
auto res = LaunchApp(appPathFinal, commandLineArgsFinal, app.isElevated);
if (res.isOk())
{
launched = true;
}
else
{
launchErrors.push_back({ std::filesystem::path(app.path).filename(), res.error() });
launchErrors.push_back({ std::filesystem::path(appPathFinal).filename(), res.error() });
}
}
Logger::trace(L"{} {} at {}", app.name, (launched ? L"launched" : L"not launched"), app.path);
Logger::trace(L"{} {} at {}", app.name, (launched ? L"launched" : L"not launched"), appPathFinal);
return launched;
}
}

View File

@ -38,6 +38,38 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cm
return 0;
}
std::wstring cmdLineStr{ GetCommandLineW() };
auto cmdArgs = split(cmdLineStr, L" ");
if (cmdArgs.workspaceId.empty())
{
Logger::warn("Incorrect command line arguments: no workspace id");
MessageBox(NULL, GET_RESOURCE_STRING(IDS_INCORRECT_ARGS).c_str(), GET_RESOURCE_STRING(IDS_WORKSPACES).c_str(), MB_ICONERROR | MB_OK);
return 1;
}
if (!cmdArgs.isRestarted)
{
// check if restart is needed. Only check it if not yet restarted to avoid endless restarting. Restart is needed if the process is elevated.
if (is_process_elevated())
{
Logger::warn("Workspaces Launcher is elevated, restart");
constexpr DWORD exe_path_size = 0xFFFF;
auto exe_path = std::make_unique<wchar_t[]>(exe_path_size);
GetModuleFileNameW(nullptr, exe_path.get(), exe_path_size);
const auto modulePath = get_module_folderpath();
std::string cmdLineStr(cmdline);
std::wstring cmdLineWStr(cmdLineStr.begin(), cmdLineStr.end());
std::wstring cmd = cmdArgs.workspaceId + L" " + std::to_wstring(cmdArgs.invokePoint) + L" " + NonLocalizable::restartedString;
RunNonElevatedEx(exe_path.get(), cmd, modulePath);
return 1;
}
}
auto mutex = CreateMutex(nullptr, true, instanceMutexName.c_str());
if (mutex == nullptr)
{
@ -50,34 +82,6 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cm
return 0;
}
std::wstring cmdLineStr{ GetCommandLineW() };
auto cmdArgs = split(cmdLineStr, L" ");
if (cmdArgs.workspaceId.empty())
{
Logger::warn("Incorrect command line arguments: no workspace id");
MessageBox(NULL, GET_RESOURCE_STRING(IDS_INCORRECT_ARGS).c_str(), GET_RESOURCE_STRING(IDS_WORKSPACES).c_str(), MB_ICONERROR | MB_OK);
return 1;
}
if (is_process_elevated())
{
Logger::warn("Workspaces Launcher is elevated, restart");
constexpr DWORD exe_path_size = 0xFFFF;
auto exe_path = std::make_unique<wchar_t[]>(exe_path_size);
GetModuleFileNameW(nullptr, exe_path.get(), exe_path_size);
const auto modulePath = get_module_folderpath();
std::string cmdLineStr(cmdline);
std::wstring cmdLineWStr(cmdLineStr.begin(), cmdLineStr.end());
std::wstring cmd = cmdArgs.workspaceId + L" " + std::to_wstring(cmdArgs.invokePoint);
RunNonElevatedEx(exe_path.get(), cmd, modulePath);
return 1;
}
// COM should be initialized before ShellExecuteEx is called.
if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
{

View File

@ -16,6 +16,8 @@ namespace WorkspacesLauncherUI.Data
public string AppUserModelId { get; set; }
public string PwaAppId { get; set; }
public string CommandLineArguments { get; set; }
public bool IsElevated { get; set; }

View File

@ -3,77 +3,23 @@
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ManagedCommon;
using Windows.Management.Deployment;
using WorkspacesCsharpLibrary.Models;
using WorkspacesLauncherUI.Data;
namespace WorkspacesLauncherUI.Models
{
public class AppLaunching : INotifyPropertyChanged, IDisposable
public class AppLaunching : BaseApplication, IDisposable
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
public ApplicationWrapper Application { get; set; }
public bool Loading => LaunchState == LaunchingState.Waiting || LaunchState == LaunchingState.Launched;
private Icon _icon;
public Icon Icon
{
get
{
if (_icon == null)
{
try
{
if (IsPackagedApp)
{
Uri uri = GetAppLogoByPackageFamilyName();
var bitmap = new Bitmap(uri.LocalPath);
var iconHandle = bitmap.GetHicon();
_icon = Icon.FromHandle(iconHandle);
}
else
{
_icon = Icon.ExtractAssociatedIcon(Application.ApplicationPath);
}
}
catch (Exception)
{
Logger.LogWarning($"Icon not found on app path: {Application.ApplicationPath}. Using default icon");
IsNotFound = true;
_icon = new Icon(@"Assets\Workspaces\DefaultIcon.ico");
}
}
return _icon;
}
}
public string Name
{
get
{
return Application.Application;
}
}
public string Name { get; set; }
public LaunchingState LaunchState { get; set; }
@ -96,128 +42,5 @@ namespace WorkspacesLauncherUI.Models
_ => new SolidColorBrush(System.Windows.Media.Color.FromArgb(255, 254, 0, 0)),
};
}
private bool _isNotFound;
[JsonIgnore]
public bool IsNotFound
{
get
{
return _isNotFound;
}
set
{
if (_isNotFound != value)
{
_isNotFound = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsNotFound)));
}
}
}
public Uri GetAppLogoByPackageFamilyName()
{
var pkgManager = new PackageManager();
var pkg = pkgManager.FindPackagesForUser(string.Empty, PackagedId).FirstOrDefault();
if (pkg == null)
{
return null;
}
return pkg.Logo;
}
private bool? _isPackagedApp;
public string PackagedId { get; set; }
public string PackagedName { get; set; }
public string PackagedPublisherID { get; set; }
public string Aumid { get; set; }
public bool IsPackagedApp
{
get
{
if (_isPackagedApp == null)
{
if (!Application.ApplicationPath.StartsWith("C:\\Program Files\\WindowsApps\\", StringComparison.InvariantCultureIgnoreCase))
{
_isPackagedApp = false;
}
else
{
string appPath = Application.ApplicationPath.Replace("C:\\Program Files\\WindowsApps\\", string.Empty);
Regex packagedAppPathRegex = new Regex(@"(?<APPID>[^_]*)_\d+.\d+.\d+.\d+_x64__(?<PublisherID>[^\\]*)", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
Match match = packagedAppPathRegex.Match(appPath);
_isPackagedApp = match.Success;
if (match.Success)
{
PackagedName = match.Groups["APPID"].Value;
PackagedPublisherID = match.Groups["PublisherID"].Value;
PackagedId = $"{PackagedName}_{PackagedPublisherID}";
Aumid = $"{PackagedId}!App";
}
}
}
return _isPackagedApp.Value;
}
}
private BitmapImage _iconBitmapImage;
public BitmapImage IconBitmapImage
{
get
{
if (_iconBitmapImage == null)
{
try
{
Bitmap previewBitmap = new Bitmap(32, 32);
using (Graphics graphics = Graphics.FromImage(previewBitmap))
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawIcon(Icon, new Rectangle(0, 0, 32, 32));
}
using (var memory = new MemoryStream())
{
previewBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
_iconBitmapImage = bitmapImage;
}
}
catch (Exception e)
{
Logger.LogError($"Exception while drawing icon for app with path: {Application.ApplicationPath}. Exception message: {e.Message}");
}
}
return _iconBitmapImage;
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
}

View File

@ -9,8 +9,10 @@ using System.ComponentModel;
using System.Diagnostics;
using ManagedCommon;
using WorkspacesCsharpLibrary;
using WorkspacesLauncherUI.Data;
using WorkspacesLauncherUI.Models;
using WorkspacesLauncherUI.Utils;
namespace WorkspacesLauncherUI.ViewModels
{
@ -20,6 +22,7 @@ namespace WorkspacesLauncherUI.ViewModels
private StatusWindow _snapshotWindow;
private int launcherProcessID;
private PwaHelper _pwaHelper;
public event PropertyChangedEventHandler PropertyChanged;
@ -30,6 +33,8 @@ namespace WorkspacesLauncherUI.ViewModels
public MainViewModel()
{
_pwaHelper = new PwaHelper();
// receive IPC Message
App.IPCMessageReceivedCallback = (string msg) =>
{
@ -54,7 +59,11 @@ namespace WorkspacesLauncherUI.ViewModels
{
appLaunchingList.Add(new AppLaunching()
{
Application = app.Application,
Name = app.Application.Application,
AppPath = app.Application.ApplicationPath,
PackagedName = app.Application.PackageFullName,
Aumid = app.Application.AppUserModelId,
PwaAppId = app.Application.PwaAppId,
LaunchState = app.State,
});
}

View File

@ -79,6 +79,7 @@
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
<ProjectReference Include="..\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">

View File

@ -23,7 +23,7 @@ namespace WorkspacesData
std::wstring settingsFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey);
return settingsFolderPath + L"\\temp-workspaces.json";
}
RECT WorkspacesProject::Application::Position::toRect() const noexcept
{
return RECT{ .left = x, .top = y, .right = x + width, .bottom = y + height };
@ -79,6 +79,7 @@ namespace WorkspacesData
const static wchar_t* AppPathID = L"application-path";
const static wchar_t* AppPackageFullNameID = L"package-full-name";
const static wchar_t* AppUserModelId = L"app-user-model-id";
const static wchar_t* PwaAppId = L"pwa-app-id";
const static wchar_t* AppTitleID = L"title";
const static wchar_t* CommandLineArgsID = L"command-line-arguments";
const static wchar_t* ElevatedID = L"is-elevated";
@ -98,6 +99,7 @@ namespace WorkspacesData
json.SetNamedValue(NonLocalizable::AppTitleID, json::value(data.title));
json.SetNamedValue(NonLocalizable::AppPackageFullNameID, json::value(data.packageFullName));
json.SetNamedValue(NonLocalizable::AppUserModelId, json::value(data.appUserModelId));
json.SetNamedValue(NonLocalizable::PwaAppId, json::value(data.pwaAppId));
json.SetNamedValue(NonLocalizable::CommandLineArgsID, json::value(data.commandLineArgs));
json.SetNamedValue(NonLocalizable::ElevatedID, json::value(data.isElevated));
json.SetNamedValue(NonLocalizable::CanLaunchElevatedID, json::value(data.canLaunchElevated));
@ -136,6 +138,11 @@ namespace WorkspacesData
result.appUserModelId = json.GetNamedString(NonLocalizable::AppUserModelId);
}
if (json.HasKey(NonLocalizable::PwaAppId))
{
result.pwaAppId = json.GetNamedString(NonLocalizable::PwaAppId);
}
result.commandLineArgs = json.GetNamedString(NonLocalizable::CommandLineArgsID);
if (json.HasKey(NonLocalizable::ElevatedID))
@ -330,11 +337,11 @@ namespace WorkspacesData
{
result.isShortcutNeeded = json.GetNamedBoolean(NonLocalizable::IsShortcutNeededID);
}
if (json.HasKey(NonLocalizable::MoveExistingWindowsID))
{
result.moveExistingWindows = json.GetNamedBoolean(NonLocalizable::MoveExistingWindowsID);
}
result.moveExistingWindows = json.GetNamedBoolean(NonLocalizable::MoveExistingWindowsID);
}
auto appsArray = json.GetNamedArray(NonLocalizable::AppsID);
for (uint32_t i = 0; i < appsArray.Size(); ++i)

View File

@ -31,6 +31,7 @@ namespace WorkspacesData
std::wstring path;
std::wstring packageFullName;
std::wstring appUserModelId;
std::wstring pwaAppId;
std::wstring commandLineArgs;
bool isElevated{};
bool canLaunchElevated{};

View File

@ -6,15 +6,22 @@
#include <workspaces-common/GuidUtils.h>
#include <workspaces-common/InvokePoint.h>
namespace NonLocalizable
{
const wchar_t restartedString[] = L"restarted";
}
struct CommandLineArgs
{
std::wstring workspaceId;
InvokePoint invokePoint;
bool isRestarted;
};
CommandLineArgs split(std::wstring s, const std::wstring& delimiter)
{
CommandLineArgs cmdArgs{};
cmdArgs.isRestarted = false;
size_t pos = 0;
std::wstring token;
@ -29,7 +36,11 @@ CommandLineArgs split(std::wstring s, const std::wstring& delimiter)
for (const auto& token : tokens)
{
if (!cmdArgs.workspaceId.empty())
if (token == NonLocalizable::restartedString)
{
cmdArgs.isRestarted = true;
}
else if (!cmdArgs.workspaceId.empty())
{
try
{
@ -47,7 +58,7 @@ CommandLineArgs split(std::wstring s, const std::wstring& delimiter)
{
cmdArgs.workspaceId = token;
}
}
}
}
return cmdArgs;

View File

@ -0,0 +1,409 @@
#include "pch.h"
#include "PwaHelper.h"
#include <ShlObj.h>
#include <tlhelp32.h>
#include <winternl.h>
#include <initguid.h>
#include <filesystem>
#include <wil/result_macros.h>
#include <common/logger/logger.h>
#include <common/utils/winapi_error.h>
#include <wil\com.h>
#pragma comment(lib, "ntdll.lib")
namespace SnapshotUtils
{
namespace NonLocalizable
{
const std::wstring EdgeAppIdIdentifier = L"--app-id=";
const std::wstring ChromeAppIdIdentifier = L"Chrome._crx_";
const std::wstring ChromeBase = L"Google\\Chrome\\User Data\\Default\\Web Applications";
const std::wstring EdgeBase = L"Microsoft\\Edge\\User Data\\Default\\Web Applications";
const std::wstring ChromeDirPrefix = L"_crx_";
const std::wstring EdgeDirPrefix = L"_crx__";
}
// {c8900b66-a973-584b-8cae-355b7f55341b}
DEFINE_GUID(CLSID_StartMenuCacheAndAppResolver, 0x660b90c8, 0x73a9, 0x4b58, 0x8c, 0xae, 0x35, 0x5b, 0x7f, 0x55, 0x34, 0x1b);
// {46a6eeff-908e-4dc6-92a6-64be9177b41c}
DEFINE_GUID(IID_IAppResolver_7, 0x46a6eeff, 0x908e, 0x4dc6, 0x92, 0xa6, 0x64, 0xbe, 0x91, 0x77, 0xb4, 0x1c);
// {de25675a-72de-44b4-9373-05170450c140}
DEFINE_GUID(IID_IAppResolver_8, 0xde25675a, 0x72de, 0x44b4, 0x93, 0x73, 0x05, 0x17, 0x04, 0x50, 0xc1, 0x40);
struct IAppResolver_7 : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetAppIDForShortcut() = 0;
virtual HRESULT STDMETHODCALLTYPE GetAppIDForWindow(HWND hWnd, WCHAR** pszAppId, void* pUnknown1, void* pUnknown2, void* pUnknown3) = 0;
virtual HRESULT STDMETHODCALLTYPE GetAppIDForProcess(DWORD dwProcessId, WCHAR** pszAppId, void* pUnknown1, void* pUnknown2, void* pUnknown3) = 0;
};
struct IAppResolver_8 : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetAppIDForShortcut() = 0;
virtual HRESULT STDMETHODCALLTYPE GetAppIDForShortcutObject() = 0;
virtual HRESULT STDMETHODCALLTYPE GetAppIDForWindow(HWND hWnd, WCHAR** pszAppId, void* pUnknown1, void* pUnknown2, void* pUnknown3) = 0;
virtual HRESULT STDMETHODCALLTYPE GetAppIDForProcess(DWORD dwProcessId, WCHAR** pszAppId, void* pUnknown1, void* pUnknown2, void* pUnknown3) = 0;
};
BOOL GetAppId_7(HWND hWnd, std::wstring* result)
{
HRESULT hr;
wil::com_ptr<IAppResolver_7> appResolver;
hr = CoCreateInstance(CLSID_StartMenuCacheAndAppResolver, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IAppResolver_7, reinterpret_cast<void**>(appResolver.put()));
if (SUCCEEDED(hr))
{
wil::unique_cotaskmem_string pszAppId;
hr = appResolver->GetAppIDForWindow(hWnd, &pszAppId, NULL, NULL, NULL);
if (SUCCEEDED(hr))
{
*result = std::wstring(pszAppId.get());
}
appResolver->Release();
}
return SUCCEEDED(hr);
}
BOOL GetAppId_8(HWND hWnd, std::wstring* result)
{
HRESULT hr;
*result = L"";
wil::com_ptr<IAppResolver_8> appResolver;
hr = CoCreateInstance(CLSID_StartMenuCacheAndAppResolver, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IAppResolver_8, reinterpret_cast<void**>(appResolver.put()));
if (SUCCEEDED(hr))
{
wil::unique_cotaskmem_string pszAppId;
hr = appResolver->GetAppIDForWindow(hWnd, &pszAppId, NULL, NULL, NULL);
if (SUCCEEDED(hr))
{
*result = std::wstring(pszAppId.get());
}
appResolver->Release();
}
return SUCCEEDED(hr);
}
BOOL PwaHelper::GetAppId(HWND hWnd, std::wstring* result)
{
HRESULT hr = GetAppId_8(hWnd, result);
if (!SUCCEEDED(hr))
{
hr = GetAppId_7(hWnd, result);
}
return SUCCEEDED(hr);
}
BOOL GetProcessId_7(DWORD dwProcessId, std::wstring* result)
{
HRESULT hr;
*result = L"";
wil::com_ptr<IAppResolver_7> appResolver;
hr = CoCreateInstance(CLSID_StartMenuCacheAndAppResolver, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IAppResolver_7, reinterpret_cast<void**>(appResolver.put()));
if (SUCCEEDED(hr))
{
wil::unique_cotaskmem_string pszAppId;
hr = appResolver->GetAppIDForProcess(dwProcessId, &pszAppId, NULL, NULL, NULL);
if (SUCCEEDED(hr))
{
*result = std::wstring(pszAppId.get());
}
appResolver->Release();
}
return SUCCEEDED(hr);
}
BOOL GetProcessId_8(DWORD dwProcessId, std::wstring* result)
{
HRESULT hr;
*result = L"";
wil::com_ptr<IAppResolver_8> appResolver;
hr = CoCreateInstance(CLSID_StartMenuCacheAndAppResolver, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IAppResolver_8, reinterpret_cast<void**>(appResolver.put()));
if (SUCCEEDED(hr))
{
wil::unique_cotaskmem_string pszAppId;
hr = appResolver->GetAppIDForProcess(dwProcessId, &pszAppId, NULL, NULL, NULL);
if (SUCCEEDED(hr))
{
*result = std::wstring(pszAppId.get());
}
appResolver->Release();
}
return SUCCEEDED(hr);
}
BOOL GetProcessId(DWORD dwProcessId, std::wstring* result)
{
HRESULT hr = GetProcessId_8(dwProcessId, result);
if (!SUCCEEDED(hr))
{
hr = GetProcessId_7(dwProcessId, result);
}
return SUCCEEDED(hr);
}
std::wstring GetProcCommandLine(DWORD pid)
{
std::wstring commandLine;
// Open a handle to the process
const HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (process == NULL)
{
Logger::error(L"Failed to open the process, error: {}", get_last_error_or_default(GetLastError()));
}
else
{
// Get the address of the ProcessEnvironmentBlock
PROCESS_BASIC_INFORMATION pbi = {};
NTSTATUS status = NtQueryInformationProcess(process, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
if (status != STATUS_SUCCESS)
{
Logger::error(L"Failed to query the process, error: {}", status);
}
else
{
// Get the address of the process parameters in the ProcessEnvironmentBlock
PEB processEnvironmentBlock = {};
if (!ReadProcessMemory(process, pbi.PebBaseAddress, &processEnvironmentBlock, sizeof(processEnvironmentBlock), NULL))
{
Logger::error(L"Failed to read the process ProcessEnvironmentBlock, error: {}", get_last_error_or_default(GetLastError()));
}
else
{
// Get the command line arguments from the process parameters
RTL_USER_PROCESS_PARAMETERS params = {};
if (!ReadProcessMemory(process, processEnvironmentBlock.ProcessParameters, &params, sizeof(params), NULL))
{
Logger::error(L"Failed to read the process params, error: {}", get_last_error_or_default(GetLastError()));
}
else
{
UNICODE_STRING& commandLineArgs = params.CommandLine;
std::vector<WCHAR> buffer(commandLineArgs.Length / sizeof(WCHAR));
if (!ReadProcessMemory(process, commandLineArgs.Buffer, buffer.data(), commandLineArgs.Length, NULL))
{
Logger::error(L"Failed to read the process command line, error: {}", get_last_error_or_default(GetLastError()));
}
else
{
commandLine.assign(buffer.data(), buffer.size());
}
}
}
}
CloseHandle(process);
}
return commandLine;
}
// Finds all PwaHelper.exe processes with the specified parent process ID
std::vector<DWORD> FindPwaHelperProcessIds()
{
std::vector<DWORD> pwaHelperProcessIds;
const HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
{
Logger::info(L"Invalid handle when creating snapshot for the search for PwaHelper processes");
return pwaHelperProcessIds;
}
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnapshot, &pe))
{
do
{
if (_wcsicmp(pe.szExeFile, L"PwaHelper.exe") == 0)
{
Logger::info(L"Found a PWA process with id {}", pe.th32ProcessID);
pwaHelperProcessIds.push_back(pe.th32ProcessID);
}
} while (Process32Next(hSnapshot, &pe));
}
CloseHandle(hSnapshot);
return pwaHelperProcessIds;
}
void PwaHelper::InitAumidToAppId()
{
if (pwaAumidToAppId.size() > 0)
{
return;
}
const auto pwaHelperProcessIds = FindPwaHelperProcessIds();
Logger::info(L"Found {} edge Pwa helper processes", pwaHelperProcessIds.size());
for (const auto subProcessID : pwaHelperProcessIds)
{
std::wstring aumidID;
GetProcessId(subProcessID, &aumidID);
std::wstring commandLineArg = GetProcCommandLine(subProcessID);
auto appIdIndexStart = commandLineArg.find(NonLocalizable::EdgeAppIdIdentifier);
if (appIdIndexStart != std::wstring::npos)
{
commandLineArg = commandLineArg.substr(appIdIndexStart + NonLocalizable::EdgeAppIdIdentifier.size());
auto appIdIndexEnd = commandLineArg.find(L" ");
if (appIdIndexEnd != std::wstring::npos)
{
commandLineArg = commandLineArg.substr(0, appIdIndexEnd);
}
}
std::wstring appId{ commandLineArg };
pwaAumidToAppId.insert(std::map<std::wstring, std::wstring>::value_type(aumidID, appId));
Logger::info(L"Found an edge Pwa helper process with AumidID {} and PwaAppId {}", aumidID, appId);
PWSTR path = NULL;
HRESULT hres = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &path);
if (SUCCEEDED(hres))
{
std::filesystem::path fsPath(path);
fsPath /= NonLocalizable::EdgeBase;
for (const auto& directory : std::filesystem::directory_iterator(fsPath))
{
if (directory.is_directory())
{
const std::filesystem::path directoryName = directory.path().filename();
if (directoryName.wstring().find(NonLocalizable::EdgeDirPrefix) == 0)
{
const std::wstring appIdDir = directoryName.wstring().substr(NonLocalizable::EdgeDirPrefix.size());
if (appIdDir == appId)
{
for (const auto& filename : std::filesystem::directory_iterator(directory))
{
if (!filename.is_directory())
{
const std::filesystem::path filenameString = filename.path().filename();
if (filenameString.extension().wstring() == L".ico")
{
pwaAppIdsToAppNames.insert(std::map<std::wstring, std::wstring>::value_type(appId, filenameString.stem().wstring()));
Logger::info(L"Storing an edge Pwa app name {} for PwaAppId {}", filenameString.stem().wstring(), appId);
}
}
}
}
}
}
}
CoTaskMemFree(path);
}
}
}
BOOL PwaHelper::GetPwaAppId(std::wstring windowAumid, std::wstring* result)
{
const auto pwaIndex = pwaAumidToAppId.find(windowAumid);
if (pwaIndex != pwaAumidToAppId.end())
{
*result = pwaIndex->second;
return true;
}
return false;
}
BOOL PwaHelper::SearchPwaName(std::wstring pwaAppId, std::wstring windowAumid, std::wstring* pwaName)
{
const auto index = pwaAppIdsToAppNames.find(pwaAppId);
if (index != pwaAppIdsToAppNames.end())
{
*pwaName = index->second;
return true;
}
std::wstring nameFromAumid{ windowAumid };
const std::size_t delimiterPos = nameFromAumid.find(L"-");
if (delimiterPos != std::string::npos)
{
nameFromAumid = nameFromAumid.substr(0, delimiterPos);
}
*pwaName = nameFromAumid;
return false;
}
void PwaHelper::InitChromeAppIds()
{
if (chromeAppIds.size() > 0)
{
return;
}
PWSTR path = NULL;
HRESULT hres = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &path);
if (SUCCEEDED(hres))
{
std::filesystem::path fsPath(path);
fsPath /= NonLocalizable::ChromeBase;
for (const auto& directory : std::filesystem::directory_iterator(fsPath))
{
if (directory.is_directory())
{
const std::filesystem::path directoryName = directory.path().filename();
if (directoryName.wstring().find(NonLocalizable::ChromeDirPrefix) == 0)
{
const std::wstring appId = directoryName.wstring().substr(NonLocalizable::ChromeDirPrefix.size());
chromeAppIds.push_back(appId);
for (const auto& filename : std::filesystem::directory_iterator(directory))
{
if (!filename.is_directory())
{
const std::filesystem::path filenameString = filename.path().filename();
if (filenameString.extension().wstring() == L".ico")
{
pwaAppIdsToAppNames.insert(std::map<std::wstring, std::wstring>::value_type(appId, filenameString.stem().wstring()));
Logger::info(L"Found an installed chrome Pwa app {} with PwaAppId {}", filenameString.stem().wstring(), appId);
}
}
}
}
}
}
CoTaskMemFree(path);
}
}
BOOL PwaHelper::SearchPwaAppId(std::wstring windowAumid, std::wstring* pwaAppId)
{
const auto appIdIndexStart = windowAumid.find(NonLocalizable::ChromeAppIdIdentifier);
if (appIdIndexStart != std::wstring::npos)
{
windowAumid = windowAumid.substr(appIdIndexStart + NonLocalizable::ChromeAppIdIdentifier.size());
const auto appIdIndexEnd = windowAumid.find(L" ");
if (appIdIndexEnd != std::wstring::npos)
{
windowAumid = windowAumid.substr(0, appIdIndexEnd);
}
const std::wstring windowAumidBegin = windowAumid.substr(0, 10);
for (const auto chromeAppId : chromeAppIds)
{
if (chromeAppId.find(windowAumidBegin) == 0)
{
*pwaAppId = chromeAppId;
return true;
}
}
}
*pwaAppId = L"";
return false;
}
}

View File

@ -0,0 +1,20 @@
#pragma once
namespace SnapshotUtils
{
class PwaHelper
{
public:
void InitAumidToAppId();
BOOL GetAppId(HWND hWnd, std::wstring* result);
BOOL GetPwaAppId(std::wstring windowAumid, std::wstring* result);
BOOL SearchPwaName(std::wstring pwaAppId, std::wstring windowAumid, std::wstring* pwaName);
void InitChromeAppIds();
BOOL SearchPwaAppId(std::wstring windowAumid, std::wstring* pwaAppId);
private:
std::map<std::wstring, std::wstring> pwaAumidToAppId;
std::vector<std::wstring> chromeAppIds;
std::map<std::wstring, std::wstring> pwaAppIdsToAppNames;
};
}

View File

@ -1,9 +1,6 @@
#include "pch.h"
#include "SnapshotUtils.h"
#include <comdef.h>
#include <Wbemidl.h>
#include <common/utils/elevation.h>
#include <common/utils/process_path.h>
#include <common/notifications/NotificationUtil.h>
@ -12,142 +9,17 @@
#include <workspaces-common/WindowFilter.h>
#include <WorkspacesLib/AppUtils.h>
#include <PwaHelper.h>
#pragma comment(lib, "ntdll.lib")
namespace SnapshotUtils
{
namespace NonLocalizable
{
const std::wstring ApplicationFrameHost = L"ApplicationFrameHost.exe";
}
class WbemHelper
{
public:
WbemHelper() = default;
~WbemHelper()
{
if (m_services)
{
m_services->Release();
}
if (m_locator)
{
m_locator->Release();
}
}
bool Initialize()
{
// Obtain the initial locator to WMI.
HRESULT hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast<LPVOID*>(&m_locator));
if (FAILED(hres))
{
Logger::error(L"Failed to create IWbemLocator object. Error: {}", get_last_error_or_default(hres));
return false;
}
// Connect to WMI through the IWbemLocator::ConnectServer method.
hres = m_locator->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &m_services);
if (FAILED(hres))
{
Logger::error(L"Could not connect to WMI. Error: {}", get_last_error_or_default(hres));
return false;
}
// Set security levels on the proxy.
hres = CoSetProxyBlanket(m_services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
if (FAILED(hres))
{
Logger::error(L"Could not set proxy blanket. Error: {}", get_last_error_or_default(hres));
return false;
}
return true;
}
std::wstring GetCommandLineArgs(DWORD processID) const
{
static std::wstring property = L"CommandLine";
std::wstring query = L"SELECT " + property + L" FROM Win32_Process WHERE ProcessId = " + std::to_wstring(processID);
return Query(query, property);
}
std::wstring GetExecutablePath(DWORD processID) const
{
static std::wstring property = L"ExecutablePath";
std::wstring query = L"SELECT " + property + L" FROM Win32_Process WHERE ProcessId = " + std::to_wstring(processID);
return Query(query, property);
}
private:
std::wstring Query(const std::wstring& query, const std::wstring& propertyName) const
{
if (!m_locator || !m_services)
{
return L"";
}
IEnumWbemClassObject* pEnumerator = NULL;
HRESULT hres = m_services->ExecQuery(bstr_t("WQL"), bstr_t(query.c_str()), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
if (FAILED(hres))
{
Logger::error(L"Query for process failed. Error: {}", get_last_error_or_default(hres));
return L"";
}
IWbemClassObject* pClassObject = NULL;
ULONG uReturn = 0;
std::wstring result = L"";
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pClassObject, &uReturn);
if (uReturn == 0)
{
break;
}
VARIANT vtProp;
hr = pClassObject->Get(propertyName.c_str(), 0, &vtProp, 0, 0);
if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR)
{
result = vtProp.bstrVal;
}
VariantClear(&vtProp);
pClassObject->Release();
}
pEnumerator->Release();
return result;
}
IWbemLocator* m_locator = NULL;
IWbemServices* m_services = NULL;
};
std::wstring GetCommandLineArgs(DWORD processID, const WbemHelper& wbemHelper)
{
std::wstring executablePath = wbemHelper.GetExecutablePath(processID);
std::wstring commandLineArgs = wbemHelper.GetCommandLineArgs(processID);
if (!commandLineArgs.empty())
{
auto pos = commandLineArgs.find(executablePath);
if (pos != std::wstring::npos)
{
commandLineArgs = commandLineArgs.substr(pos + executablePath.size());
auto spacePos = commandLineArgs.find_first_of(' ');
if (spacePos != std::wstring::npos)
{
commandLineArgs = commandLineArgs.substr(spacePos + 1);
}
}
}
return commandLineArgs;
const std::wstring EdgeFilename = L"msedge.exe";
const std::wstring ChromeFilename = L"chrome.exe";
}
bool IsProcessElevated(DWORD processID)
@ -168,16 +40,23 @@ namespace SnapshotUtils
return false;
}
bool IsEdge(Utils::Apps::AppData appData)
{
return appData.installPath.ends_with(NonLocalizable::EdgeFilename);
}
bool IsChrome(Utils::Apps::AppData appData)
{
return appData.installPath.ends_with(NonLocalizable::ChromeFilename);
}
std::vector<WorkspacesData::WorkspacesProject::Application> GetApps(const std::function<unsigned int(HWND)> getMonitorNumberFromWindowHandle, const std::function<WorkspacesData::WorkspacesProject::Monitor::MonitorRect(unsigned int)> getMonitorRect)
{
PwaHelper pwaHelper{};
std::vector<WorkspacesData::WorkspacesProject::Application> apps{};
auto installedApps = Utils::Apps::GetAppsList();
auto windows = WindowEnumerator::Enumerate(WindowFilter::Filter);
// for command line args detection
// WbemHelper wbemHelper;
// wbemHelper.Initialize();
for (const auto window : windows)
{
@ -232,7 +111,7 @@ namespace SnapshotUtils
if (pid != otherPid && title == WindowUtils::GetWindowTitle(otherWindow))
{
processPath = get_process_path(otherPid);
break;
break;
}
}
}
@ -249,6 +128,48 @@ namespace SnapshotUtils
continue;
}
std::wstring pwaAppId = L"";
std::wstring finalName = data.value().name;
std::wstring pwaName = L"";
if (IsEdge(data.value()))
{
pwaHelper.InitAumidToAppId();
std::wstring windowAumid;
pwaHelper.GetAppId(window, &windowAumid);
Logger::info(L"Found an edge window with aumid {}", windowAumid);
if (pwaHelper.GetPwaAppId(windowAumid, &pwaAppId))
{
Logger::info(L"The found edge window is a PWA app with appId {}", pwaAppId);
if (pwaHelper.SearchPwaName(pwaAppId, windowAumid ,& pwaName))
{
Logger::info(L"The found edge window is a PWA app with name {}", finalName);
}
finalName = pwaName + L" (" + finalName + L")";
}
else
{
Logger::info(L"The found edge window does not contain a PWA app", pwaAppId);
}
}
else if (IsChrome(data.value()))
{
pwaHelper.InitChromeAppIds();
std::wstring windowAumid;
pwaHelper.GetAppId(window, &windowAumid);
Logger::info(L"Found a chrome window with aumid {}", windowAumid);
if (pwaHelper.SearchPwaAppId(windowAumid, &pwaAppId))
{
if (pwaHelper.SearchPwaName(pwaAppId, windowAumid, &pwaName))
{
finalName = pwaName + L" (" + finalName + L")";
}
}
}
bool isMinimized = WindowUtils::IsMinimized(window);
unsigned int monitorNumber = getMonitorNumberFromWindowHandle(window);
@ -263,12 +184,13 @@ namespace SnapshotUtils
}
WorkspacesData::WorkspacesProject::Application app{
.name = data.value().name,
.name = finalName,
.title = title,
.path = data.value().installPath,
.packageFullName = data.value().packageFullName,
.appUserModelId = data.value().appUserModelId,
.commandLineArgs = L"", // GetCommandLineArgs(pid, wbemHelper),
.pwaAppId = pwaAppId,
.commandLineArgs = L"",
.isElevated = IsProcessElevated(pid),
.canLaunchElevated = data.value().canLaunchElevated,
.isMinimized = isMinimized,

View File

@ -104,7 +104,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>shcore.lib;Shell32.lib;propsys.lib;DbgHelp.lib;wbemuuid.lib</AdditionalDependencies>
<AdditionalDependencies>shcore.lib;Shell32.lib;propsys.lib;DbgHelp.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
@ -130,10 +130,12 @@
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="PwaHelper.cpp" />
<ClCompile Include="SnapshotUtils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="PwaHelper.h" />
<ClInclude Include="resource.base.h" />
<ClInclude Include="SnapshotUtils.h" />
</ItemGroup>

View File

@ -24,6 +24,9 @@
<ClInclude Include="resource.base.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PwaHelper.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@ -35,6 +38,9 @@
<ClCompile Include="SnapshotUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PwaHelper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@ -6,13 +6,14 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Threading;
namespace ColorPicker.Common
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "File name is correct, ignore generics")]
public sealed class RangeObservableCollection<T> : ObservableCollection<T>
{
private object _collectionChangedLock = new object();
private Lock _collectionChangedLock = new Lock();
private bool _suppressNotification;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)

View File

@ -325,7 +325,7 @@ namespace ColorPicker.Controls
private static string ColorToHex(Color color, string oldValue = "")
{
string newHexString = BitConverter.ToString(new byte[] { color.R, color.G, color.B }).Replace("-", string.Empty, StringComparison.InvariantCulture);
string newHexString = Convert.ToHexString(new byte[] { color.R, color.G, color.B });
newHexString = newHexString.ToLowerInvariant();
// Return only with hashtag if user typed it before

View File

@ -4,6 +4,7 @@
using System;
using System.ComponentModel.Composition;
using System.Threading;
using System.Windows;
using System.Windows.Interop;
@ -23,7 +24,7 @@ namespace ColorPicker.Helpers
private readonly IUserSettings _userSettings;
private ColorEditorWindow _colorEditorWindow;
private bool _colorPickerShown;
private object _colorPickerVisibilityLock = new object();
private Lock _colorPickerVisibilityLock = new Lock();
private HwndSource _hwndSource;
private const int _globalHotKeyId = 0x0001;

View File

@ -4,6 +4,7 @@
using System;
using System.ComponentModel.Composition;
using System.Threading;
using System.Windows.Threading;
namespace ColorPicker.Helpers
@ -11,7 +12,7 @@ namespace ColorPicker.Helpers
[Export(typeof(IThrottledActionInvoker))]
public sealed class ThrottledActionInvoker : IThrottledActionInvoker
{
private object _invokerLock = new object();
private Lock _invokerLock = new Lock();
private Action _actionToRun;
private DispatcherTimer _timer;

View File

@ -33,7 +33,7 @@ namespace ColorPicker.Settings
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Actually, call back is LoadSettingsFromJson")]
private readonly IFileSystemWatcher _watcher;
private readonly object _loadingSettingsLock = new object();
private readonly Lock _loadingSettingsLock = new Lock();
private bool _loadingColorsHistory;

View File

@ -31,7 +31,7 @@ namespace FancyZonesEditorCommon.Utils
{
try
{
using (Stream inputStream = _fileSystem.File.Open(fileName, FileMode.Open))
using (FileSystemStream inputStream = _fileSystem.File.Open(fileName, FileMode.Open))
using (StreamReader reader = new StreamReader(inputStream))
{
string data = reader.ReadToEnd();

View File

@ -27,7 +27,11 @@ namespace Microsoft.FancyZonesEditor.UITests.Utils
}
else
{
_fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(file));
var path = Path.GetDirectoryName(file);
if (path != null)
{
_fileSystem.Directory.CreateDirectory(path);
}
}
}

View File

@ -421,7 +421,7 @@ namespace ImageResizer.Properties
string jsonData = JsonSerializer.Serialize(new SettingsWrapper() { Properties = this }, _jsonSerializerOptions);
// Create directory if it doesn't exist
IFileInfo file = _fileSystem.FileInfo.FromFileName(SettingsPath);
IFileInfo file = _fileSystem.FileInfo.New(SettingsPath);
file.Directory.Create();
// write string to file

View File

@ -51,7 +51,7 @@ namespace Microsoft.Plugin.Folder.UnitTests
[DataRow(@"c:", 2, 1, false, DisplayName = "Root without \\")]
[DataRow(@"c:\", 2, 1, false, DisplayName = "Normal root")]
[DataRow(@"c:\Test", 2, 2, false, DisplayName = "Select yourself")]
[DataRow(@"c:\not-exist", 2, 1, false, DisplayName = "Folder not exist, return root")]
[DataRow(@"c:\not-exist", 2, 0, false, DisplayName = "Folder not exist, return root")]
[DataRow(@"c:\not-exist\not-exist2", 0, 0, false, DisplayName = "Folder not exist, return root")]
[DataRow(@"c:\bla.t", 2, 1, false, DisplayName = "Partial match file")]
[DataRow(@"c:/bla.t", 2, 1, false, DisplayName = "Partial match file with /")]
@ -88,8 +88,8 @@ namespace Microsoft.Plugin.Folder.UnitTests
[DataTestMethod]
[DataRow(@"c:\>", 3, 3, true, DisplayName = "Max Folder test recursive")]
[DataRow(@"c:\Test>", 3, 3, true, DisplayName = "2 Folders recursive")]
[DataRow(@"c:\not-exist>", 3, 3, true, DisplayName = "Folder not exist, return root recursive")]
[DataRow(@"c:\Test>", 3, 0, true, DisplayName = "2 Folders recursive")]
[DataRow(@"c:\not-exist>", 3, 0, true, DisplayName = "Folder not exist, return root recursive")]
[DataRow(@"c:\not-exist\not-exist2>", 0, 0, false, DisplayName = "Folder not exist, return root recursive")]
public void Query_Recursive_WhenCalled(string search, int folders, int files, bool truncated)
{

View File

@ -25,7 +25,7 @@ namespace Microsoft.Plugin.Folder.Sources
public IEnumerable<DisplayFileInfo> MatchFileSystemInfo(string search, string incompleteName, bool isRecursive)
{
// search folder and add results
var directoryInfo = _directoryInfoFactory.FromDirectoryName(search);
var directoryInfo = _directoryInfoFactory.New(search);
var fileSystemInfos = directoryInfo.EnumerateFileSystemInfos(incompleteName, new EnumerationOptions
{
MatchType = _matchType,

View File

@ -17,6 +17,15 @@
<ItemGroup>
<PackageReference Include="System.Data.OleDb" />
<PackageReference Include="System.Configuration.ConfigurationManager">
<ExcludeAssets>runtime</ExcludeAssets> <!-- Should already be present on .net sdk runtime, so we avoid the conflicting runtime version from nuget -->
</PackageReference>
<PackageReference Include="System.Diagnostics.EventLog">
<ExcludeAssets>runtime</ExcludeAssets> <!-- Should already be present on .net sdk runtime, so we avoid the conflicting runtime version from nuget -->
</PackageReference>
<PackageReference Include="System.Diagnostics.PerformanceCounter">
<ExcludeAssets>runtime</ExcludeAssets> <!-- Should already be present on .net sdk runtime, so we avoid the conflicting runtime version from nuget -->
</PackageReference>
</ItemGroup>
<ItemGroup>

View File

@ -22,7 +22,7 @@ namespace Microsoft.Plugin.Program
public string Location { get; set; }
public string Name { get => name ?? FileSystem.DirectoryInfo.FromDirectoryName(Location).Name; set => name = value; }
public string Name { get => name ?? FileSystem.DirectoryInfo.New(Location).Name; set => name = value; }
public bool Enabled { get; set; } = true;

View File

@ -21,7 +21,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
/// <summary>
/// Used to enforce single execution of EnumWindows
/// </summary>
private static readonly object _enumWindowsLock = new();
private static readonly Lock _enumWindowsLock = new();
/// <summary>
/// PowerLauncher main executable

View File

@ -39,6 +39,15 @@
<PackageReference Include="System.Drawing.Common">
<ExcludeAssets>runtime</ExcludeAssets> <!-- Should already be present on .net sdk runtime, so we avoid the conflicting runtime version from nuget -->
</PackageReference>
<PackageReference Include="System.Configuration.ConfigurationManager">
<ExcludeAssets>runtime</ExcludeAssets> <!-- Should already be present on .net sdk runtime, so we avoid the conflicting runtime version from nuget -->
</PackageReference>
<PackageReference Include="System.Diagnostics.EventLog">
<ExcludeAssets>runtime</ExcludeAssets> <!-- Should already be present on .net sdk runtime, so we avoid the conflicting runtime version from nuget -->
</PackageReference>
<PackageReference Include="System.Diagnostics.PerformanceCounter">
<ExcludeAssets>runtime</ExcludeAssets> <!-- Should already be present on .net sdk runtime, so we avoid the conflicting runtime version from nuget -->
</PackageReference>
</ItemGroup>
<ItemGroup>

View File

@ -22,7 +22,7 @@ namespace Microsoft.PowerToys.Run.Plugin.PowerToys
private const int MaxNumberOfRetry = 5;
private readonly List<Utility> _utilities;
private readonly FileSystemWatcher _watcher;
private readonly object _loadingSettingsLock = new();
private readonly Lock _loadingSettingsLock = new();
private bool _disposed;
public UtilityProvider()

View File

@ -20,6 +20,9 @@
<ItemGroup>
<PackageReference Include="System.ServiceProcess.ServiceController" />
<PackageReference Include="System.Diagnostics.EventLog">
<ExcludeAssets>runtime</ExcludeAssets> <!-- Should already be present on .net sdk runtime, so we avoid the conflicting runtime version from nuget -->
</PackageReference>
</ItemGroup>
<ItemGroup>

View File

@ -49,7 +49,7 @@ namespace PowerLauncher
private ETWTrace etwTrace = new ETWTrace();
// To prevent two disposals running at the same time.
private static readonly object _disposingLock = new object();
private static readonly Lock _disposingLock = new Lock();
[STAThread]
public static void Main()

View File

@ -11,6 +11,7 @@ using System.IO.Abstractions;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@ -29,7 +30,7 @@ namespace PowerLauncher.Plugin
{
private static readonly IFileSystem FileSystem = new FileSystem();
private static readonly IDirectory Directory = FileSystem.Directory;
private static readonly object AllPluginsLock = new object();
private static readonly Lock AllPluginsLock = new Lock();
private static readonly CompositeFormat FailedToInitializePluginsDescription = System.Text.CompositeFormat.Parse(Properties.Resources.FailedToInitializePluginsDescription);

View File

@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
<TargetFramework>net9.0-windows10.0.22621.0</TargetFramework>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<PublishDir>$(PowerToysRoot)\$(Platform)\$(Configuration)</PublishDir>

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