[New PowerToy] File Locksmith (#20930)

* Imported offline solution

* Make solution compile

* Add Windows sample, doesn't work?

* Added new project to implement the dll

* Remove unneeded header

* Implemented IUnknown part of ExplorerCommand

* Implemented IExplorerCommand methods

* Implemented ClassFactory

* Implemented DLL register/unregister

* Implemented other DLL exports, not working?

* Implemented IShellExtInit inferface

* Implemented IContextMenu, it works!

* Implement command data fetching

* Make sample project compile on VS 2022

* Add plan

* Implement Lib as separate project

* Implemented IPC, not tested

* Console UI project skeleton

* Implemented basic console UI

* Implemented piping, there are bugs

* Prototype works

* Remove old project

* Added GUI project skeleton

* Mitigate issue with WinUI3

* Added a control for displaying results

* Add button

* Implement core functions in lib project

* Call new library function from console main

* Implement showing results

* Improve UI

* Implemented subdirectory search

* Remove useless code

* Set window size

* UI adjustments

* Implement killing process

* Rename variable

* Add lib project to main solution

* Add Ext and GUI projects to solution

* Tweak packages for GUI project

* Add a settings page

* Add a few resource strings

* Add one more resources string

* VS keeps trying to correct this

* Add references to File Locksmith in /,github

* Implement some parts of FileLocksmithModule

* Change output directory

* Change target name and add to runner

* Add logger

* Started implementing settings backend

* Fix log folder

* Settings work

* Add some basic tracing

* Attempt at adding resources

* Remove junk files

* Added missing defines

* Replaced some constants with resources

Something's not working

* Move resources to the Ext project

* Remove experiment

* Add binaries for signing

* Improve tracing

* Remove old Settings calls

* Show something when there are no results

* Change window title

* Move computation to another thread, improve UX

* Increase font size for default text

* Remove entries for killed processes

* Show user name

* Remove nonrecursive implementation

* Implement back end for getting file names

* Show list of files, UI tweaks

* Remove useless includes

* Implement back end for getting full process path

* Dark title bar on dark themes

* Using Expander, other UI adjustments

* Show "No results" after killing all processes

* Show progress ring

* Update configuration mapping

* Revert "Update configuration mapping"

This reverts commit d8e13206f3c7de3c6dbf880299bfff3bf9f27a37.

* Fixed solution configuration, ARM64 should build

* Backend for refreshing

* Variable window size

* Add refresh button

* New WinUI3 C# project for FL

* Started porting functionality

* Add Interop project

* Move IPC to Ext project

* Ported native functions to Interop

* Ported finding processes

* Ported most of Main Window functionality

* Display paths of files

* Implement killing processes

* Use resource string for "End Task"

* Remove entries for terminated processes

* Show User name

* Set default window size

* Make the new UI the default

* Reading paths from stdin, completed port to C#

* Fix small bug

* Moving to MVVM

* Adding Labs

* Merge branch 'ivan/file-locksmith' of https://github.com/microsoft/PowerToys into ivan/file-locksmith

Removing one parent commit for cleaner history

Co-Authored-By: Niels Laute <niels.laute@live.nl>

* Reintroducing features

* Moving UI strings to resources file

* Restored functionality

* Add missing dlls

* Add FIle Locksmith to publish.cmd

* Rebase fixes

* Try updating nuget.config

* Fix copy-paste blunder

* Add File Locksmith UI for publishing

* Add .pubxml file in FileLocksmith

* Change build output folder

* Fix installer build issues

Remove old projects from solution so MSBuild doesn't build them.
Downgrade target framework to what most other projects are using.
Fix publishing profile and project runtimes.
Remove unused CsWinRT references.

* [CI] Add clear to nuget packages

* Fix module reference counting

* Fix nuget for release CI

* Fix version and signing

* Fix path for resources

* Fix incorrect results when running 2 instances

* Fix default nuget source

* Windows 10 icon and fallback for UI

* Code clean-up and spaces instead of tabs

* Add gif showcasing FL

* Add screenshot of File Locksmith for Settings

* Add new files to the installer

* Add OOBE page

* Showing selected paths in the header

* Tweak path list

* Added new, wider gif

* Add GPO

* Add some logs

* [CI]Get CommunityToolkit.Labs from BigPark feed

* [CI]Use azure package feed for Nuget in release

* [CI]Another try for the labs source

* Revert changes to feed

* Use RestoreAdditionalProjectSources

* Add tooltip to file list

* Change tooltip to not trim the lines

* Add Tips and tricks section mentioning elevated

* Add some more logs messages.

* Grammar fix

* Add to bug report tool

* Fix UI virtualization not working

* Disable virtualization to avoid crashes

* Get better virtualization

* Add dialog instead of tooltip to show list of items

* No results refresh icon is now a button too

* Use managed methods for handling processes

* Remove registry code from Ext.

* Support drives too

Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
This commit is contained in:
ivanstosic-janea
2022-10-28 15:51:21 +02:00
committed by GitHub
parent 429569dcf3
commit db191b8b75
88 changed files with 4550 additions and 100 deletions

View File

@@ -52,6 +52,7 @@ body:
- ColorPicker - ColorPicker
- FancyZones - FancyZones
- FancyZones Editor - FancyZones Editor
- File Locksmith
- File Explorer: Preview Pane - File Explorer: Preview Pane
- File Explorer: Thumbnail preview - File Explorer: Thumbnail preview
- Image Resizer - Image Resizer

View File

@@ -25,7 +25,8 @@ body:
- Awake - Awake
- ColorPicker - ColorPicker
- FancyZones - FancyZones
- FancyZones Editor - FancyZones Editor
- File Locksmith
- File Explorer: Preview Pane - File Explorer: Preview Pane
- File Explorer: Thumbnail preview - File Explorer: Thumbnail preview
- Image Resizer - Image Resizer

View File

@@ -71,6 +71,11 @@
"modules\\Hosts\\PowerToys.Hosts.dll", "modules\\Hosts\\PowerToys.Hosts.dll",
"modules\\Hosts\\PowerToys.Hosts.exe", "modules\\Hosts\\PowerToys.Hosts.exe",
"modules\\FileLocksmith\\PowerToys.FileLocksmithLib.Interop.dll",
"modules\\FileLocksmith\\PowerToys.FileLocksmithExt.dll",
"modules\\FileLocksmith\\PowerToys.FileLocksmithUI.exe",
"modules\\FileLocksmith\\PowerToys.FileLocksmithUI.dll",
"modules\\ImageResizer\\PowerToys.ImageResizer.exe", "modules\\ImageResizer\\PowerToys.ImageResizer.exe",
"modules\\ImageResizer\\PowerToys.ImageResizer.dll", "modules\\ImageResizer\\PowerToys.ImageResizer.dll",
"modules\\ImageResizer\\PowerToys.ImageResizerExt.dll", "modules\\ImageResizer\\PowerToys.ImageResizerExt.dll",
@@ -207,6 +212,7 @@
"vcomp140_app.dll", "vcomp140_app.dll",
"vcruntime140_1_app.dll", "vcruntime140_1_app.dll",
"vcruntime140_app.dll", "vcruntime140_app.dll",
"modules\\FileLocksmith\\CommunityToolkit.Labs.WinUI.SettingsControls.dll",
"modules\\PowerAccent\\Vanara.Core.dll", "modules\\PowerAccent\\Vanara.Core.dll",
"modules\\PowerAccent\\Vanara.PInvoke.ComCtl32.dll", "modules\\PowerAccent\\Vanara.PInvoke.ComCtl32.dll",
"modules\\PowerAccent\\Vanara.PInvoke.Cryptography.dll", "modules\\PowerAccent\\Vanara.PInvoke.Cryptography.dll",

View File

@@ -2,7 +2,7 @@
<configuration> <configuration>
<packageSources> <packageSources>
<clear /> <clear />
<add key="PowerToysCDPX_Feed" value="https://github-private.pkgs.visualstudio.com/microsoft/_packaging/PowerToysCDPX_Feed/nuget/v3/index.json" /> <add key="PowerToysCDPX_Feed" value="https://github-private.pkgs.visualstudio.com/microsoft/_packaging/PowerToysCDPX_Feed/nuget/v3/index.json" />
</packageSources> </packageSources>
<disabledPackageSources> <disabledPackageSources>
<clear /> <clear />

View File

@@ -295,6 +295,22 @@ jobs:
configuration: $(BuildConfiguration) configuration: $(BuildConfiguration)
maximumCpuCount: true maximumCpuCount: true
- task: VSBuild@1
displayName: Publish File Locksmith UI for Packaging
inputs:
solution: 'src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj'
vsVersion: 17.0
# The arguments should be the same as the ones for Settings; make sure they are.
msbuildArgs: >-
/target:Publish
/p:Configuration=$(BuildConfiguration);Platform=$(BuildPlatform);AppxBundle=Never
/p:VCRTForwarders-IncludeDebugCRT=false
/p:PowerToysRoot=$(Build.SourcesDirectory)
/p:PublishProfile=InstallationPublishProfile.pubxml
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
maximumCpuCount: true
- task: VSBuild@1 - task: VSBuild@1
displayName: Build PowerToysSetupCustomActions DLL # This dll needs to be build and signed before building the MSI. displayName: Build PowerToysSetupCustomActions DLL # This dll needs to be build and signed before building the MSI.
inputs: inputs:

View File

@@ -458,6 +458,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hosts.Tests", "src\modules\
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HostsModuleInterface", "src\modules\Hosts\HostsModuleInterface\HostsModuleInterface.vcxproj", "{B41B888C-7DB8-4747-B262-4062E05A230D}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HostsModuleInterface", "src\modules\Hosts\HostsModuleInterface\HostsModuleInterface.vcxproj", "{B41B888C-7DB8-4747-B262-4062E05A230D}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FileLocksmith", "FileLocksmith", "{AB82E5DD-C32D-4F28-9746-2C780846188E}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileLocksmithExt", "src\modules\FileLocksmith\FileLocksmithExt\FileLocksmithExt.vcxproj", "{57175EC7-92A5-4C1E-8244-E3FBCA2A81DE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileLocksmithUI", "src\modules\FileLocksmith\FileLocksmithUI\FileLocksmithUI.csproj", "{E69B044A-2F8A-45AA-AD0B-256C59421807}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileLocksmithLibInterop", "src\modules\FileLocksmith\FileLocksmithLibInterop\FileLocksmithLibInterop.vcxproj", "{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPOWrapper", "src\common\GPOWrapper\GPOWrapper.vcxproj", "{E599C30B-9DC8-4E5A-BF27-93D4CCEDE788}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPOWrapper", "src\common\GPOWrapper\GPOWrapper.vcxproj", "{E599C30B-9DC8-4E5A-BF27-93D4CCEDE788}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GPOWrapperProjection", "src\common\GPOWrapperProjection\GPOWrapperProjection.csproj", "{00EE9BA6-4E8F-43CA-960D-D4882F0FBB97}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GPOWrapperProjection", "src\common\GPOWrapperProjection\GPOWrapperProjection.csproj", "{00EE9BA6-4E8F-43CA-960D-D4882F0FBB97}"
@@ -1547,8 +1555,8 @@ Global
{04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|ARM64.Build.0 = Release|ARM64 {04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|ARM64.Build.0 = Release|ARM64
{04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x64.ActiveCfg = Release|x64 {04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x64.ActiveCfg = Release|x64
{04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x64.Build.0 = Release|x64 {04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x64.Build.0 = Release|x64
{04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x86.ActiveCfg = Release|Any CPU {04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x86.ActiveCfg = Release|x64
{04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x86.Build.0 = Release|Any CPU {04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x86.Build.0 = Release|x64
{F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|ARM64.ActiveCfg = Debug|ARM64 {F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|ARM64.ActiveCfg = Debug|ARM64
{F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|ARM64.Build.0 = Debug|ARM64 {F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|ARM64.Build.0 = Debug|ARM64
{F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|x64.ActiveCfg = Debug|x64 {F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|x64.ActiveCfg = Debug|x64
@@ -1853,6 +1861,36 @@ Global
{B41B888C-7DB8-4747-B262-4062E05A230D}.Release|x64.Build.0 = Release|x64 {B41B888C-7DB8-4747-B262-4062E05A230D}.Release|x64.Build.0 = Release|x64
{B41B888C-7DB8-4747-B262-4062E05A230D}.Release|x86.ActiveCfg = Release|x64 {B41B888C-7DB8-4747-B262-4062E05A230D}.Release|x86.ActiveCfg = Release|x64
{B41B888C-7DB8-4747-B262-4062E05A230D}.Release|x86.Build.0 = Release|x64 {B41B888C-7DB8-4747-B262-4062E05A230D}.Release|x86.Build.0 = Release|x64
{57175EC7-92A5-4C1E-8244-E3FBCA2A81DE}.Debug|ARM64.ActiveCfg = Debug|ARM64
{57175EC7-92A5-4C1E-8244-E3FBCA2A81DE}.Debug|ARM64.Build.0 = Debug|ARM64
{57175EC7-92A5-4C1E-8244-E3FBCA2A81DE}.Debug|x64.ActiveCfg = Debug|x64
{57175EC7-92A5-4C1E-8244-E3FBCA2A81DE}.Debug|x64.Build.0 = Debug|x64
{57175EC7-92A5-4C1E-8244-E3FBCA2A81DE}.Debug|x86.ActiveCfg = Debug|x64
{57175EC7-92A5-4C1E-8244-E3FBCA2A81DE}.Release|ARM64.ActiveCfg = Release|ARM64
{57175EC7-92A5-4C1E-8244-E3FBCA2A81DE}.Release|ARM64.Build.0 = Release|ARM64
{57175EC7-92A5-4C1E-8244-E3FBCA2A81DE}.Release|x64.ActiveCfg = Release|x64
{57175EC7-92A5-4C1E-8244-E3FBCA2A81DE}.Release|x64.Build.0 = Release|x64
{57175EC7-92A5-4C1E-8244-E3FBCA2A81DE}.Release|x86.ActiveCfg = Release|x64
{E69B044A-2F8A-45AA-AD0B-256C59421807}.Debug|ARM64.ActiveCfg = Debug|arm64
{E69B044A-2F8A-45AA-AD0B-256C59421807}.Debug|ARM64.Build.0 = Debug|arm64
{E69B044A-2F8A-45AA-AD0B-256C59421807}.Debug|x64.ActiveCfg = Debug|x64
{E69B044A-2F8A-45AA-AD0B-256C59421807}.Debug|x64.Build.0 = Debug|x64
{E69B044A-2F8A-45AA-AD0B-256C59421807}.Debug|x86.ActiveCfg = Debug|x64
{E69B044A-2F8A-45AA-AD0B-256C59421807}.Release|ARM64.ActiveCfg = Release|arm64
{E69B044A-2F8A-45AA-AD0B-256C59421807}.Release|ARM64.Build.0 = Release|arm64
{E69B044A-2F8A-45AA-AD0B-256C59421807}.Release|x64.ActiveCfg = Release|x64
{E69B044A-2F8A-45AA-AD0B-256C59421807}.Release|x64.Build.0 = Release|x64
{E69B044A-2F8A-45AA-AD0B-256C59421807}.Release|x86.ActiveCfg = Release|x64
{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}.Debug|ARM64.Build.0 = Debug|ARM64
{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}.Debug|x64.ActiveCfg = Debug|x64
{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}.Debug|x64.Build.0 = Debug|x64
{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}.Debug|x86.ActiveCfg = Debug|x64
{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}.Release|ARM64.ActiveCfg = Release|ARM64
{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}.Release|ARM64.Build.0 = Release|ARM64
{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}.Release|x64.ActiveCfg = Release|x64
{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}.Release|x64.Build.0 = Release|x64
{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}.Release|x86.ActiveCfg = Release|x64
{E599C30B-9DC8-4E5A-BF27-93D4CCEDE788}.Debug|ARM64.ActiveCfg = Debug|ARM64 {E599C30B-9DC8-4E5A-BF27-93D4CCEDE788}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E599C30B-9DC8-4E5A-BF27-93D4CCEDE788}.Debug|ARM64.Build.0 = Debug|ARM64 {E599C30B-9DC8-4E5A-BF27-93D4CCEDE788}.Debug|ARM64.Build.0 = Debug|ARM64
{E599C30B-9DC8-4E5A-BF27-93D4CCEDE788}.Debug|x64.ActiveCfg = Debug|x64 {E599C30B-9DC8-4E5A-BF27-93D4CCEDE788}.Debug|x64.ActiveCfg = Debug|x64
@@ -2033,6 +2071,10 @@ Global
{F05E590D-AD46-42BE-9C25-6A63ADD2E3EA} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA} {E2D03E0F-7A75-4813-9F4B-D8763D43FD3A} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
{B41B888C-7DB8-4747-B262-4062E05A230D} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA} {B41B888C-7DB8-4747-B262-4062E05A230D} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
{AB82E5DD-C32D-4F28-9746-2C780846188E} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{57175EC7-92A5-4C1E-8244-E3FBCA2A81DE} = {AB82E5DD-C32D-4F28-9746-2C780846188E}
{E69B044A-2F8A-45AA-AD0B-256C59421807} = {AB82E5DD-C32D-4F28-9746-2C780846188E}
{C604B37E-9D0E-4484-8778-E8B31B0E1B3A} = {AB82E5DD-C32D-4F28-9746-2C780846188E}
{E599C30B-9DC8-4E5A-BF27-93D4CCEDE788} = {1AFB6476-670D-4E80-A464-657E01DFF482} {E599C30B-9DC8-4E5A-BF27-93D4CCEDE788} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{00EE9BA6-4E8F-43CA-960D-D4882F0FBB97} = {1AFB6476-670D-4E80-A464-657E01DFF482} {00EE9BA6-4E8F-43CA-960D-D4882F0FBB97} = {1AFB6476-670D-4E80-A464-657E01DFF482}
EndGlobalSection EndGlobalSection

View File

@@ -57,6 +57,16 @@
<decimal value="0" /> <decimal value="0" />
</disabledValue> </disabledValue>
</policy> </policy>
<policy name="ConfigureEnabledUtilityFileLocksmith" class="Both" displayName="$(string.ConfigureEnabledUtilityFileLocksmith)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityFileLocksmith">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_64_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityFileExplorerSVGPreview" class="Both" displayName="$(string.ConfigureEnabledUtilityFileExplorerSVGPreview)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityFileExplorerSVGPreview"> <policy name="ConfigureEnabledUtilityFileExplorerSVGPreview" class="Both" displayName="$(string.ConfigureEnabledUtilityFileExplorerSVGPreview)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityFileExplorerSVGPreview">
<parentCategory ref="PowerToys" /> <parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_64_0" /> <supportedOn ref="SUPPORTED_POWERTOYS_0_64_0" />

View File

@@ -32,6 +32,7 @@ If you don't configure this setting, users are able to disable or enable the uti
<string id="ConfigureEnabledUtilityAwake">Awake: Configure enabled state</string> <string id="ConfigureEnabledUtilityAwake">Awake: Configure enabled state</string>
<string id="ConfigureEnabledUtilityColorPicker">Color Picker: Configure enabled state</string> <string id="ConfigureEnabledUtilityColorPicker">Color Picker: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFancyZones">FancyZones: Configure enabled state</string> <string id="ConfigureEnabledUtilityFancyZones">FancyZones: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileLocksmith">File Locksmith: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerSVGPreview">SVG file preview: Configure enabled state</string> <string id="ConfigureEnabledUtilityFileExplorerSVGPreview">SVG file preview: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerMarkdownPreview">Markdown file preview: Configure enabled state</string> <string id="ConfigureEnabledUtilityFileExplorerMarkdownPreview">Markdown file preview: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerMonacoPreview">Source code file preview: Configure enabled state</string> <string id="ConfigureEnabledUtilityFileExplorerMonacoPreview">Source code file preview: Configure enabled state</string>

View File

@@ -8,6 +8,7 @@
<?define KeyboardManagerProjectName="KeyboardManager"?> <?define KeyboardManagerProjectName="KeyboardManager"?>
<?define PowerAccentProjectName="PowerAccent"?> <?define PowerAccentProjectName="PowerAccent"?>
<?define PowerRenameProjectName="PowerRename"?> <?define PowerRenameProjectName="PowerRename"?>
<?define FileLocksmithProjectName="FileLocksmith"?>
<?define ColorPickerProjectName="ColorPicker"?> <?define ColorPickerProjectName="ColorPicker"?>
<?define PowerOCRProjectName="PowerOCR"?> <?define PowerOCRProjectName="PowerOCR"?>
<?define VideoConferenceProjectName="VideoConference"?> <?define VideoConferenceProjectName="VideoConference"?>
@@ -52,11 +53,11 @@
<?define SettingsV2Files=backup_restore_settings.json;Ijwhost.dll;ColorCode.Core.dll;ColorCode.WinUI.dll;CommunityToolkit.Common.dll;CommunityToolkit.WinUI.dll;CommunityToolkit.WinUI.UI.Controls.Core.dll;CommunityToolkit.WinUI.UI.Controls.DataGrid.dll;CommunityToolkit.WinUI.UI.Controls.Input.dll;CommunityToolkit.WinUI.UI.Controls.Layout.dll;CommunityToolkit.WinUI.UI.Controls.Markdown.dll;CommunityToolkit.WinUI.UI.Controls.Media.dll;CommunityToolkit.WinUI.UI.Controls.Primitives.dll;CommunityToolkit.WinUI.UI.dll;icon.ico;Microsoft.Graphics.Canvas.Interop.dll;Microsoft.InteractiveExperiences.Projection.dll;Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll;Microsoft.Windows.ApplicationModel.Resources.Projection.dll;Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll;Microsoft.Windows.AppLifecycle.Projection.dll;Microsoft.Windows.SDK.NET.dll;Microsoft.Windows.System.Power.Projection.dll;Microsoft.WindowsAppRuntime.Bootstrap.Net.dll;Microsoft.WinUI.dll;Microsoft.Xaml.Interactions.dll;Microsoft.Xaml.Interactivity.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.Settings.deps.json;PowerToys.Settings.dll;PowerToys.Settings.exe;PowerToys.Settings.runtimeconfig.json;PowerToys.Settings.UI.Lib.dll;resources.pri;System.CodeDom.dll;System.IO.Abstractions.dll;System.Text.Json.dll;WinRT.Runtime.dll;Microsoft.Graphics.Canvas.dll;System.Management.dll;PowerToys.GPOWrapper.dll?> <?define SettingsV2Files=backup_restore_settings.json;Ijwhost.dll;ColorCode.Core.dll;ColorCode.WinUI.dll;CommunityToolkit.Common.dll;CommunityToolkit.WinUI.dll;CommunityToolkit.WinUI.UI.Controls.Core.dll;CommunityToolkit.WinUI.UI.Controls.DataGrid.dll;CommunityToolkit.WinUI.UI.Controls.Input.dll;CommunityToolkit.WinUI.UI.Controls.Layout.dll;CommunityToolkit.WinUI.UI.Controls.Markdown.dll;CommunityToolkit.WinUI.UI.Controls.Media.dll;CommunityToolkit.WinUI.UI.Controls.Primitives.dll;CommunityToolkit.WinUI.UI.dll;icon.ico;Microsoft.Graphics.Canvas.Interop.dll;Microsoft.InteractiveExperiences.Projection.dll;Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll;Microsoft.Windows.ApplicationModel.Resources.Projection.dll;Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll;Microsoft.Windows.AppLifecycle.Projection.dll;Microsoft.Windows.SDK.NET.dll;Microsoft.Windows.System.Power.Projection.dll;Microsoft.WindowsAppRuntime.Bootstrap.Net.dll;Microsoft.WinUI.dll;Microsoft.Xaml.Interactions.dll;Microsoft.Xaml.Interactivity.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.Settings.deps.json;PowerToys.Settings.dll;PowerToys.Settings.exe;PowerToys.Settings.runtimeconfig.json;PowerToys.Settings.UI.Lib.dll;resources.pri;System.CodeDom.dll;System.IO.Abstractions.dll;System.Text.Json.dll;WinRT.Runtime.dll;Microsoft.Graphics.Canvas.dll;System.Management.dll;PowerToys.GPOWrapper.dll?>
<?define SettingsV2AssetsModulesFiles=ColorPicker.png;FancyZones.png;AlwaysOnTop.png;HostsFileEditor.png;Awake.png;ImageResizer.png;KBM.png;MouseUtils.png;PowerAccent.png;PowerOCR.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ScreenRuler.png;ShortcutGuide.png;VideoConference.png?> <?define SettingsV2AssetsModulesFiles=ColorPicker.png;FancyZones.png;FileLocksmith.png;AlwaysOnTop.png;HostsFileEditor.png;Awake.png;ImageResizer.png;KBM.png;MouseUtils.png;PowerAccent.png;PowerOCR.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ScreenRuler.png;ShortcutGuide.png;VideoConference.png?>
<?define SettingsV2OOBEAssetsModulesFiles=ColorPicker.gif;AlwaysOnTop.png;HostsFileEditor.png;Awake.png;FancyZones.gif;FileExplorer.png;ImageResizer.gif;KBM.gif;MouseUtils.gif;PowerAccent.gif;PowerOCR.gif;PowerRename.gif;Run.gif;ScreenRuler.gif;OOBEShortcutGuide.png;VideoConferenceMute.png;OOBEPTHero.png?> <?define SettingsV2OOBEAssetsModulesFiles=ColorPicker.gif;AlwaysOnTop.png;HostsFileEditor.png;Awake.png;FancyZones.gif;FileExplorer.png;FileLocksmith.gif;ImageResizer.gif;KBM.gif;MouseUtils.gif;PowerAccent.gif;PowerOCR.gif;PowerRename.gif;Run.gif;ScreenRuler.gif;OOBEShortcutGuide.png;VideoConferenceMute.png;OOBEPTHero.png?>
<?define SettingsV2OOBEAssetsFluentIconsFiles=ColorPicker.png;FancyZones.png;AlwaysOnTop.png;Awake.png;FileExplorerPreview.png;FindMyMouse.png;Hosts.png;ImageResizer.png;KeyboardManager.png;MouseHighlighter.png;MouseCrosshairs.png;MouseUtils.png;PowerAccent.png;PowerOcr.png;PowerRename.png;PowerToys.png;PowerToysRun.png;ScreenRuler.png;Settings.png;ShortcutGuide.png;VideoConferenceMute.png?> <?define SettingsV2OOBEAssetsFluentIconsFiles=ColorPicker.png;FancyZones.png;FileLocksmith.png;AlwaysOnTop.png;Awake.png;FileExplorerPreview.png;FindMyMouse.png;Hosts.png;ImageResizer.png;KeyboardManager.png;MouseHighlighter.png;MouseCrosshairs.png;MouseUtils.png;PowerAccent.png;PowerOcr.png;PowerRename.png;PowerToys.png;PowerToysRun.png;ScreenRuler.png;Settings.png;ShortcutGuide.png;VideoConferenceMute.png?>
<?define SettingsV2MicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?> <?define SettingsV2MicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
@@ -116,6 +117,8 @@
<?define PowerRenameFiles=PowerRenameUI.ico;PowerToys.PowerRename.exe;resources.pri;PowerToys.PowerRenameExt.dll;PowerRenameContextMenuPackage.msix;PowerToys.PowerRenameContextMenu.dll?> <?define PowerRenameFiles=PowerRenameUI.ico;PowerToys.PowerRename.exe;resources.pri;PowerToys.PowerRenameExt.dll;PowerRenameContextMenuPackage.msix;PowerToys.PowerRenameContextMenu.dll?>
<?define FileLocksmithFiles=icon.ico;Microsoft.Win32.SystemEvents.dll;Microsoft.Xaml.Interactions.dll;Microsoft.Xaml.Interactivity.dll;CommunityToolkit.WinUI.dll;CommunityToolkit.WinUI.UI.dll;CommunityToolkit.Common.dll;CommunityToolkit.Mvvm.dll;CommunityToolkit.Labs.WinUI.SettingsControls.dll;Ijwhost.dll;Microsoft.InteractiveExperiences.Projection.dll;Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll;Microsoft.Windows.ApplicationModel.Resources.Projection.dll;Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll;Microsoft.Windows.AppLifecycle.Projection.dll;Microsoft.Windows.SDK.NET.dll;Microsoft.Windows.System.Power.Projection.dll;Microsoft.WindowsAppRuntime.Bootstrap.Net.dll;Microsoft.WinUI.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.FileLocksmithExt.dll;PowerToys.FileLocksmithLib.Interop.dll;PowerToys.FileLocksmithUI.exe;PowerToys.FileLocksmithUI.dll;PowerToys.FileLocksmithUI.deps.json;PowerToys.FileLocksmithUI.runtimeconfig.json;System.CodeDom.dll;System.Drawing.Common.dll;System.Management.dll;WinRT.Runtime.dll;WinUIEx.dll;resources.pri;PowerToys.GPOWrapper.dll?>
<?define PowerRenameSparsePackageAssets=LargeTile.png;SmallTile.png;SplashScreen.png;Square150x150Logo.png;Square44x44Logo.png;storelogo.png;Wide310x150Logo.png?> <?define PowerRenameSparsePackageAssets=LargeTile.png;SmallTile.png;SplashScreen.png;Square150x150Logo.png;Square44x44Logo.png;storelogo.png;Wide310x150Logo.png?>
<?define ImageResizerSparsePackageAssets=LargeTile.png;SmallTile.png;SplashScreen.png;Square150x150Logo.png;Square44x44Logo.png;storelogo.png;Wide310x150Logo.png?> <?define ImageResizerSparsePackageAssets=LargeTile.png;SmallTile.png;SplashScreen.png;Square150x150Logo.png;Square44x44Logo.png;storelogo.png;Wide310x150Logo.png?>
@@ -498,6 +501,8 @@
</Directory> </Directory>
<Directory Id="PowerAccentInstallFolder" Name="$(var.PowerAccentProjectName)"> <Directory Id="PowerAccentInstallFolder" Name="$(var.PowerAccentProjectName)">
</Directory> </Directory>
<Directory Id="FileLocksmithInstallFolder" Name="$(var.FileLocksmithProjectName)">
</Directory>
<Directory Id="PowerRenameInstallFolder" Name="$(var.PowerRenameProjectName)"> <Directory Id="PowerRenameInstallFolder" Name="$(var.PowerRenameProjectName)">
<Directory Id="PowerRenameAssetsFolder" Name="Assets" /> <Directory Id="PowerRenameAssetsFolder" Name="Assets" />
<Directory Id="PowerRenameMicrosoftUIXamlInstallFolder" Name="Microsoft.UI.Xaml"> <Directory Id="PowerRenameMicrosoftUIXamlInstallFolder" Name="Microsoft.UI.Xaml">
@@ -877,6 +882,30 @@
<?endforeach?> <?endforeach?>
</DirectoryRef> </DirectoryRef>
<!-- FileLocksmith -->
<DirectoryRef Id="FileLocksmithInstallFolder" FileSource="$(var.BinDir)modules\$(var.FileLocksmithProjectName)">
<?foreach File in $(var.FileLocksmithFiles)?>
<Component Id="FileLocksmith_$(var.File)" Win64="yes">
<File Id="FileLocksmithFile_$(var.File)" Source="$(var.BinDir)modules\$(var.FileLocksmithProjectName)\$(var.File)" />
</Component>
<?endforeach?>
<!-- !Warning! Make sure to change Component Guid if you update something here -->
<Component Id="Module_FileLocksmith" Guid="108D3EC1-E6E0-4E81-88EF-25966133CB41" Win64="yes">
<RegistryKey Root="HKLM" Key="Software\Classes\CLSID\{84D68575-E186-46AD-B0CB-BAEB45EE29C0}">
<RegistryValue Type="string" Value="File Locksmith Shell Extension" />
<RegistryValue Type="string" Name="ContextMenuOptIn" Value="" />
<RegistryValue Type="string" Key="InprocServer32" Value="[FileLocksmithInstallFolder]PowerToys.FileLocksmithExt.dll" />
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Apartment" />
</RegistryKey>
<RegistryKey Root="HKLM" Key="Software\Classes\AllFileSystemObjects\ShellEx\ContextMenuHandlers\FileLocksmithExt">
<RegistryValue Type="string" Value="{84D68575-E186-46AD-B0CB-BAEB45EE29C0}"/>
</RegistryKey>
<RegistryKey Root="HKLM" Key="Software\Classes\Drive\ShellEx\ContextMenuHandlers\FileLocksmithExt">
<RegistryValue Type="string" Value="{84D68575-E186-46AD-B0CB-BAEB45EE29C0}"/>
</RegistryKey>
</Component>
</DirectoryRef>
<!-- PowerRename --> <!-- PowerRename -->
<DirectoryRef Id="PowerRenameInstallFolder" FileSource="$(var.BinDir)modules\$(var.PowerRenameProjectName)"> <DirectoryRef Id="PowerRenameInstallFolder" FileSource="$(var.BinDir)modules\$(var.PowerRenameProjectName)">
<?foreach File in $(var.PowerRenameFiles)?> <?foreach File in $(var.PowerRenameFiles)?>
@@ -1241,6 +1270,10 @@
<?endforeach?> <?endforeach?>
<ComponentRef Id="DesktopShortcut" /> <ComponentRef Id="DesktopShortcut" />
<ComponentRef Id="Module_PowerRename" /> <ComponentRef Id="Module_PowerRename" />
<ComponentRef Id="Module_FileLocksmith" />
<?foreach File in $(var.FileLocksmithFiles)?>
<ComponentRef Id="FileLocksmith_$(var.File)" />
<?endforeach?>
<?foreach File in $(var.PowerAccentFiles)?> <?foreach File in $(var.PowerAccentFiles)?>
<ComponentRef Id="PowerAccent_$(var.File)" /> <ComponentRef Id="PowerAccent_$(var.File)" />
<?endforeach?> <?endforeach?>
@@ -1649,7 +1682,7 @@
<!-- Localization languages shipped with WinAppSDK. We should ship these as well. --> <!-- Localization languages shipped with WinAppSDK. We should ship these as well. -->
<?define WinAppSDKLocLanguageList = af-ZA;ar-SA;az-Latn-AZ;bg-BG;bs-Latn-BA;ca-ES;cs-CZ;cy-GB;da-DK;de-DE;el-GR;en-GB;en-us;es-ES;es-MX;et-EE;eu-ES;fa-IR;fi-FI;fr-CA;fr-FR;gl-ES;he-IL;hi-IN;hr-HR;hu-HU;id-ID;is-IS;it-IT;ja-JP;ka-GE;kk-KZ;ko-KR;lt-LT;lv-LV;ms-MY;nb-NO;nl-NL;nn-NO;pl-PL;pt-BR;pt-PT;ro-RO;ru-RU;sk-SK;sl-SI;sq-AL;sr-Cyrl-RS;sr-Latn-RS;sv-SE;th-TH;tr-TR;uk-UA;vi-VN;zh-CN;zh-TW?> <?define WinAppSDKLocLanguageList = af-ZA;ar-SA;az-Latn-AZ;bg-BG;bs-Latn-BA;ca-ES;cs-CZ;cy-GB;da-DK;de-DE;el-GR;en-GB;en-us;es-ES;es-MX;et-EE;eu-ES;fa-IR;fi-FI;fr-CA;fr-FR;gl-ES;he-IL;hi-IN;hr-HR;hu-HU;id-ID;is-IS;it-IT;ja-JP;ka-GE;kk-KZ;ko-KR;lt-LT;lv-LV;ms-MY;nb-NO;nl-NL;nn-NO;pl-PL;pt-BR;pt-PT;ro-RO;ru-RU;sk-SK;sl-SI;sq-AL;sr-Cyrl-RS;sr-Latn-RS;sv-SE;th-TH;tr-TR;uk-UA;vi-VN;zh-CN;zh-TW?>
<Fragment> <Fragment>
<?foreach ParentDirectory in SettingsV2InstallFolder;PowerRenameInstallFolder;MeasureToolInstallFolder;HostsInstallFolder?> <?foreach ParentDirectory in SettingsV2InstallFolder;PowerRenameInstallFolder;MeasureToolInstallFolder;HostsInstallFolder;FileLocksmithInstallFolder?>
<DirectoryRef Id="$(var.ParentDirectory)"> <DirectoryRef Id="$(var.ParentDirectory)">
<?foreach Language in $(var.WinAppSDKLocLanguageList)?> <?foreach Language in $(var.WinAppSDKLocLanguageList)?>
<?if $(var.Language) = af-ZA?> <?if $(var.Language) = af-ZA?>
@@ -1975,9 +2008,16 @@
<File Id="Hosts_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlMui_File" Source="$(var.BinDir)modules\$(var.HostsProjectName)\$(var.Language)\Microsoft.ui.xaml.dll.mui" /> <File Id="Hosts_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlMui_File" Source="$(var.BinDir)modules\$(var.HostsProjectName)\$(var.Language)\Microsoft.ui.xaml.dll.mui" />
<File Id="Hosts_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlPhoneMui_File" Source="$(var.BinDir)modules\$(var.HostsProjectName)\$(var.Language)\Microsoft.UI.Xaml.Phone.dll.mui" /> <File Id="Hosts_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlPhoneMui_File" Source="$(var.BinDir)modules\$(var.HostsProjectName)\$(var.Language)\Microsoft.UI.Xaml.Phone.dll.mui" />
</Component> </Component>
<?undef IdSafeLanguage?> <Component
<?undef CompGUIDPrefix?> Id="FileLocksmith_WinAppSDKLoc_$(var.IdSafeLanguage)_Component"
<?endforeach?> Directory="WinAppSDKLoc$(var.IdSafeLanguage)FileLocksmithInstallFolder"
Guid="$(var.CompGUIDPrefix)05">
<File Id="FileLocksmith_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlMui_File" Source="$(var.BinDir)modules\$(var.FileLocksmithProjectName)\$(var.Language)\Microsoft.ui.xaml.dll.mui" />
<File Id="FileLocksmith_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlPhoneMui_File" Source="$(var.BinDir)modules\$(var.FileLocksmithProjectName)\$(var.Language)\Microsoft.UI.Xaml.Phone.dll.mui" />
</Component>
<?undef IdSafeLanguage?>
<?undef CompGUIDPrefix?>
<?endforeach?>
</ComponentGroup> </ComponentGroup>
</Fragment> </Fragment>
<Fragment> <Fragment>

View File

@@ -20,3 +20,5 @@ msbuild !PTRoot!\src\modules\previewpane\SvgPreviewHandler\SvgPreviewHandler.csp
msbuild !PTRoot!\src\modules\previewpane\SvgThumbnailProvider\SvgThumbnailProvider.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml msbuild !PTRoot!\src\modules\previewpane\SvgThumbnailProvider\SvgThumbnailProvider.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml
msbuild !PTRoot!\src\modules\MeasureTool\MeasureToolUI\MeasureToolUI.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml msbuild !PTRoot!\src\modules\MeasureTool\MeasureToolUI\MeasureToolUI.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml
msbuild !PTRoot!\src\modules\FileLocksmith\FileLocksmithUI\FileLocksmithUI.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml

View File

@@ -1045,11 +1045,20 @@ UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
return WcaFinalize(er); return WcaFinalize(er);
} }
const std::wstring WinAppSDKConsumers[] =
{
L"Settings",
L"modules\\PowerRename",
L"modules\\MeasureTool",
L"modules\\FileLocksmith",
L"modules\\Hosts",
};
UINT __stdcall CreateWinAppSDKHardlinksCA(MSIHANDLE hInstall) UINT __stdcall CreateWinAppSDKHardlinksCA(MSIHANDLE hInstall)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
std::wstring installationFolder, winAppSDKFilesSrcDir, settingsDir, powerRenameDir, measureToolDir, hostsFileEditorDir; std::wstring installationFolder, winAppSDKFilesSrcDir;
hr = WcaInitialize(hInstall, "CreateWinAppSDKHardlinksCA"); hr = WcaInitialize(hInstall, "CreateWinAppSDKHardlinksCA");
ExitOnFailure(hr, "Failed to initialize"); ExitOnFailure(hr, "Failed to initialize");
@@ -1058,25 +1067,21 @@ UINT __stdcall CreateWinAppSDKHardlinksCA(MSIHANDLE hInstall)
ExitOnFailure(hr, "Failed to get installation folder"); ExitOnFailure(hr, "Failed to get installation folder");
winAppSDKFilesSrcDir = installationFolder + L"dll\\WinAppSDK\\"; winAppSDKFilesSrcDir = installationFolder + L"dll\\WinAppSDK\\";
hostsFileEditorDir = installationFolder + L"modules\\Hosts\\";
settingsDir = installationFolder + L"Settings\\";
powerRenameDir = installationFolder + L"modules\\PowerRename\\";
measureToolDir = installationFolder + L"modules\\MeasureTool\\";
for (auto file : winAppSdkFiles) for (auto file : winAppSdkFiles)
{ {
std::error_code ec; for (auto consumer : WinAppSDKConsumers)
std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (hostsFileEditorDir + file).c_str(), ec);
std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (settingsDir + file).c_str(), ec);
std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (powerRenameDir + file).c_str(), ec);
std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (measureToolDir + file).c_str(), ec);
if (ec.value() != S_OK)
{ {
std::wstring errorMessage{ L"Error creating hard link for: " }; std::error_code ec;
errorMessage += file; std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (installationFolder + consumer + L"\\" + file).c_str(), ec);
errorMessage += L", error code: " + std::to_wstring(ec.value());
Logger::error(errorMessage); if (ec.value() != S_OK)
{
std::wstring errorMessage{ L"Error creating hard link for: " };
errorMessage += file;
errorMessage += L", error code: " + std::to_wstring(ec.value());
Logger::error(errorMessage);
}
} }
} }
@@ -1085,12 +1090,26 @@ LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
const std::wstring PTInteropConsumers[] =
{
L"modules\\ColorPicker",
L"modules\\PowerOCR",
L"modules\\launcher",
L"modules\\FancyZones",
L"modules\\ImageResizer",
L"Settings",
L"modules\\Awake",
L"modules\\MeasureTool",
L"modules\\PowerAccent",
L"modules\\FileLocksmith",
L"modules\\Hosts",
};
UINT __stdcall CreatePTInteropHardlinksCA(MSIHANDLE hInstall) UINT __stdcall CreatePTInteropHardlinksCA(MSIHANDLE hInstall)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
std::wstring installationFolder, interopFilesSrcDir, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir, std::wstring installationFolder, interopFilesSrcDir;
imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, hostsFileEditorDir;
hr = WcaInitialize(hInstall, "CreatePTInteropHardlinksCA"); hr = WcaInitialize(hInstall, "CreatePTInteropHardlinksCA");
ExitOnFailure(hr, "Failed to initialize"); ExitOnFailure(hr, "Failed to initialize");
@@ -1099,37 +1118,21 @@ UINT __stdcall CreatePTInteropHardlinksCA(MSIHANDLE hInstall)
ExitOnFailure(hr, "Failed to get installation folder"); ExitOnFailure(hr, "Failed to get installation folder");
interopFilesSrcDir = installationFolder + L"dll\\Interop\\"; interopFilesSrcDir = installationFolder + L"dll\\Interop\\";
colorPickerDir = installationFolder + L"modules\\ColorPicker\\";
powerOCRDir = installationFolder + L"modules\\PowerOCR\\";
launcherDir = installationFolder + L"modules\\launcher\\";
fancyZonesDir = installationFolder + L"modules\\FancyZones\\";
hostsFileEditorDir = installationFolder + L"modules\\Hosts\\";
imageResizerDir = installationFolder + L"modules\\ImageResizer\\";
settingsDir = installationFolder + L"Settings\\";
awakeDir = installationFolder + L"modules\\Awake\\";
measureToolDir = installationFolder + L"modules\\MeasureTool\\";
powerAccentDir = installationFolder + L"modules\\PowerAccent\\";
for (auto file : powerToysInteropFiles) for (auto file : powerToysInteropFiles)
{ {
std::error_code ec; for (auto consumer : PTInteropConsumers)
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (colorPickerDir + file).c_str(), ec);
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (powerOCRDir + file).c_str(), ec);
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (launcherDir + file).c_str(), ec);
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (fancyZonesDir + file).c_str(), ec);
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (hostsFileEditorDir + file).c_str(), ec);
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (imageResizerDir + file).c_str(), ec);
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (settingsDir + file).c_str(), ec);
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (awakeDir + file).c_str(), ec);
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (measureToolDir + file).c_str(), ec);
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (powerAccentDir + file).c_str(), ec);
if (ec.value() != S_OK)
{ {
std::wstring errorMessage{ L"Error creating hard link for: " }; std::error_code ec;
errorMessage += file; std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (installationFolder + consumer + L"\\" + file).c_str(), ec);
errorMessage += L", error code: " + std::to_wstring(ec.value());
Logger::error(errorMessage); if (ec.value() != S_OK)
{
std::wstring errorMessage{ L"Error creating hard link for: " };
errorMessage += file;
errorMessage += L", error code: " + std::to_wstring(ec.value());
Logger::error(errorMessage);
}
} }
} }
@@ -1142,7 +1145,7 @@ UINT __stdcall DeleteWinAppSDKHardlinksCA(MSIHANDLE hInstall)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
std::wstring installationFolder, settingsDir, powerRenameDir, measureToolDir, hostsFileEditorDir; std::wstring installationFolder;
hr = WcaInitialize(hInstall, "DeleteWinAppSDKHardlinksCA"); hr = WcaInitialize(hInstall, "DeleteWinAppSDKHardlinksCA");
ExitOnFailure(hr, "Failed to initialize"); ExitOnFailure(hr, "Failed to initialize");
@@ -1150,19 +1153,14 @@ UINT __stdcall DeleteWinAppSDKHardlinksCA(MSIHANDLE hInstall)
hr = getInstallFolder(hInstall, installationFolder); hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installation folder"); ExitOnFailure(hr, "Failed to get installation folder");
hostsFileEditorDir = installationFolder + L"modules\\Hosts\\";
settingsDir = installationFolder + L"Settings\\";
powerRenameDir = installationFolder + L"modules\\PowerRename\\";
measureToolDir = installationFolder + L"modules\\MeasureTool\\";
try try
{ {
for (auto file : winAppSdkFiles) for (auto file : winAppSdkFiles)
{ {
DeleteFile((hostsFileEditorDir + file).c_str()); for (auto consumer : WinAppSDKConsumers)
DeleteFile((settingsDir + file).c_str()); {
DeleteFile((powerRenameDir + file).c_str()); DeleteFile((installationFolder + consumer + L"\\" + file).c_str());
DeleteFile((measureToolDir + file).c_str()); }
} }
} }
catch (std::exception e) catch (std::exception e)
@@ -1183,8 +1181,7 @@ UINT __stdcall DeletePTInteropHardlinksCA(MSIHANDLE hInstall)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
std::wstring installationFolder, interopFilesSrcDir, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir, std::wstring installationFolder, interopFilesSrcDir;
imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, hostsFileEditorDir;
hr = WcaInitialize(hInstall, "DeletePTInteropHardlinksCA"); hr = WcaInitialize(hInstall, "DeletePTInteropHardlinksCA");
ExitOnFailure(hr, "Failed to initialize"); ExitOnFailure(hr, "Failed to initialize");
@@ -1192,31 +1189,14 @@ UINT __stdcall DeletePTInteropHardlinksCA(MSIHANDLE hInstall)
hr = getInstallFolder(hInstall, installationFolder); hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installation folder"); ExitOnFailure(hr, "Failed to get installation folder");
colorPickerDir = installationFolder + L"modules\\ColorPicker\\";
powerOCRDir = installationFolder + L"modules\\PowerOCR\\";
launcherDir = installationFolder + L"modules\\launcher\\";
fancyZonesDir = installationFolder + L"modules\\FancyZones\\";
hostsFileEditorDir = installationFolder + L"modules\\Hosts\\";
imageResizerDir = installationFolder + L"modules\\ImageResizer\\";
settingsDir = installationFolder + L"Settings\\";
awakeDir = installationFolder + L"modules\\Awake\\";
measureToolDir = installationFolder + L"modules\\MeasureTool\\";
powerAccentDir = installationFolder + L"modules\\PowerAccent\\";
try try
{ {
for (auto file : powerToysInteropFiles) for (auto file : powerToysInteropFiles)
{ {
DeleteFile((colorPickerDir + file).c_str()); for (auto consumer : PTInteropConsumers)
DeleteFile((powerOCRDir + file).c_str()); {
DeleteFile((launcherDir + file).c_str()); DeleteFile((installationFolder + consumer + L"\\" + file).c_str());
DeleteFile((fancyZonesDir + file).c_str()); }
DeleteFile((hostsFileEditorDir + file).c_str());
DeleteFile((imageResizerDir + file).c_str());
DeleteFile((settingsDir + file).c_str());
DeleteFile((awakeDir + file).c_str());
DeleteFile((measureToolDir + file).c_str());
DeleteFile((powerAccentDir + file).c_str());
} }
} }
catch (std::exception e) catch (std::exception e)
@@ -1256,6 +1236,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
L"PowerToys.Awake.exe", L"PowerToys.Awake.exe",
L"PowerToys.FancyZones.exe", L"PowerToys.FancyZones.exe",
L"PowerToys.FancyZonesEditor.exe", L"PowerToys.FancyZonesEditor.exe",
L"PowerToys.FileLocksmithUI.exe",
L"PowerToys.ColorPickerUI.exe", L"PowerToys.ColorPickerUI.exe",
L"PowerToys.AlwaysOnTop.exe", L"PowerToys.AlwaysOnTop.exe",
L"PowerToys.exe" L"PowerToys.exe"

View File

@@ -1,5 +0,0 @@
<configuration>
<config>
<clear />
</config>
</configuration>

View File

@@ -20,6 +20,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{ {
return (GpoRuleConfigured)powertoys_gpo::getConfiguredFancyZonesEnabledValue(); return (GpoRuleConfigured)powertoys_gpo::getConfiguredFancyZonesEnabledValue();
} }
GpoRuleConfigured GPOWrapper::GetConfiguredFileLocksmithEnabledValue()
{
return (GpoRuleConfigured)powertoys_gpo::getConfiguredFileLocksmithEnabledValue();
}
GpoRuleConfigured GPOWrapper::GetConfiguredSvgPreviewEnabledValue() GpoRuleConfigured GPOWrapper::GetConfiguredSvgPreviewEnabledValue()
{ {
return (GpoRuleConfigured)powertoys_gpo::getConfiguredSvgPreviewEnabledValue(); return (GpoRuleConfigured)powertoys_gpo::getConfiguredSvgPreviewEnabledValue();

View File

@@ -11,6 +11,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetConfiguredAwakeEnabledValue(); static GpoRuleConfigured GetConfiguredAwakeEnabledValue();
static GpoRuleConfigured GetConfiguredColorPickerEnabledValue(); static GpoRuleConfigured GetConfiguredColorPickerEnabledValue();
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue(); static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue();
static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue();
static GpoRuleConfigured GetConfiguredMarkdownPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredMarkdownPreviewEnabledValue();
static GpoRuleConfigured GetConfiguredMonacoPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredMonacoPreviewEnabledValue();

View File

@@ -15,6 +15,7 @@ namespace PowerToys
static GpoRuleConfigured GetConfiguredAwakeEnabledValue(); static GpoRuleConfigured GetConfiguredAwakeEnabledValue();
static GpoRuleConfigured GetConfiguredColorPickerEnabledValue(); static GpoRuleConfigured GetConfiguredColorPickerEnabledValue();
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue(); static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue();
static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue();
static GpoRuleConfigured GetConfiguredMarkdownPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredMarkdownPreviewEnabledValue();
static GpoRuleConfigured GetConfiguredMonacoPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredMonacoPreviewEnabledValue();

View File

@@ -34,6 +34,7 @@ struct LogSettings
inline const static std::string powerRenameLoggerName = "powerrename"; inline const static std::string powerRenameLoggerName = "powerrename";
inline const static std::string alwaysOnTopLoggerName = "always-on-top"; inline const static std::string alwaysOnTopLoggerName = "always-on-top";
inline const static std::string powerOcrLoggerName = "TextExtractor"; inline const static std::string powerOcrLoggerName = "TextExtractor";
inline const static std::string fileLocksmithLoggerName = "FileLocksmith";
inline const static std::wstring alwaysOnTopLogPath = L"always-on-top-log.txt"; inline const static std::wstring alwaysOnTopLogPath = L"always-on-top-log.txt";
inline const static std::string hostsLoggerName = "hosts"; inline const static std::string hostsLoggerName = "hosts";
inline const static std::wstring hostsLogPath = L"Logs\\hosts-log.txt"; inline const static std::wstring hostsLogPath = L"Logs\\hosts-log.txt";

View File

@@ -23,6 +23,7 @@ namespace powertoys_gpo {
const std::wstring POLICY_CONFIGURE_ENABLED_AWAKE = L"ConfigureEnabledUtilityAwake"; const std::wstring POLICY_CONFIGURE_ENABLED_AWAKE = L"ConfigureEnabledUtilityAwake";
const std::wstring POLICY_CONFIGURE_ENABLED_COLOR_PICKER = L"ConfigureEnabledUtilityColorPicker"; const std::wstring POLICY_CONFIGURE_ENABLED_COLOR_PICKER = L"ConfigureEnabledUtilityColorPicker";
const std::wstring POLICY_CONFIGURE_ENABLED_FANCYZONES = L"ConfigureEnabledUtilityFancyZones"; const std::wstring POLICY_CONFIGURE_ENABLED_FANCYZONES = L"ConfigureEnabledUtilityFancyZones";
const std::wstring POLICY_CONFIGURE_ENABLED_FILE_LOCKSMITH = L"ConfigureEnabledUtilityFileLocksmith";
const std::wstring POLICY_CONFIGURE_ENABLED_SVG_PREVIEW = L"ConfigureEnabledUtilityFileExplorerSVGPreview"; const std::wstring POLICY_CONFIGURE_ENABLED_SVG_PREVIEW = L"ConfigureEnabledUtilityFileExplorerSVGPreview";
const std::wstring POLICY_CONFIGURE_ENABLED_MARKDOWN_PREVIEW = L"ConfigureEnabledUtilityFileExplorerMarkdownPreview"; const std::wstring POLICY_CONFIGURE_ENABLED_MARKDOWN_PREVIEW = L"ConfigureEnabledUtilityFileExplorerMarkdownPreview";
const std::wstring POLICY_CONFIGURE_ENABLED_MONACO_PREVIEW = L"ConfigureEnabledUtilityFileExplorerMonacoPreview"; const std::wstring POLICY_CONFIGURE_ENABLED_MONACO_PREVIEW = L"ConfigureEnabledUtilityFileExplorerMonacoPreview";
@@ -119,6 +120,11 @@ namespace powertoys_gpo {
return getConfiguredValue(POLICY_CONFIGURE_ENABLED_FANCYZONES); return getConfiguredValue(POLICY_CONFIGURE_ENABLED_FANCYZONES);
} }
inline gpo_rule_configured_t getConfiguredFileLocksmithEnabledValue()
{
return getConfiguredValue(POLICY_CONFIGURE_ENABLED_FILE_LOCKSMITH);
}
inline gpo_rule_configured_t getConfiguredSvgPreviewEnabledValue() inline gpo_rule_configured_t getConfiguredSvgPreviewEnabledValue()
{ {
return getConfiguredValue(POLICY_CONFIGURE_ENABLED_SVG_PREVIEW); return getConfiguredValue(POLICY_CONFIGURE_ENABLED_SVG_PREVIEW);

View File

@@ -0,0 +1,80 @@
#include "pch.h"
#include "ClassFactory.h"
#include "ExplorerCommand.h"
#include "dllmain.h"
// Class ctor/dtors
ClassFactory::ClassFactory(_In_ REFCLSID clsid) :
m_ref_count(1),
m_clsid(clsid)
{
++globals::ref_count;
}
ClassFactory::~ClassFactory()
{
--globals::ref_count;
}
// Implementations of inherited IUnknown methods
IFACEMETHODIMP ClassFactory::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(ClassFactory, IClassFactory),
{ 0, 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) ClassFactory::AddRef()
{
return ++m_ref_count;
}
IFACEMETHODIMP_(ULONG) ClassFactory::Release()
{
auto result = --m_ref_count;
if (result == 0)
{
delete this;
}
return result;
}
// Implementations of inherited IClassFactory methods
IFACEMETHODIMP ClassFactory::CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvObject)
{
*ppvObject = NULL;
HRESULT hr;
if (pUnkOuter)
{
hr = CLASS_E_NOAGGREGATION;
}
else if (m_clsid == __uuidof(ExplorerCommand))
{
hr = ExplorerCommand::s_CreateInstance(pUnkOuter, riid, ppvObject);
}
else
{
hr = CLASS_E_CLASSNOTAVAILABLE;
}
return hr;
}
IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock)
{
if (fLock)
{
++globals::ref_count;
}
else
{
--globals::ref_count;
}
return S_OK;
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "pch.h"
class ClassFactory : public IClassFactory
{
public:
ClassFactory(_In_ REFCLSID clsid);
~ClassFactory();
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
IFACEMETHODIMP_(ULONG) AddRef() override;
IFACEMETHODIMP_(ULONG) Release() override;
// IClassFactory
IFACEMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvObject) override;
IFACEMETHODIMP LockServer(BOOL fLock) override;
private:
std::atomic<ULONG> m_ref_count;
IID m_clsid;
};

View File

@@ -0,0 +1,33 @@
#pragma once
#include "pch.h"
// Non-localizable constants
namespace constants::nonlocalizable
{
// Description of the registry key
constexpr WCHAR RegistryKeyDescription[] = L"File Locksmith Shell Extension";
// File name of the UI executable
constexpr WCHAR FileNameUIExe[] = L"PowerToys.FileLocksmithUI.exe";
// String key used by PowerToys
constexpr WCHAR PowerToyKey[] = L"File Locksmith";
// Nonlocalized name of this PowerToy, for logs, etc
constexpr WCHAR PowerToyName[] = L"File Locksmith";
// JSON key used to store whether the module is enabled
constexpr WCHAR JsonKeyEnabled[] = L"Enabled";
// Path of the JSON file used to store settings
constexpr WCHAR DataFilePath[] = L"\\file-locksmith-settings.json";
// Name of the file where the list of files to checked will be stored
constexpr WCHAR LastRunPath[] = L"\\last-run.log";
}
// Macros, non-localizable
// Description of the registry key
#define REGISTRY_CONTEXT_MENU_KEY L"FileLocksmithExt"

View File

@@ -0,0 +1,296 @@
#include "pch.h"
#include "ExplorerCommand.h"
#include "Constants.h"
#include "Settings.h"
#include "dllmain.h"
#include "Trace.h"
#include "Generated Files/resource.h"
// Implementations of inherited IUnknown methods
IFACEMETHODIMP ExplorerCommand::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(ExplorerCommand, IExplorerCommand),
QITABENT(ExplorerCommand, IShellExtInit),
QITABENT(ExplorerCommand, IContextMenu),
{ 0, 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) ExplorerCommand::AddRef()
{
return ++m_ref_count;
}
IFACEMETHODIMP_(ULONG) ExplorerCommand::Release()
{
auto result = --m_ref_count;
if (result == 0)
{
delete this;
}
return result;
}
// Implementations of inherited IExplorerCommand methods
IFACEMETHODIMP ExplorerCommand::GetTitle(IShellItemArray* psiItemArray, LPWSTR* ppszName)
{
WCHAR buffer[128];
LoadStringW(globals::instance, IDS_FILELOCKSMITH_COMMANDTITLE, buffer, ARRAYSIZE(buffer));
return SHStrDupW(buffer, ppszName);
}
IFACEMETHODIMP ExplorerCommand::GetIcon(IShellItemArray* psiItemArray, LPWSTR* ppszIcon)
{
// Path to the icon should be computed relative to the path of this module
ppszIcon = NULL;
return E_NOTIMPL;
}
IFACEMETHODIMP ExplorerCommand::GetToolTip(IShellItemArray* psiItemArray, LPWSTR* ppszInfotip)
{
// No tooltip for now
return E_NOTIMPL;
}
IFACEMETHODIMP ExplorerCommand::GetCanonicalName(GUID* pguidCommandName)
{
*pguidCommandName = __uuidof(this);
return S_OK;
}
IFACEMETHODIMP ExplorerCommand::GetState(IShellItemArray* psiItemArray, BOOL fOkToBeSlow, EXPCMDSTATE* pCmdState)
{
if (globals::enabled)
{
*pCmdState = ECS_ENABLED;
}
else
{
*pCmdState = ECS_HIDDEN;
}
return S_OK;
}
IFACEMETHODIMP ExplorerCommand::Invoke(IShellItemArray* psiItemArray, IBindCtx* pbc)
{
return S_OK;
}
IFACEMETHODIMP ExplorerCommand::GetFlags(EXPCMDFLAGS* pFlags)
{
*pFlags = ECF_DEFAULT;
return S_OK;
}
IFACEMETHODIMP ExplorerCommand::EnumSubCommands(IEnumExplorerCommand** ppEnum)
{
*ppEnum = NULL;
return E_NOTIMPL;
}
// Implementations of inherited IShellExtInit methods
IFACEMETHODIMP ExplorerCommand::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID)
{
m_data_obj = pdtobj;
m_data_obj->AddRef();
return S_OK;
}
// Implementations of inherited IContextMenu methods
IFACEMETHODIMP ExplorerCommand::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
// Skip if disabled
if (!FileLocksmithSettingsInstance().GetEnabled())
{
return S_OK;
}
HRESULT hr = E_UNEXPECTED;
if (m_data_obj && !(uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY | CMF_OPTIMIZEFORINVOKE)))
{
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE;
mii.wID = idCmdFirst++;
mii.fType = MFT_STRING;
hr = GetTitle(NULL, &mii.dwTypeData);
if (FAILED(hr))
{
return hr;
}
mii.fState = MFS_ENABLED;
// TODO icon from file
if (!InsertMenuItem(hmenu, indexMenu, TRUE, &mii))
{
hr = HRESULT_FROM_WIN32(GetLastError());
Trace::QueryContextMenuError(hr);
}
else
{
hr = MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1);
}
}
return hr;
}
IFACEMETHODIMP ExplorerCommand::InvokeCommand(CMINVOKECOMMANDINFO* pici)
{
Trace::Invoked();
ipc::Writer writer;
if (HRESULT result = writer.start(); FAILED(result))
{
Trace::InvokedRet(result);
return result;
}
if (HRESULT result = LaunchUI(pici, &writer); FAILED(result))
{
Trace::InvokedRet(result);
return result;
}
IShellItemArray* shell_item_array;
HRESULT result = SHCreateShellItemArrayFromDataObject(m_data_obj, __uuidof(IShellItemArray), reinterpret_cast<void**>(&shell_item_array));
if (SUCCEEDED(result))
{
DWORD num_items;
shell_item_array->GetCount(&num_items);
for (DWORD i = 0; i < num_items; i++)
{
IShellItem* item;
result = shell_item_array->GetItemAt(i, &item);
if (SUCCEEDED(result))
{
LPWSTR file_path;
result = item->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
if (SUCCEEDED(result))
{
// TODO Aggregate items and send to UI
writer.add_path(file_path);
CoTaskMemFree(file_path);
}
item->Release();
}
}
shell_item_array->Release();
}
Trace::InvokedRet(S_OK);
return S_OK;
}
IFACEMETHODIMP ExplorerCommand::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pReserved, CHAR* pszName, UINT cchMax)
{
return E_NOTIMPL;
}
HRESULT ExplorerCommand::s_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvObject)
{
*ppvObject = NULL;
HRESULT hr = E_OUTOFMEMORY;
ExplorerCommand* pNew = new (std::nothrow) ExplorerCommand;
if (pNew)
{
hr = pNew->QueryInterface(riid, ppvObject);
pNew->Release();
}
return hr;
}
ExplorerCommand::ExplorerCommand()
{
++globals::ref_count;
}
ExplorerCommand::~ExplorerCommand()
{
if (m_data_obj)
{
m_data_obj->Release();
}
--globals::ref_count;
}
// Implementation taken from src/common/utils
// TODO reference that function
inline std::wstring get_module_folderpath(HMODULE mod = nullptr, const bool removeFilename = true)
{
wchar_t buffer[MAX_PATH + 1];
DWORD actual_length = GetModuleFileNameW(mod, buffer, MAX_PATH + 1);
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
const DWORD long_path_length = 0xFFFF; // should be always enough
std::wstring long_filename(long_path_length, L'\0');
actual_length = GetModuleFileNameW(mod, long_filename.data(), long_path_length);
PathRemoveFileSpecW(long_filename.data());
long_filename.resize(std::wcslen(long_filename.data()));
long_filename.shrink_to_fit();
return long_filename;
}
if (removeFilename)
{
PathRemoveFileSpecW(buffer);
}
return { buffer, (UINT)lstrlenW(buffer) };
}
HRESULT ExplorerCommand::LaunchUI(CMINVOKECOMMANDINFO* pici, ipc::Writer* writer)
{
// Compute exe path
std::wstring exe_path = get_module_folderpath(globals::instance);
exe_path += L'\\';
exe_path += constants::nonlocalizable::FileNameUIExe;
STARTUPINFO startupInfo;
ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
startupInfo.cb = sizeof(STARTUPINFO);
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
if (pici)
{
startupInfo.wShowWindow = pici->nShow;
}
else
{
startupInfo.wShowWindow = SW_SHOWNORMAL;
}
PROCESS_INFORMATION processInformation;
std::wstring command_line = L"\"";
command_line += exe_path;
command_line += L"\"\0";
CreateProcessW(
NULL,
command_line.data(),
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&startupInfo,
&processInformation);
// Discard handles
CloseHandle(processInformation.hProcess);
CloseHandle(processInformation.hThread);
return S_OK;
}

View File

@@ -0,0 +1,50 @@
#pragma once
#include "pch.h"
#include "IPC.h"
#define EXPLORER_COMMAND_UUID_STR "84d68575-e186-46ad-b0cb-baeb45ee29c0"
class __declspec(uuid(EXPLORER_COMMAND_UUID_STR)) ExplorerCommand : public IExplorerCommand, public IShellExtInit, public IContextMenu
{
public:
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
IFACEMETHODIMP_(ULONG) AddRef() override;
IFACEMETHODIMP_(ULONG) Release() override;
// IExplorerCommand
IFACEMETHODIMP GetTitle(IShellItemArray* psiItemArray, LPWSTR* ppszName) override;
IFACEMETHODIMP GetIcon(IShellItemArray* psiItemArray, LPWSTR* ppszIcon) override;
IFACEMETHODIMP GetToolTip(IShellItemArray* psiItemArray, LPWSTR* ppszInfotip) override;
IFACEMETHODIMP GetCanonicalName(GUID* pguidCommandName) override;
IFACEMETHODIMP GetState(IShellItemArray* psiItemArray, BOOL fOkToBeSlow, EXPCMDSTATE* pCmdState) override;
IFACEMETHODIMP Invoke(IShellItemArray* psiItemArray, IBindCtx* pbc) override;
IFACEMETHODIMP GetFlags(EXPCMDFLAGS* pFlags) override;
IFACEMETHODIMP EnumSubCommands(IEnumExplorerCommand** ppEnum) override;
// IShellExtInit
IFACEMETHODIMP Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) override;
// IContextMenu
IFACEMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) override;
IFACEMETHODIMP InvokeCommand(CMINVOKECOMMANDINFO* pici) override;
IFACEMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pReserved, CHAR* pszName, UINT cchMax) override;
// Static member to create an instance
static HRESULT s_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvObject);
// Constructor
ExplorerCommand();
// Destructor
~ExplorerCommand();
private:
// Helpers
HRESULT LaunchUI(CMINVOKECOMMANDINFO* pici, ipc::Writer* writer);
std::atomic<ULONG> m_ref_count = 1;
IDataObject* m_data_obj = NULL;
};

View File

@@ -0,0 +1,50 @@
#include <windows.h>
#include "Generated Files/resource.h"
#include "../../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@@ -0,0 +1,299 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" />
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h FileLocksmithExt.base.rc FileLocksmithExt.rc" />
</Target>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{57175ec7-92a5-4c1e-8244-e3fbca2a81de}</ProjectGuid>
<RootNamespace>FileLocksmithExt</RootNamespace>
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithExt</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithExt</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithExt</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithExt</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithExt</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithExt</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;FILELOCKSMITHEXT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>../../;../../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;FILELOCKSMITHEXT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>../../;../../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;FILELOCKSMITHEXT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>../../;../../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;FILELOCKSMITHEXT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>../../;../../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;FILELOCKSMITHLIB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>../../;../../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;FILELOCKSMITHLIB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>../../;../../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="ClassFactory.h" />
<ClInclude Include="Constants.h" />
<ClInclude Include="dllmain.h" />
<ClInclude Include="ExplorerCommand.h" />
<ClInclude Include="IPC.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Settings.h" />
<ClInclude Include="Trace.h" />
<None Include="packages.config" />
<None Include="resource.base.h" />
<ClInclude Include="Generated Files/resource.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files/FileLocksmithExt.rc" />
<None Include="FileLocksmithExt.base.rc" />
</ItemGroup>
<ItemGroup>
<None Include="Resources.resx" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ClassFactory.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="ExplorerCommand.cpp" />
<ClCompile Include="IPC.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="PowerToysModule.cpp" />
<ClCompile Include="Settings.cpp" />
<ClCompile Include="Trace.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="dll.def" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\version\version.vcxproj">
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ExplorerCommand.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Constants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ClassFactory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="dllmain.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Settings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Generated Files/resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IPC.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ExplorerCommand.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ClassFactory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PowerToysModule.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Settings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="IPC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="dll.def">
<Filter>Source Files</Filter>
</None>
<None Include="resource.base.h">
<Filter>Header Files</Filter>
</None>
<None Include="FileLocksmithExt.base.rc">
<Filter>Resource Files</Filter>
</None>
<None Include="Resources.resx">
<Filter>Resource Files</Filter>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files/FileLocksmithExt.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,62 @@
#include "pch.h"
#include "IPC.h"
#include "Constants.h"
#include <common/SettingsAPI/settings_helpers.h>
constexpr DWORD DefaultPipeBufferSize = 8192;
constexpr DWORD DefaultPipeTimeoutMillis = 200;
namespace ipc
{
Writer::Writer()
{
start();
}
Writer::~Writer()
{
finish();
}
HRESULT Writer::start()
{
std::wstring path = PTSettingsHelper::get_module_save_folder_location(constants::nonlocalizable::PowerToyName);
path += L"\\";
path += constants::nonlocalizable::LastRunPath;
try
{
m_stream = std::ofstream(path);
return S_OK;
}
catch (...)
{
return E_FAIL;
}
}
HRESULT Writer::add_path(LPCWSTR path)
{
int length = lstrlenW(path);
if (!m_stream.write(reinterpret_cast<const char*>(path), length * sizeof(WCHAR)))
{
return E_FAIL;
}
WCHAR line_break = L'\n';
if (!m_stream.write(reinterpret_cast<const char*>(&line_break), sizeof(WCHAR)))
{
return E_FAIL;
}
return S_OK;
}
void Writer::finish()
{
add_path(L"");
m_stream.close();
}
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "pch.h"
namespace ipc
{
class Writer
{
public:
Writer();
~Writer();
HRESULT start();
HRESULT add_path(LPCWSTR path);
void finish();
HANDLE get_read_handle();
private:
std::ofstream m_stream;
};
}

View File

@@ -0,0 +1,118 @@
#include "pch.h"
#include <interface/powertoy_module_interface.h>
#include <common/SettingsAPI/settings_objects.h>
#include <common/logger/logger.h>
#include <common/logger/logger_settings.h>
#include <common/utils/logger_helper.h>
#include <optional>
#include "Constants.h"
#include "dllmain.h"
#include "Settings.h"
#include "Trace.h"
#include "Generated Files/resource.h"
class FileLocksmithModule : public PowertoyModuleIface
{
public:
FileLocksmithModule()
{
LoggerHelpers::init_logger(constants::nonlocalizable::PowerToyName, L"ModuleInterface", LogSettings::fileLocksmithLoggerName);
init_settings();
}
virtual const wchar_t* get_name() override
{
static WCHAR buffer[128];
LoadStringW(globals::instance, IDS_FILELOCKSMITH_POWERTOYNAME, buffer, ARRAYSIZE(buffer));
return buffer;
}
virtual const wchar_t* get_key() override
{
return constants::nonlocalizable::PowerToyKey;
}
// Return the configured status for the gpo policy for the module
virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
{
return powertoys_gpo::getConfiguredFileLocksmithEnabledValue();
}
// Return JSON with the configuration options.
// These are the settings shown on the settings page along with their current values.
virtual bool get_config(_Out_ PWSTR buffer, _Out_ int* buffer_size) override
{
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
// Create a Settings object.
PowerToysSettings::Settings settings(hinstance, get_name());
return settings.serialize_to_buffer(buffer, buffer_size);
}
// Passes JSON with the configuration settings for the powertoy.
// This is called when the user hits Save on the settings page.
virtual void set_config(PCWSTR config) override
{
try
{
// Parse the input JSON string.
PowerToysSettings::PowerToyValues values =
PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
// Currently, there are no settings, so we don't do anything.
}
catch (std::exception& e)
{
Logger::error("Configuration parsing failed: {}", std::string{ e.what() });
}
}
virtual void enable() override
{
Logger::info(L"File Locksmith enabled");
m_enabled = true;
save_settings();
}
virtual void disable() override
{
Logger::info(L"File Locksmith disabled");
m_enabled = false;
save_settings();
}
virtual bool is_enabled() override
{
return m_enabled;
}
virtual void destroy() override
{
delete this;
}
private:
bool m_enabled;
void init_settings()
{
m_enabled = FileLocksmithSettingsInstance().GetEnabled();
Trace::EnableFileLocksmith(m_enabled);
}
void save_settings()
{
auto& settings = FileLocksmithSettingsInstance();
settings.SetEnabled(m_enabled);
settings.Save();
Trace::EnableFileLocksmith(m_enabled);
}
};
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
return new FileLocksmithModule();
}

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="FileLocksmith_CommandTitle" xml:space="preserve">
<value>What's using this file?</value>
<comment>This text will be shown when the user opens the context menu (right clicks) a file.</comment>
</data>
<data name="FileLocksmith_PowerToyName" xml:space="preserve">
<value>File Locksmith</value>
<comment>Localized name of the PowerToy.</comment>
</data>
</root>

View File

@@ -0,0 +1,85 @@
#include "pch.h"
#include "Settings.h"
#include "Constants.h"
#include <common/utils/json.h>
#include <common/SettingsAPI/settings_helpers.h>
static bool LastModifiedTime(const std::wstring& filePath, FILETIME* lpFileTime)
{
WIN32_FILE_ATTRIBUTE_DATA attr{};
if (GetFileAttributesExW(filePath.c_str(), GetFileExInfoStandard, &attr))
{
*lpFileTime = attr.ftLastWriteTime;
return true;
}
return false;
}
FileLocksmithSettings::FileLocksmithSettings()
{
std::wstring savePath = PTSettingsHelper::get_module_save_folder_location(constants::nonlocalizable::PowerToyKey);
std::error_code ec;
jsonFilePath = savePath + constants::nonlocalizable::DataFilePath;
Load();
}
void FileLocksmithSettings::Save()
{
json::JsonObject jsonData;
jsonData.SetNamedValue(constants::nonlocalizable::JsonKeyEnabled, json::value(settings.enabled));
json::to_file(jsonFilePath, jsonData);
GetSystemTimeAsFileTime(&lastLoadedTime);
}
void FileLocksmithSettings::Load()
{
if (!std::filesystem::exists(jsonFilePath))
{
Save();
}
else
{
ParseJson();
}
}
void FileLocksmithSettings::Reload()
{
// Load json settings from data file if it is modified in the meantime.
FILETIME lastModifiedTime{};
if (LastModifiedTime(jsonFilePath, &lastModifiedTime) &&
CompareFileTime(&lastModifiedTime, &lastLoadedTime) == 1)
{
Load();
}
}
void FileLocksmithSettings::ParseJson()
{
auto json = json::from_file(jsonFilePath);
if (json)
{
const json::JsonObject& jsonSettings = json.value();
try
{
if (json::has(jsonSettings, constants::nonlocalizable::JsonKeyEnabled, json::JsonValueType::Boolean))
{
settings.enabled = jsonSettings.GetNamedBoolean(constants::nonlocalizable::JsonKeyEnabled);
}
}
catch (const winrt::hresult_error&)
{
}
}
GetSystemTimeAsFileTime(&lastLoadedTime);
}
FileLocksmithSettings& FileLocksmithSettingsInstance()
{
static FileLocksmithSettings instance;
return instance;
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include "pch.h"
#include <common/utils/gpo.h>
class FileLocksmithSettings
{
public:
FileLocksmithSettings();
inline bool GetEnabled()
{
auto gpoSetting = powertoys_gpo::getConfiguredFileLocksmithEnabledValue();
if (gpoSetting == powertoys_gpo::gpo_rule_configured_enabled)
return true;
if (gpoSetting == powertoys_gpo::gpo_rule_configured_disabled)
return false;
Reload();
return settings.enabled;
}
inline void SetEnabled(bool enabled)
{
settings.enabled = enabled;
Save();
}
void Save();
void Load();
private:
struct Settings
{
bool enabled{ true };
};
void Reload();
void ParseJson();
Settings settings;
std::wstring jsonFilePath;
FILETIME lastLoadedTime;
};
FileLocksmithSettings& FileLocksmithSettingsInstance();

View File

@@ -0,0 +1,60 @@
#include "pch.h"
#include "Trace.h"
#include "../common/Telemetry/ProjectTelemetry.h"
TRACELOGGING_DEFINE_PROVIDER(
g_hProvider,
"Microsoft.PowerToys",
// {38e8889b-9731-53f5-e901-e8a7c1753074}
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
TraceLoggingOptionProjectTelemetry());
void Trace::RegisterProvider() noexcept
{
TraceLoggingRegister(g_hProvider);
}
void Trace::UnregisterProvider() noexcept
{
TraceLoggingUnregister(g_hProvider);
}
void Trace::EnableFileLocksmith(_In_ bool enabled) noexcept
{
TraceLoggingWrite(
g_hProvider,
"FileLocksmith_EnableFileLocksmith",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingBoolean(enabled, "Enabled"));
}
void Trace::Invoked() noexcept
{
TraceLoggingWrite(
g_hProvider,
"FileLocksmith_Invoked",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::InvokedRet(_In_ HRESULT hr) noexcept
{
TraceLoggingWrite(
g_hProvider,
"FileLocksmith_InvokedRet",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingHResult(hr),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::QueryContextMenuError(_In_ HRESULT hr) noexcept
{
TraceLoggingWrite(
g_hProvider,
"FileLocksmith_QueryContextMenuError",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingHResult(hr),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "pch.h"
class Trace
{
public:
static void RegisterProvider() noexcept;
static void UnregisterProvider() noexcept;
static void EnableFileLocksmith(_In_ bool enabled) noexcept;
static void Invoked() noexcept;
static void InvokedRet(_In_ HRESULT hr) noexcept;
static void QueryContextMenuError(_In_ HRESULT hr) noexcept;
};

View File

@@ -0,0 +1,5 @@
EXPORTS
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE

View File

@@ -0,0 +1,62 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
// Additional libraries to link
#pragma comment(lib, "shlwapi")
#include "ClassFactory.h"
#include "Trace.h"
namespace globals
{
HMODULE instance;
std::atomic<ULONG> ref_count;
std::atomic<bool> enabled;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
globals::instance = hModule;
Trace::RegisterProvider();
break;
case DLL_PROCESS_DETACH:
Trace::UnregisterProvider();
break;
}
return TRUE;
}
STDAPI DllRegisterServer()
{
return S_OK;
}
STDAPI DllUnregisterServer()
{
return S_OK;
}
STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void** ppv)
{
HRESULT result = E_FAIL;
*ppv = NULL;
ClassFactory* class_factory = new (std::nothrow) ClassFactory(clsid);
if (class_factory)
{
result = class_factory->QueryInterface(riid, ppv);
class_factory->Release();
}
return result;
}
STDAPI DllCanUnloadNow(void)
{
return globals::ref_count == 0 ? S_OK : S_FALSE;
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include "pch.h"
namespace globals
{
extern HMODULE instance;
extern std::atomic<ULONG> ref_count;
extern std::atomic<bool> enabled;
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.220929.3" targetFramework="native" />
</packages>

View File

@@ -0,0 +1,5 @@
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.

View File

@@ -0,0 +1,16 @@
#pragma once
// add headers that you want to pre-compile here
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <Windows.h>
#include <Shlwapi.h>
#include <ShlObj_core.h>
#include <atlbase.h>
#include <commctrl.h>
// C++ Standard library
#include <atomic>
#include <string>
#include <filesystem>
#include <fstream>

View File

@@ -0,0 +1,13 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by FileLocksmithLib.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys File Locksmith Static Library"
#define INTERNAL_NAME "PowerToys.FileLocksmithLib.lib"
#define ORIGINAL_FILENAME "PowerToys.FileLocksmithLib.lib"
// Non-localizable
//////////////////////////////

View File

@@ -0,0 +1,172 @@
#include "pch.h"
#include "FileLocksmith.h"
#include "NtdllExtensions.h"
static bool is_directory(const std::wstring path)
{
DWORD attributes = GetFileAttributesW(path.c_str());
return attributes != INVALID_FILE_ATTRIBUTES && attributes & FILE_ATTRIBUTE_DIRECTORY;
}
// C++20 method
static bool starts_with(std::wstring_view whole, std::wstring_view part)
{
return whole.size() >= part.size() && whole.substr(0, part.size()) == part;
}
std::vector<ProcessResult> find_processes_recursive(const std::vector<std::wstring>& paths)
{
NtdllExtensions nt_ext;
// TODO use a trie!
// This maps kernel names of files within `paths` to their normal paths.
std::map<std::wstring, std::wstring> kernel_names_files;
// This maps kernel names of directories within `paths` to their normal paths.
std::map<std::wstring, std::wstring> kernel_names_dirs;
for (const auto& path : paths)
{
auto kernel_path = nt_ext.path_to_kernel_name(path.c_str());
if (!kernel_path.empty())
{
(is_directory(path) ? kernel_names_dirs : kernel_names_files)[kernel_path] = path;
}
}
std::map<DWORD, std::set<std::wstring>> pid_files;
// Returns a normal path of the file specified by kernel_name, if it matches
// the search criteria. Otherwise, return an empty string.
auto kernel_paths_contain = [&](const std::wstring& kernel_name) -> std::wstring
{
// Normal equivalence
if (auto it = kernel_names_files.find(kernel_name); it != kernel_names_files.end())
{
return it->second;
}
if (auto it = kernel_names_dirs.find(kernel_name); it != kernel_names_dirs.end())
{
return it->second;
}
for (const auto& [dir_kernel_name, dir_path] : kernel_names_dirs)
{
if (starts_with(kernel_name, dir_kernel_name + (dir_kernel_name.length()>0&&dir_kernel_name[dir_kernel_name.length()-1]!=L'\\' ? L"\\" : L"")))
{
return dir_path + kernel_name.substr(dir_kernel_name.size());
}
}
return {};
};
for (const auto& handle_info : nt_ext.handles())
{
if (handle_info.type_name == L"File")
{
auto path = kernel_paths_contain(handle_info.kernel_file_name);
if (!path.empty())
{
pid_files[handle_info.pid].insert(std::move(path));
}
}
}
// Check all modules used by processes
auto processes = nt_ext.processes();
for (const auto& process : processes)
{
for (const auto& path : process.modules)
{
auto kernel_name = nt_ext.path_to_kernel_name(path.c_str());
auto found_path = kernel_paths_contain(kernel_name);
if (!found_path.empty())
{
pid_files[process.pid].insert(std::move(found_path));
}
}
}
std::vector<ProcessResult> result;
for (const auto& process_info : processes)
{
if (auto it = pid_files.find(process_info.pid); it != pid_files.end())
{
result.push_back(ProcessResult
{
process_info.name,
process_info.pid,
std::vector(it->second.begin(), it->second.end())
});
}
}
return result;
}
std::wstring pid_to_user(DWORD pid)
{
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (process == NULL)
{
return {};
}
std::wstring user = L"";
std::wstring domain = L"";
HANDLE token = NULL;
if (OpenProcessToken(process, TOKEN_QUERY, &token))
{
DWORD token_size = 0;
GetTokenInformation(token, TokenUser, NULL, 0, &token_size);
if (token_size > 0)
{
std::vector<BYTE> token_buffer(token_size);
GetTokenInformation(token, TokenUser, token_buffer.data(), token_size, &token_size);
TOKEN_USER* user_ptr = (TOKEN_USER*)token_buffer.data();
PSID psid = user_ptr->User.Sid;
DWORD user_size = 0;
DWORD domain_size = 0;
SID_NAME_USE sid_name;
LookupAccountSidW(NULL, psid, NULL, &user_size, NULL, &domain_size, &sid_name);
user.resize(user_size + 1);
domain.resize(domain_size + 1);
LookupAccountSidW(NULL, psid, user.data(), &user_size, domain.data(), &domain_size, &sid_name);
user[user_size] = L'\0';
domain[domain_size] = L'\0';
}
CloseHandle(token);
}
CloseHandle(process);
return user;
}
constexpr size_t LongMaxPathSize = 65536;
std::wstring pid_to_full_path(DWORD pid)
{
HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
std::wstring result(LongMaxPathSize, L'\0');
// Returns zero on failure, so it's okay to resize to zero.
auto length = GetModuleFileNameExW(process, NULL, result.data(), (DWORD)result.size());
result.resize(length);
CloseHandle(process);
return result;
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include "pch.h"
struct ProcessResult
{
std::wstring name;
DWORD pid;
std::vector<std::wstring> files;
};
// Second version, checks handles towards files and all subfiles and folders of given dirs, if any.
std::vector<ProcessResult> find_processes_recursive(const std::vector<std::wstring>& paths);
// Gives the user name of the account running this process
std::wstring pid_to_user(DWORD pid);
// Gives the full path of the executable, given the process id
std::wstring pid_to_full_path(DWORD pid);

View File

@@ -0,0 +1,40 @@
#include <windows.h>
#include "resource.h"
#include "../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
1 VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
END
END

View File

@@ -0,0 +1,290 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{c604b37e-9d0e-4484-8778-e8b31b0e1b3a}</ProjectGuid>
<RootNamespace>FileLocksmithLibInterop</RootNamespace>
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
<TargetFramework>net6.0-windows</TargetFramework>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<CLRSupport>NetCore</CLRSupport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<CLRSupport>NetCore</CLRSupport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<CLRSupport>NetCore</CLRSupport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<CLRSupport>NetCore</CLRSupport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<CLRSupport>NetCore</CLRSupport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<CLRSupport>NetCore</CLRSupport>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithLib.Interop</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithLib.Interop</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithLib.Interop</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithLib.Interop</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithLib.Interop</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithLib.Interop</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;FILELOCKSMITHLIBINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsManaged>NetCore</CompileAsManaged>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<AdditionalOptions>/Zc:twoPhase-</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;FILELOCKSMITHLIBINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsManaged>NetCore</CompileAsManaged>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/Zc:twoPhase-</AdditionalOptions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;FILELOCKSMITHLIBINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsManaged>NetCore</CompileAsManaged>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<AdditionalOptions>/Zc:twoPhase-</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;FILELOCKSMITHLIBINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsManaged>NetCore</CompileAsManaged>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<AdditionalOptions>/Zc:twoPhase-</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;FILELOCKSMITHLIBINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsManaged>NetCore</CompileAsManaged>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/Zc:twoPhase-</AdditionalOptions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;FILELOCKSMITHLIBINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsManaged>NetCore</CompileAsManaged>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/Zc:twoPhase-</AdditionalOptions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="FileLocksmith.cpp" />
<ClCompile Include="Interop.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="NtdllBase.cpp" />
<ClCompile Include="NtdllExtensions.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="FileLocksmith.h" />
<ClInclude Include="NtdllBase.h" />
<ClInclude Include="NtdllExtensions.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj">
<Project>{f055103b-f80b-4d0c-bf48-057c55620033}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="FileLocksmithLibInterop.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Interop.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FileLocksmith.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="NtdllBase.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="NtdllExtensions.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FileLocksmith.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="NtdllBase.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="NtdllExtensions.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="FileLocksmithLibInterop.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,61 @@
#include "pch.h"
#include "NtdllBase.h"
Ntdll::Ntdll()
{
m_module = GetModuleHandleW(L"ntdll.dll");
if (m_module == 0)
{
throw std::runtime_error{ "GetModuleHandleW returned null" };
}
m_NtQuerySystemInformation = (NtQuerySystemInformation_t)GetProcAddress(m_module, "NtQuerySystemInformation");
if (m_NtQuerySystemInformation == 0)
{
throw std::runtime_error{ "GetProcAddress returned null for NtQuerySystemInformation" };
}
m_NtDuplicateObject = (NtDuplicateObject_t)GetProcAddress(m_module, "NtDuplicateObject");
if (m_NtDuplicateObject == 0)
{
throw std::runtime_error{ "GetProcAddress returned null for NtDuplicateObject" };
}
m_NtQueryObject = (NtQueryObject_t)GetProcAddress(m_module, "NtQueryObject");
if (m_NtQueryObject == 0)
{
throw std::runtime_error{ "GetProcAddress returned null for NtQueryObject" };
}
}
NTSTATUS Ntdll::NtQuerySystemInformation(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength)
{
return m_NtQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
}
NTSTATUS Ntdll::NtDuplicateObject(
HANDLE SourceProcessHandle,
HANDLE SourceHandle,
HANDLE TargetProcessHandle,
PHANDLE TargetHandle,
ACCESS_MASK DesiredAccess,
ULONG Attributes,
ULONG Options)
{
return m_NtDuplicateObject(SourceProcessHandle, SourceHandle, TargetProcessHandle, TargetHandle, DesiredAccess, Attributes, Options);
}
NTSTATUS Ntdll::NtQueryObject(
HANDLE ObjectHandle,
ULONG ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength)
{
return m_NtQueryObject(ObjectHandle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength);
}

View File

@@ -0,0 +1,98 @@
#pragma once
#include "pch.h"
#define DECLARE_NTDLL_FUNCTION(name, ...) \
private: \
typedef NTSTATUS(NTAPI* name ## _t)( \
__VA_ARGS__ \
); \
name ## _t m_ ## name; \
public: \
NTSTATUS name(__VA_ARGS__);
class Ntdll
{
private:
HMODULE m_module;
public:
struct SYSTEM_HANDLE
{
ULONG ProcessId;
BYTE ObjectTypeNumber;
BYTE Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
};
struct SYSTEM_HANDLE_INFORMATION
{
ULONG HandleCount;
SYSTEM_HANDLE Handles[1];
};
enum POOL_TYPE
{
NonPagedPool,
PagedPool,
NonPagedPoolMustSucceed,
DontUseThisType,
NonPagedPoolCacheAligned,
PagedPoolCacheAligned,
NonPagedPoolCacheAlignedMustS
};
struct OBJECT_TYPE_INFORMATION
{
UNICODE_STRING Name;
ULONG TotalNumberOfObjects;
ULONG TotalNumberOfHandles;
ULONG TotalPagedPoolUsage;
ULONG TotalNonPagedPoolUsage;
ULONG TotalNamePoolUsage;
ULONG TotalHandleTableUsage;
ULONG HighWaterNumberOfObjects;
ULONG HighWaterNumberOfHandles;
ULONG HighWaterPagedPoolUsage;
ULONG HighWaterNonPagedPoolUsage;
ULONG HighWaterNamePoolUsage;
ULONG HighWaterHandleTableUsage;
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccess;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
USHORT MaintainTypeList;
POOL_TYPE PoolType;
ULONG PagedPoolUsage;
ULONG NonPagedPoolUsage;
};
Ntdll();
DECLARE_NTDLL_FUNCTION(NtQuerySystemInformation,
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
)
DECLARE_NTDLL_FUNCTION(NtDuplicateObject,
HANDLE SourceProcessHandle,
HANDLE SourceHandle,
HANDLE TargetProcessHandle,
PHANDLE TargetHandle,
ACCESS_MASK DesiredAccess,
ULONG Attributes,
ULONG Options
)
DECLARE_NTDLL_FUNCTION(NtQueryObject,
HANDLE ObjectHandle,
ULONG ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength
);
};

View File

@@ -0,0 +1,266 @@
#include "pch.h"
#include "NtdllExtensions.h"
#define STATUS_INFO_LENGTH_MISMATCH ((LONG)0xC0000004)
// Calls NtQuerySystemInformation and returns a buffer containing the result.
namespace
{
std::wstring_view unicode_to_view(UNICODE_STRING unicode_str)
{
return std::wstring_view(unicode_str.Buffer, unicode_str.Length / sizeof(WCHAR));
}
std::wstring unicode_to_str(UNICODE_STRING unicode_str)
{
return std::wstring(unicode_str.Buffer, unicode_str.Length / sizeof(WCHAR));
}
// Implementation adapted from src/common/utils
inline std::wstring get_module_name(HANDLE process, HMODULE mod)
{
wchar_t buffer[MAX_PATH + 1];
DWORD actual_length = GetModuleFileNameExW(process, mod, buffer, MAX_PATH + 1);
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
const DWORD long_path_length = 0xFFFF; // should be always enough
std::wstring long_filename(long_path_length, L'\0');
actual_length = GetModuleFileNameW(mod, long_filename.data(), long_path_length);
long_filename.resize(std::wcslen(long_filename.data()));
long_filename.shrink_to_fit();
return long_filename;
}
return { buffer, (UINT)lstrlenW(buffer) };
}
constexpr size_t DefaultModulesResultSize = 512;
std::vector<std::wstring> process_modules(DWORD pid)
{
HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (!process)
{
return {};
}
std::vector<std::wstring> result;
bool completed = false;
std::vector<HMODULE> modules(DefaultModulesResultSize);
while (!completed)
{
DWORD needed;
auto status = EnumProcessModules(process, modules.data(), static_cast<DWORD>(modules.size() * sizeof(HMODULE)), &needed);
if (!status)
{
// Give up
return {};
}
if (needed > modules.size() * sizeof(HMODULE))
{
// Array is too small
modules.resize(needed / sizeof(HMODULE));
continue;
}
// Okay
modules.resize(needed / sizeof(HMODULE));
for (auto mod : modules)
{
result.push_back(get_module_name(process, mod));
}
completed = true;
}
CloseHandle(process);
return result;
}
}
NtdllExtensions::MemoryLoopResult NtdllExtensions::NtQuerySystemInformationMemoryLoop(ULONG SystemInformationClass)
{
MemoryLoopResult result;
result.memory.resize(DefaultResultBufferSize);
while (result.memory.size() <= MaxResultBufferSize)
{
ULONG result_len;
result.status = NtQuerySystemInformation(SystemInformationClass, result.memory.data(), (ULONG)result.memory.size(), &result_len);
if (result.status == STATUS_INFO_LENGTH_MISMATCH)
{
result.memory.resize(result.memory.size() * 2);
continue;
}
if (NT_ERROR(result.status))
{
result.memory.clear();
}
return result;
}
result.status = STATUS_INFO_LENGTH_MISMATCH;
result.memory.clear();
return result;
}
std::wstring NtdllExtensions::file_handle_to_kernel_name(HANDLE file_handle, std::vector<BYTE>& buffer)
{
if (GetFileType(file_handle) != FILE_TYPE_DISK)
{
return L"";
}
ULONG return_length;
auto status = NtQueryObject(file_handle, ObjectNameInformation, buffer.data(), (ULONG)buffer.size(), &return_length);
if (NT_SUCCESS(status))
{
auto object_name_info = (UNICODE_STRING*)buffer.data();
return unicode_to_str(*object_name_info);
}
return L"";
}
std::wstring NtdllExtensions::file_handle_to_kernel_name(HANDLE file_handle)
{
std::vector<BYTE> buffer(DefaultResultBufferSize);
return file_handle_to_kernel_name(file_handle, buffer);
}
std::wstring NtdllExtensions::path_to_kernel_name(LPCWSTR path)
{
HANDLE file_handle = CreateFileW(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (file_handle == INVALID_HANDLE_VALUE)
{
return {};
}
auto kernel_name = file_handle_to_kernel_name(file_handle);
CloseHandle(file_handle);
return kernel_name;
}
std::vector<NtdllExtensions::HandleInfo> NtdllExtensions::handles() noexcept
{
auto get_info_result = NtQuerySystemInformationMemoryLoop(SystemHandleInformation);
if (NT_ERROR(get_info_result.status))
{
return {};
}
auto info_ptr = (SYSTEM_HANDLE_INFORMATION*)get_info_result.memory.data();
std::map<DWORD, HANDLE> pid_to_handle;
std::vector<HandleInfo> result;
std::vector<BYTE> object_info_buffer(DefaultResultBufferSize);
for (ULONG i = 0; i < info_ptr->HandleCount; i++)
{
auto handle_info = info_ptr->Handles + i;
DWORD pid = handle_info->ProcessId;
HANDLE process_handle = NULL;
auto iter = pid_to_handle.find(pid);
if (iter != pid_to_handle.end())
{
process_handle = iter->second;
}
else
{
process_handle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
if (!process_handle)
{
continue;
}
pid_to_handle[pid] = process_handle;
}
// According to this:
// https://stackoverflow.com/questions/46384048/enumerate-handles
// NtQueryObject could hang
// TODO uncomment and investigate
// if (handle_info->GrantedAccess == 0x0012019f) {
// continue;
// }
HANDLE handle_copy;
auto dh_result = DuplicateHandle(process_handle, (HANDLE)handle_info->Handle, GetCurrentProcess(), &handle_copy, 0, 0, DUPLICATE_SAME_ACCESS);
if (dh_result == 0)
{
// Ignore this handle.
continue;
}
ULONG return_length;
auto status = NtQueryObject(handle_copy, ObjectTypeInformation, object_info_buffer.data(), (ULONG)object_info_buffer.size(), &return_length);
if (NT_ERROR(status))
{
// Ignore this handle.
CloseHandle(handle_copy);
continue;
}
auto object_type_info = (OBJECT_TYPE_INFORMATION*)object_info_buffer.data();
auto type_name = unicode_to_str(object_type_info->Name);
std::wstring file_name = file_handle_to_kernel_name(handle_copy, object_info_buffer);
if (type_name == L"File")
{
file_name = file_handle_to_kernel_name(handle_copy, object_info_buffer);
}
result.push_back(HandleInfo{ pid, handle_info->Handle, type_name, file_name });
CloseHandle(handle_copy);
}
for (auto [pid, handle] : pid_to_handle)
{
CloseHandle(handle);
}
return result;
}
// Returns the list of all processes.
// On failure, returns an empty vector.
std::vector<NtdllExtensions::ProcessInfo> NtdllExtensions::processes() noexcept
{
auto get_info_result = NtQuerySystemInformationMemoryLoop(SystemProcessInformation);
if (NT_ERROR(get_info_result.status))
{
return {};
}
std::vector<ProcessInfo> result;
auto info_ptr = (PSYSTEM_PROCESS_INFORMATION)get_info_result.memory.data();
while (info_ptr->NextEntryOffset)
{
info_ptr = decltype(info_ptr)((LPBYTE)info_ptr + info_ptr->NextEntryOffset);
ProcessInfo item;
item.name = unicode_to_str(info_ptr->ImageName);
item.pid = (DWORD)(uintptr_t)info_ptr->UniqueProcessId;
item.modules = process_modules(item.pid);
result.push_back(item);
}
return result;
}

View File

@@ -0,0 +1,52 @@
#pragma once
#include "pch.h"
#include "NtdllBase.h"
class NtdllExtensions : protected Ntdll
{
private:
constexpr static size_t DefaultResultBufferSize = 64 * 1024;
constexpr static size_t MaxResultBufferSize = 1024 * 1024 * 1024;
constexpr static int ObjectNameInformation = 1;
constexpr static int SystemHandleInformation = 16;
struct MemoryLoopResult
{
NTSTATUS status = 0;
std::vector<BYTE> memory;
};
// Calls NtQuerySystemInformation and returns a buffer containing the result.
MemoryLoopResult NtQuerySystemInformationMemoryLoop(ULONG SystemInformationClass);
std::wstring file_handle_to_kernel_name(HANDLE file_handle, std::vector<BYTE>& buffer);
public:
struct ProcessInfo
{
DWORD pid;
std::wstring name;
std::vector<std::wstring> modules;
};
struct HandleInfo
{
DWORD pid;
USHORT handle;
std::wstring type_name;
std::wstring kernel_file_name;
};
std::wstring file_handle_to_kernel_name(HANDLE file_handle);
std::wstring path_to_kernel_name(LPCWSTR path);
std::vector<HandleInfo> handles() noexcept;
// Returns the list of all processes.
// On failure, returns an empty vector.
std::vector<ProcessInfo> processes() noexcept;
};

View File

@@ -0,0 +1,186 @@
#include "pch.h"
#include "FileLocksmith.h"
#include "../FileLocksmithExt/Constants.h"
namespace FileLocksmith::Interop
{
public ref struct ProcessResult
{
System::String^ name;
System::UInt32 pid;
array<System::String^>^ files;
System::Boolean isExpanded; // For helping in the UI
};
System::String^ from_wstring_view(std::wstring_view str)
{
return gcnew System::String(str.data(), 0, static_cast<int>(str.size()));
}
std::wstring from_system_string(System::String^ str)
{
// TODO use some built-in method
auto chars = str->ToCharArray();
std::wstring result(chars->Length, 0);
for (int i = 0; i < chars->Length; i++)
{
result[i] = chars[i];
}
return result;
}
std::wstring paths_file()
{
std::wstring path = from_system_string(interop::Constants::AppDataPath());
path += L"\\";
path += constants::nonlocalizable::PowerToyName;
path += L"\\";
path += constants::nonlocalizable::LastRunPath;
return path;
}
std::wstring executable_path()
{
return pid_to_full_path(GetCurrentProcessId());
}
public ref struct NativeMethods
{
static array<ProcessResult ^> ^ FindProcessesRecursive(array<System::String^>^ paths)
{
const int n = paths->Length;
std::vector<std::wstring> paths_cpp(n);
for (int i = 0; i < n; i++)
{
paths_cpp[i] = from_system_string(paths[i]);
}
auto result_cpp = find_processes_recursive(paths_cpp);
const auto result_size = static_cast<int>(result_cpp.size());
auto result = gcnew array<ProcessResult ^>(result_size);
for (int i = 0; i < result_size; i++)
{
auto item = gcnew ProcessResult;
item->name = from_wstring_view(result_cpp[i].name);
item->pid = result_cpp[i].pid;
const int n_files = static_cast<int>(result_cpp[i].files.size());
item->files = gcnew array<System::String ^>(n_files);
for (int j = 0; j < n_files; j++)
{
item->files[j] = from_wstring_view(result_cpp[i].files[j]);
}
item->isExpanded = false;
result[i] = item;
}
return result;
}
static System::String^ PidToUser(System::UInt32 pid)
{
auto user_cpp = pid_to_user(pid);
return from_wstring_view(user_cpp);
}
static System::String^ PidToFullPath(System::UInt32 pid)
{
auto path_cpp = pid_to_full_path(pid);
return from_wstring_view(path_cpp);
}
static array<System::String^>^ ReadPathsFromFile()
{
std::ifstream stream(paths_file());
std::vector<std::wstring> result_cpp;
std::wstring line;
bool finished = false;
while (!finished)
{
WCHAR ch;
// We have to read data like this
if (!stream.read(reinterpret_cast<char*>(&ch), 2))
{
finished = true;
}
else if (ch == L'\n')
{
if (line.empty())
{
finished = true;
}
else
{
result_cpp.push_back(line);
line = {};
}
}
else
{
line += ch;
}
}
auto result = gcnew array<System::String ^>(static_cast<int>(result_cpp.size()));
for (int i = 0; i < result->Length; i++)
{
result[i] = from_wstring_view(result_cpp[i]);
}
return result;
}
static System::Boolean StartAsElevated(array<System::String ^> ^ paths)
{
std::ofstream stream(paths_file());
const WCHAR newline = L'\n';
for (int i = 0; i < paths->Length; i++)
{
auto path_cpp = from_system_string(paths[i]);
stream.write(reinterpret_cast<const char*>(path_cpp.c_str()), path_cpp.size() * sizeof(WCHAR));
stream.write(reinterpret_cast<const char*>(&newline), sizeof(WCHAR));
}
stream.write(reinterpret_cast<const char*>(&newline), sizeof(WCHAR));
if (!stream)
{
return false;
}
stream.close();
auto exec_path = executable_path();
SHELLEXECUTEINFOW exec_info;
exec_info.cbSize = sizeof(exec_info);
exec_info.fMask = SEE_MASK_NOCLOSEPROCESS;
exec_info.hwnd = NULL;
exec_info.lpVerb = L"runas";
exec_info.lpFile = exec_path.c_str();
exec_info.lpParameters = L"--elevated";
exec_info.lpDirectory = NULL;
exec_info.nShow = SW_SHOW;
exec_info.hInstApp = NULL;
if (ShellExecuteExW(&exec_info))
{
CloseHandle(exec_info.hProcess);
return true;
}
return false;
}
};
}

View File

@@ -0,0 +1 @@
#include "pch.h"

View File

@@ -0,0 +1,19 @@
#pragma once
// System headers
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <winternl.h>
#include <Psapi.h>
#include <shellapi.h>
// C++ standard library headers
#include <stdexcept>
#include <vector>
#include <map>
#include <iostream>
#include <string>
#include <set>
#include <algorithm>
#include <fstream>

View File

@@ -0,0 +1,13 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by FileLocksmithLibInterop.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys FileLocksmithLib Interop"
#define INTERNAL_NAME "PowerToys.FileLocksmithLib.Interop"
#define ORIGINAL_FILENAME "PowerToys.FileLocksmithLib.Interop.dll"
// Non-localizable
//////////////////////////////

View File

@@ -0,0 +1,118 @@
<Application
x:Class="FileLocksmithUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FileLocksmithUI">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="SubtleButtonBackground" Color="{ThemeResource SubtleFillColorTransparent}" />
<SolidColorBrush x:Key="SubtleButtonBackgroundPointerOver" Color="{ThemeResource SubtleFillColorSecondary}" />
<SolidColorBrush x:Key="SubtleButtonBackgroundPressed" Color="{ThemeResource SubtleFillColorTertiary}" />
<SolidColorBrush x:Key="SubtleButtonBackgroundDisabled" Color="{ThemeResource SubtleFillColorTransparent}" />
<SolidColorBrush x:Key="SubtleButtonForeground" Color="{ThemeResource TextFillColorPrimary}" />
<SolidColorBrush x:Key="SubtleButtonForegroundPointerOver" Color="{ThemeResource TextFillColorPrimary}" />
<SolidColorBrush x:Key="SubtleButtonForegroundPressed" Color="{ThemeResource TextFillColorSecondary}" />
<SolidColorBrush x:Key="SubtleButtonForegroundDisabled" Color="{ThemeResource TextFillColorDisabled}" />
<Style x:Key="SubtleButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{ThemeResource SubtleButtonBackground}" />
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
<Setter Property="Foreground" Value="{ThemeResource SubtleButtonForeground}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Width" Value="36" />
<Setter Property="Padding" Value="0" />
<Setter Property="Height" Value="36" />
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin" Value="-3" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ContentPresenter
x:Name="ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AnimatedIcon.State="Normal"
AutomationProperties.AccessibilityView="Raw"
Background="{TemplateBinding Background}"
BackgroundSizing="{TemplateBinding BackgroundSizing}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
CornerRadius="{TemplateBinding CornerRadius}">
<ContentPresenter.BackgroundTransition>
<BrushTransition Duration="0:0:0.083" />
</ContentPresenter.BackgroundTransition>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<VisualState.Setters>
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="PointerOver" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<VisualState.Setters>
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Pressed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<VisualState.Setters>
<!-- DisabledVisual Should be handled by the control, not the animated icon. -->
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Normal" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</ContentPresenter>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Linq;
using FileLocksmithUI.Helpers;
using ManagedCommon;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
namespace FileLocksmithUI
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : Application
{
/// <summary>
/// Initializes a new instance of the <see cref="App"/> class.
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredFileLocksmithEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
Environment.Exit(0); // Current.Exit won't work until there's a window opened.
return;
}
_window = new MainWindow(Environment.GetCommandLineArgs().Contains("--elevated"));
_window.Activate();
}
private Window _window;
}
}

View File

@@ -0,0 +1,25 @@
// 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 PowerToys.FileLocksmithUI.Converters
{
using System;
using FileLocksmith.Interop;
using Microsoft.UI.Xaml.Data;
public sealed class FileCountConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
#pragma warning disable CA1305 // Specify IFormatProvider
return ((string[])value).Length.ToString();
#pragma warning restore CA1305 // Specify IFormatProvider
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value;
}
}
}

View File

@@ -0,0 +1,49 @@
// 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 PowerToys.FileLocksmithUI.Converters
{
using System;
using System.IO;
using FileLocksmith.Interop;
using Microsoft.UI.Xaml.Data;
public sealed class FileListToDescriptionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var paths = (string[])value;
if (paths.Length == 0)
{
return string.Empty;
}
string firstPath = paths[0];
firstPath = Path.GetFileName(paths[0]);
if (string.IsNullOrEmpty(firstPath))
{
firstPath = Path.GetDirectoryName(paths[0]);
}
if (string.IsNullOrEmpty(firstPath))
{
firstPath = Path.GetPathRoot(paths[0]);
}
if (paths.Length == 1)
{
return firstPath;
}
else
{
return firstPath + "; +" + (paths.Length - 1);
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value;
}
}
}

View File

@@ -0,0 +1,51 @@
// 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 PowerToys.FileLocksmithUI.Converters
{
using System;
using System.Drawing;
using System.IO;
using CommunityToolkit.WinUI.UI;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Media.Imaging;
using Windows.Storage;
public sealed class PidToIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var y = FileLocksmith.Interop.NativeMethods.PidToFullPath((uint)value);
Icon icon = null;
if (!string.IsNullOrEmpty(y))
{
icon = Icon.ExtractAssociatedIcon(y);
}
if (icon != null)
{
Bitmap bitmap = icon.ToBitmap();
BitmapImage bitmapImage = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
stream.Position = 0;
bitmapImage.SetSource(stream.AsRandomAccessStream());
}
return bitmapImage;
}
else
{
return new BitmapImage();
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value;
}
}
}

View File

@@ -0,0 +1,23 @@
// 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 PowerToys.FileLocksmithUI.Converters
{
using System;
using FileLocksmith.Interop;
using Microsoft.UI.Xaml.Data;
public sealed class PidToUserConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return NativeMethods.PidToUser((uint)value);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value;
}
}
}

View File

@@ -0,0 +1,82 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\Version.props" />
<PropertyGroup>
<AssemblyTitle>PowerToys.FileLocksmith</AssemblyTitle>
<AssemblyDescription>PowerToys File Locksmith</AssemblyDescription>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\modules\FileLocksmith</OutputPath>
<RootNamespace>PowerToys.FileLocksmithUI</RootNamespace>
<AssemblyName>PowerToys.FileLocksmithUI</AssemblyName>
<ApplicationManifest>app.manifest</ApplicationManifest>
<RuntimeIdentifiers>win10-x64;win10-arm64</RuntimeIdentifiers>
<UseWinUI>true</UseWinUI>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
<WindowsPackageType>None</WindowsPackageType>
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<ApplicationIcon>icon.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<NoWarn>0436</NoWarn>
</PropertyGroup>
<ItemGroup>
<None Remove="Views\MainPage.xaml" />
</ItemGroup>
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->
<PropertyGroup>
<CsWinRTIncludes>PowerToys.GPOWrapper</CsWinRTIncludes>
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
<!-- Needed for CommunityToolkit.Labs.WinUI.SettingsControls. -->
<PropertyGroup>
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json</RestoreAdditionalProjectSources>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.0.0" />
<PackageReference Include="CommunityToolkit.Labs.WinUI.SettingsControls" Version="0.0.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.0.0" />
<PackageReference Include="CommunityToolkit.WinUI.UI" Version="7.1.2" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.1.5" />
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22000.194" />
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
<PackageReference Include="WinUIEx" Version="1.8.0" />
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
<!-- Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
Tools extension to be activated for this project even if the Windows App SDK Nuget
package has not yet been restored -->
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnablePreviewMsixTooling)'=='true'">
<ProjectCapability Include="Msix" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<Content Include="icon.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\FileLocksmithLibInterop\FileLocksmithLibInterop.vcxproj" />
</ItemGroup>
<ItemGroup>
<Page Update="Views\MainPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,76 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
namespace FileLocksmithUI.Helpers
{
public static class Logger
{
private static readonly string ApplicationLogPath = Path.Combine(interop.Constants.AppDataPath(), "File Locksmith\\FileLocksmithUI\\Logs");
static Logger()
{
if (!Directory.Exists(ApplicationLogPath))
{
Directory.CreateDirectory(ApplicationLogPath);
}
// Using InvariantCulture since this is used for a log file name
var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
Trace.AutoFlush = true;
}
public static void LogError(string message)
{
Log(message, "ERROR");
}
public static void LogError(string message, Exception ex)
{
Log(
message + Environment.NewLine +
ex?.Message + Environment.NewLine +
"Inner exception: " + Environment.NewLine +
ex?.InnerException?.Message + Environment.NewLine +
"Stack trace: " + Environment.NewLine +
ex?.StackTrace,
"ERROR");
}
public static void LogWarning(string message)
{
Log(message, "WARNING");
}
public static void LogInfo(string message)
{
Log(message, "INFO");
}
private static void Log(string message, string type)
{
Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay);
Trace.Indent();
Trace.WriteLine(GetCallerInfo());
Trace.WriteLine(message);
Trace.Unindent();
}
private static string GetCallerInfo()
{
StackTrace stackTrace = new StackTrace();
var methodName = stackTrace.GetFrame(3)?.GetMethod();
var className = methodName?.DeclaringType.Name;
return "[Method]: " + methodName?.Name + " [Class]: " + className;
}
}
}

View File

@@ -0,0 +1,44 @@
<winuiex:WindowEx
x:Class="FileLocksmithUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:contract7NotPresent="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract,7)"
xmlns:contract7Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,7)"
xmlns:converters="using:CommunityToolkit.WinUI.UI.Converters"
xmlns:converters1="using:PowerToys.FileLocksmithUI.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interop="using:FileLocksmith.Interop"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="using:PowerToys.FileLocksmithUI.Views"
xmlns:winuiex="using:WinUIEx"
x:Uid="AppTitle"
Width="680"
MinWidth="480"
MinHeight="320"
IsShownInSwitchers="True"
IsTitleBarVisible="True"
mc:Ignorable="d">
<winuiex:WindowEx.Backdrop>
<winuiex:MicaSystemBackdrop />
</winuiex:WindowEx.Backdrop>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="titleBar">
<StackPanel
Margin="16,8,8,8"
VerticalAlignment="Top"
Orientation="Horizontal">
<TextBlock FontFamily="{ThemeResource SymbolThemeFontFamily}" Text="&#xEA3F;" />
<TextBlock
x:Uid="AppTitleText"
Margin="12,0,0,0"
Style="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
</Grid>
<views:MainPage x:Name="mainPage" Grid.Row="1" />
</Grid>
</winuiex:WindowEx>

View File

@@ -0,0 +1,49 @@
// 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 ManagedCommon;
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using WinUIEx;
namespace FileLocksmithUI
{
public sealed partial class MainWindow : WindowEx, IDisposable
{
public MainWindow(bool isElevated)
{
InitializeComponent();
mainPage.ViewModel.IsElevated = isElevated;
SetTitleBar();
}
private void SetTitleBar()
{
if (AppWindowTitleBar.IsCustomizationSupported())
{
AppWindow window = this.GetAppWindow();
window.TitleBar.ExtendsContentIntoTitleBar = true;
window.TitleBar.ButtonBackgroundColor = Colors.Transparent;
SetTitleBar(titleBar);
}
else
{
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
ThemeHelpers.SetImmersiveDarkMode(hWnd, ThemeHelpers.GetAppTheme() == AppTheme.Dark);
titleBar.Visibility = Visibility.Collapsed;
// Set window icon
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hWnd);
AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
appWindow.SetIcon("icon.ico");
}
}
public void Dispose()
{
}
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<PublishDir>$(PowerToysRoot)\$(Platform)\$(Configuration)\modules\FileLocksmith</PublishDir>
<RuntimeIdentifier>win10-$(Platform)</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<PublishSingleFile>False</PublishSingleFile>
<PublishReadyToRun>False</PublishReadyToRun>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AppTitle.Title" xml:space="preserve">
<value>File Locksmith</value>
</data>
<data name="AppTitleText.Text" xml:space="preserve">
<value>File Locksmith</value>
</data>
<data name="EmptyListDescription.Text" xml:space="preserve">
<value>No results</value>
</data>
<data name="EndTask.Text" xml:space="preserve">
<value>End task</value>
</data>
<data name="Files.Text" xml:space="preserve">
<value>Files</value>
</data>
<data name="PathsTooltipDescription.Text" xml:space="preserve">
<value>Click to see the entire list of paths.</value>
<comment>Paths as in file paths that were selected for the utility to check.</comment>
</data>
<data name="SelectedFilesListDialog.Title" xml:space="preserve">
<value>Selected file paths</value>
<comment>Paths as in file paths that were selected for the utility to check.</comment>
</data>
<data name="SelectedFilesListDialog.CloseButtonText" xml:space="preserve">
<value>Close</value>
<comment>As in, close a dialog prompt.</comment>
</data>
<data name="NoFilesSelected.Text" xml:space="preserve">
<value>No files selected</value>
</data>
<data name="ProcessID.Header" xml:space="preserve">
<value>Process ID</value>
</data>
<data name="Reload.Text" xml:space="preserve">
<value>Reload</value>
</data>
<data name="RestartAsAdmin.Text" xml:space="preserve">
<value>Restart as administrator</value>
</data>
<data name="User.Header" xml:space="preserve">
<value>User</value>
</data>
</root>

View File

@@ -0,0 +1,184 @@
// 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 PowerToys.FileLocksmithUI.ViewModels
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FileLocksmith.Interop;
using global::FileLocksmithUI;
using global::FileLocksmithUI.Helpers;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Controls;
#pragma warning disable CA1708 // Identifiers should differ by more than case
public partial class MainViewModel : ObservableObject, IDisposable
#pragma warning restore CA1708 // Identifiers should differ by more than case
{
public IAsyncRelayCommand LoadProcessesCommand { get; }
private bool _isLoading;
private bool _isElevated;
private string[] paths;
private bool _disposed;
private CancellationTokenSource _cancelProcessWatching;
public ObservableCollection<ProcessResult> Processes { get; } = new ();
public bool IsLoading
{
get
{
return _isLoading;
}
set
{
_isLoading = value;
OnPropertyChanged(nameof(IsLoading));
}
}
public bool IsElevated
{
get
{
return _isElevated;
}
set
{
_isElevated = value;
OnPropertyChanged(nameof(IsElevated));
}
}
public string[] Paths
{
get => paths;
set
{
paths = value;
OnPropertyChanged(nameof(Paths));
}
}
public string PathsToString
{
get
{
return string.Join("\n", paths);
}
}
public MainViewModel()
{
paths = NativeMethods.ReadPathsFromFile();
Logger.LogInfo($"Starting FileLocksmith with {paths.Length} files selected.");
LoadProcessesCommand = new AsyncRelayCommand(LoadProcessesAsync);
}
private async Task LoadProcessesAsync()
{
IsLoading = true;
Processes.Clear();
if (_cancelProcessWatching is not null)
{
_cancelProcessWatching.Cancel();
}
_cancelProcessWatching = new CancellationTokenSource();
foreach (ProcessResult p in await FindProcesses(paths))
{
Processes.Add(p);
WatchProcess(p, _cancelProcessWatching.Token);
}
IsLoading = false;
}
private async Task<List<ProcessResult>> FindProcesses(string[] paths)
{
var results = new List<ProcessResult>();
await Task.Run(() =>
{
results = NativeMethods.FindProcessesRecursive(paths).ToList();
});
return results;
}
private async void WatchProcess(ProcessResult process, CancellationToken token)
{
Process handle = Process.GetProcessById((int)process.pid);
try
{
await handle.WaitForExitAsync(token);
}
catch (TaskCanceledException)
{
// Nothing to do, normal operation
}
if (handle.HasExited)
{
Processes.Remove(process);
}
}
[RelayCommand]
public void EndTask(ProcessResult selectedProcess)
{
Process handle = Process.GetProcessById((int)selectedProcess.pid);
try
{
handle.Kill();
}
catch (Exception)
{
Logger.LogError($"Couldn't kill process {selectedProcess.name} with PID {selectedProcess.pid}.");
}
}
[RelayCommand]
public void RestartElevated()
{
if (NativeMethods.StartAsElevated(paths))
{
// TODO gentler exit
Environment.Exit(0);
}
else
{
// TODO report error?
Logger.LogError($"Couldn't restart as elevated.");
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_disposed = true;
}
}
}
}
}

View File

@@ -0,0 +1,206 @@
<Page
x:Class="PowerToys.FileLocksmithUI.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:PowerToys.FileLocksmithUI.Converters"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:interop="using:FileLocksmith.Interop"
xmlns:labs="using:CommunityToolkit.Labs.WinUI"
xmlns:local="using:PowerToys.FileLocksmithUI.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:toolkitConverters="using:CommunityToolkit.WinUI.UI.Converters"
mc:Ignorable="d">
<Page.Resources>
<toolkitConverters:BoolToVisibilityConverter
x:Key="boolToVisibilityConverter"
FalseValue="Visible"
TrueValue="Collapsed" />
<toolkitConverters:DoubleToVisibilityConverter
x:Key="doubleToVisibilityConverter"
FalseValue="Visible"
GreaterThan="0"
TrueValue="Collapsed" />
<converters:FileCountConverter x:Key="fileCountConverter" />
<converters:PidToIconConverter x:Key="pidToIconConverter" />
<converters:PidToUserConverter x:Key="pidToUserConverter" />
<converters:FileListToDescriptionConverter x:Key="fileListToDescriptionConverter" />
</Page.Resources>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Loaded">
<core:InvokeCommandAction Command="{x:Bind ViewModel.LoadProcessesCommand}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="48" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Padding="18,0,18,4" VerticalAlignment="Bottom">
<!--<TextBlock Margin="0,2,0,0" Text="Current proccesses:" Style="{ThemeResource BodyStrongTextBlockStyle}"/>-->
<Button Content="{x:Bind ViewModel.Paths, Converter={StaticResource fileListToDescriptionConverter}}" Click="ShowSelectedPathsButton_Click">
<Button.Template>
<ControlTemplate TargetType="Button">
<TextBlock Margin="0,8,0,0"
HorizontalAlignment="Left"
Style="{ThemeResource BodyStrongTextBlockStyle}"
Text="{TemplateBinding Content}" >
</TextBlock>
</ControlTemplate>
</Button.Template>
<ToolTipService.ToolTip>
<TextBlock x:Uid="PathsTooltipDescription" TextWrapping="WrapWholeWords"/>
</ToolTipService.ToolTip>
</Button>
<StackPanel
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Orientation="Horizontal"
Spacing="6">
<Button
Padding="0"
HorizontalAlignment="Right"
Command="{Binding LoadProcessesCommand}"
Style="{StaticResource SubtleButtonStyle}">
<FontIcon
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="16"
Glyph="&#xE149;" />
<ToolTipService.ToolTip>
<TextBlock x:Uid="Reload" />
</ToolTipService.ToolTip>
</Button>
<Button
x:Name="RestartAsAdminBtn"
Padding="0"
Command="{Binding RestartElevatedCommand}"
Style="{StaticResource SubtleButtonStyle}"
Visibility="{x:Bind ViewModel.IsElevated, Converter={StaticResource boolToVisibilityConverter}}">
<FontIcon
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="16"
Glyph="&#xE1A7;" />
<ToolTipService.ToolTip>
<TextBlock x:Uid="RestartAsAdmin" />
</ToolTipService.ToolTip>
</Button>
</StackPanel>
</Grid>
<Grid Grid.Row="1">
<Grid Visibility="{x:Bind ViewModel.IsLoading, Converter={StaticResource boolToVisibilityConverter}, Mode=OneWay}">
<ListView
x:Name="ProcessesListView"
Grid.Row="1"
ItemsSource="{x:Bind ViewModel.Processes}"
IncrementalLoadingThreshold="10"
SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate x:DataType="interop:ProcessResult">
<labs:SettingsExpander IsExpanded="{Binding isExpanded}" Margin="0,3,0,0">
<labs:SettingsExpander.Header>
<!-- We can't use the HeaderIcon because it only support a BitmapIcon, which only supports UriSource - not a direct BitmapImage -->
<StackPanel Orientation="Horizontal">
<Image
Width="16"
Height="16"
Margin="2,0,24,0"
Source="{x:Bind pid, Converter={StaticResource pidToIconConverter}}" />
<TextBlock IsTextSelectionEnabled="True" Text="{x:Bind name}" />
</StackPanel>
</labs:SettingsExpander.Header>
<labs:SettingsExpander.Content>
<Button Command="{Binding Path=DataContext.EndTaskCommand, ElementName=ProcessesListView}" CommandParameter="{Binding}">
<StackPanel Orientation="Horizontal" Spacing="6">
<FontIcon
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="14"
Glyph="&#xf140;" />
<TextBlock x:Uid="EndTask" />
</StackPanel>
</Button>
</labs:SettingsExpander.Content>
<labs:SettingsExpander.Items>
<labs:SettingsCard x:Uid="ProcessID">
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind pid}" />
</labs:SettingsCard>
<labs:SettingsCard x:Uid="User">
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind pid, Converter={StaticResource pidToUserConverter}}" />
</labs:SettingsCard>
<labs:SettingsCard ContentAlignment="Vertical">
<labs:SettingsCard.Header>
<TextBlock>
<Run x:Uid="Files" />
<Run Text="(" /><Run Text="{x:Bind files, Converter={StaticResource fileCountConverter}}" /><Run Text=")" />
</TextBlock>
</labs:SettingsCard.Header>
<ListView
Margin="-16,0,0,0"
ItemsSource="{x:Bind files}"
SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<TextBlock
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding}"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap"
ToolTipService.ToolTip="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</labs:SettingsCard>
</labs:SettingsExpander.Items>
</labs:SettingsExpander>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Vertical"
Spacing="8"
Visibility="{x:Bind ViewModel.Processes.Count, Mode=OneWay, Converter={StaticResource doubleToVisibilityConverter}}">
<Button HorizontalAlignment="Center" Command="{Binding LoadProcessesCommand}" >
<Button.Template>
<ControlTemplate TargetType="Button">
<FontIcon FontSize="32" Glyph="&#xE9F3;" />
</ControlTemplate>
</Button.Template>
<ToolTipService.ToolTip>
<TextBlock x:Uid="Reload" />
</ToolTipService.ToolTip>
</Button>
<TextBlock x:Uid="EmptyListDescription" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</StackPanel>
</Grid>
<ProgressRing
Width="48"
Height="48"
IsActive="{x:Bind ViewModel.IsLoading, Mode=OneWay}" />
</Grid>
<ContentDialog
x:Name="SelectedFilesListDialog"
x:Uid="SelectedFilesListDialog"
>
<ScrollViewer HorizontalScrollBarVisibility="Auto" HorizontalScrollMode="Auto" VerticalScrollBarVisibility="Auto" VerticalScrollMode="Auto">
<TextBlock Text="{x:Bind ViewModel.PathsToString, Mode=OneWay}"/>
</ScrollViewer>
</ContentDialog>
</Grid>
</Page>

View File

@@ -0,0 +1,27 @@
// 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 PowerToys.FileLocksmithUI.Views
{
using System;
using Microsoft.UI.Xaml.Controls;
using PowerToys.FileLocksmithUI.ViewModels;
public sealed partial class MainPage : Page
{
public MainViewModel ViewModel { get; private set; }
public MainPage()
{
this.InitializeComponent();
ViewModel = new MainViewModel();
DataContext = ViewModel;
}
private async void ShowSelectedPathsButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
await SelectedFilesListDialog.ShowAsync();
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -155,6 +155,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
L"modules/MouseUtils/PowerToys.MousePointerCrosshairs.dll", L"modules/MouseUtils/PowerToys.MousePointerCrosshairs.dll",
L"modules/PowerAccent/PowerToys.PowerAccentModuleInterface.dll", L"modules/PowerAccent/PowerToys.PowerAccentModuleInterface.dll",
L"modules/PowerOCR/PowerToys.PowerOCRModuleInterface.dll", L"modules/PowerOCR/PowerToys.PowerOCRModuleInterface.dll",
L"modules/FileLocksmith/PowerToys.FileLocksmithExt.dll",
L"modules/MeasureTool/PowerToys.MeasureToolModuleInterface.dll", L"modules/MeasureTool/PowerToys.MeasureToolModuleInterface.dll",
L"modules/Hosts/PowerToys.HostsModuleInterface.dll", L"modules/Hosts/PowerToys.HostsModuleInterface.dll",
}; };

View File

@@ -303,6 +303,22 @@ namespace Microsoft.PowerToys.Settings.UI.Library
} }
} }
private bool fileLocksmith = true;
[JsonPropertyName("File Locksmith")]
public bool FileLocksmith
{
get => fileLocksmith;
set
{
if (fileLocksmith != value)
{
LogTelemetryEvent(value);
fileLocksmith = value;
}
}
}
public string ToJsonString() public string ToJsonString()
{ {
return JsonSerializer.Serialize(this); return JsonSerializer.Serialize(this);

View File

@@ -118,6 +118,7 @@ namespace Microsoft.PowerToys.Settings.UI
case "Awake": StartupPage = typeof(Views.AwakePage); break; case "Awake": StartupPage = typeof(Views.AwakePage); break;
case "ColorPicker": StartupPage = typeof(Views.ColorPickerPage); break; case "ColorPicker": StartupPage = typeof(Views.ColorPickerPage); break;
case "FancyZones": StartupPage = typeof(Views.FancyZonesPage); break; case "FancyZones": StartupPage = typeof(Views.FancyZonesPage); break;
case "FileLocksmith": StartupPage = typeof(Views.FileLocksmithPage); break;
case "Run": StartupPage = typeof(Views.PowerLauncherPage); break; case "Run": StartupPage = typeof(Views.PowerLauncherPage); break;
case "ImageResizer": StartupPage = typeof(Views.ImageResizerPage); break; case "ImageResizer": StartupPage = typeof(Views.ImageResizerPage); break;
case "KBM": StartupPage = typeof(Views.KeyboardManagerPage); break; case "KBM": StartupPage = typeof(Views.KeyboardManagerPage); break;

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@@ -11,6 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Enums
Awake, Awake,
ColorPicker, ColorPicker,
FancyZones, FancyZones,
FileLocksmith,
FileExplorer, FileExplorer,
ImageResizer, ImageResizer,
KBM, KBM,

View File

@@ -0,0 +1,37 @@
<Page x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.OobeFileLocksmith"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Microsoft.PowerToys.Settings.UI.OOBE.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
xmlns:toolkitcontrols="using:CommunityToolkit.WinUI.UI.Controls"
mc:Ignorable="d">
<controls:OOBEPageControl x:Uid="Oobe_FileLocksmith"
HeroImage="ms-appx:///Assets/Modules/OOBE/FileLocksmith.gif">
<controls:OOBEPageControl.PageContent>
<StackPanel Orientation="Vertical">
<TextBlock x:Uid="Oobe_HowToUse"
Style="{ThemeResource OobeSubtitleStyle}" />
<toolkitcontrols:MarkdownTextBlock Background="Transparent" x:Uid="Oobe_FileLocksmith_HowToUse" />
<TextBlock x:Uid="Oobe_TipsAndTricks"
Style="{ThemeResource OobeSubtitleStyle}"/>
<toolkitcontrols:MarkdownTextBlock Background="Transparent" x:Uid="Oobe_FileLocksmith_TipsAndTricks" />
<StackPanel Orientation="Horizontal" Spacing="12" Margin="0,24,0,0">
<Button x:Uid="OOBE_Settings"
Click="SettingsLaunchButton_Click"/>
<HyperlinkButton NavigateUri="https://aka.ms/PowerToysOverview_FileLocksmith" Style="{StaticResource TextButtonStyle}">
<TextBlock x:Uid="LearnMore_FileLocksmith"
TextWrapping="Wrap" />
</HyperlinkButton>
</StackPanel>
</StackPanel>
</controls:OOBEPageControl.PageContent>
</controls:OOBEPageControl>
</Page>

View File

@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
using Microsoft.PowerToys.Settings.UI.Views;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class OobeFileLocksmith : Page
{
public OobePowerToysModule ViewModel { get; set; }
public OobeFileLocksmith()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.FileLocksmith]);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(FileLocksmithPage));
}
ViewModel.LogOpeningSettingsEvent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ViewModel.LogOpeningModuleEvent();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
ViewModel.LogClosingModuleEvent();
}
}
}

View File

@@ -48,6 +48,11 @@
<BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsFancyZones.png" /> <BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsFancyZones.png" />
</NavigationViewItem.Icon> </NavigationViewItem.Icon>
</NavigationViewItem> </NavigationViewItem>
<NavigationViewItem x:Uid="Shell_FileLocksmith" Tag="FileLocksmith">
<NavigationViewItem.Icon>
<BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsFileLocksmith.png" />
</NavigationViewItem.Icon>
</NavigationViewItem>
<NavigationViewItem x:Uid="Shell_PowerPreview" Tag="FileExplorer"> <NavigationViewItem x:Uid="Shell_PowerPreview" Tag="FileExplorer">
<NavigationViewItem.Icon> <NavigationViewItem.Icon>
<BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsFileExplorerPreview.png" /> <BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsFileExplorerPreview.png" />

View File

@@ -64,7 +64,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
Modules.Insert((int)PowerToysModules.AlwaysOnTop, new OobePowerToysModule() Modules.Insert((int)PowerToysModules.AlwaysOnTop, new OobePowerToysModule()
{ {
ModuleName = "AlwaysOnTop", ModuleName = "AlwaysOnTop",
IsNew = true, IsNew = false,
}); });
Modules.Insert((int)PowerToysModules.Awake, new OobePowerToysModule() Modules.Insert((int)PowerToysModules.Awake, new OobePowerToysModule()
{ {
@@ -81,6 +81,11 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
ModuleName = "FancyZones", ModuleName = "FancyZones",
IsNew = false, IsNew = false,
}); });
Modules.Insert((int)PowerToysModules.FileLocksmith, new OobePowerToysModule()
{
ModuleName = "FileLocksmith",
IsNew = true,
});
Modules.Insert((int)PowerToysModules.FileExplorer, new OobePowerToysModule() Modules.Insert((int)PowerToysModules.FileExplorer, new OobePowerToysModule()
{ {
ModuleName = "FileExplorer", ModuleName = "FileExplorer",
@@ -99,7 +104,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
Modules.Insert((int)PowerToysModules.MouseUtils, new OobePowerToysModule() Modules.Insert((int)PowerToysModules.MouseUtils, new OobePowerToysModule()
{ {
ModuleName = "MouseUtils", ModuleName = "MouseUtils",
IsNew = true, IsNew = false,
}); });
Modules.Insert((int)PowerToysModules.PowerRename, new OobePowerToysModule() Modules.Insert((int)PowerToysModules.PowerRename, new OobePowerToysModule()
{ {
@@ -114,7 +119,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
Modules.Insert((int)PowerToysModules.QuickAccent, new OobePowerToysModule() Modules.Insert((int)PowerToysModules.QuickAccent, new OobePowerToysModule()
{ {
ModuleName = "QuickAccent", ModuleName = "QuickAccent",
IsNew = true, IsNew = false,
}); });
Modules.Insert((int)PowerToysModules.ShortcutGuide, new OobePowerToysModule() Modules.Insert((int)PowerToysModules.ShortcutGuide, new OobePowerToysModule()
{ {
@@ -124,19 +129,19 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
Modules.Insert((int)PowerToysModules.TextExtractor, new OobePowerToysModule() Modules.Insert((int)PowerToysModules.TextExtractor, new OobePowerToysModule()
{ {
ModuleName = "TextExtractor", ModuleName = "TextExtractor",
IsNew = true, IsNew = false,
}); });
Modules.Insert((int)PowerToysModules.VideoConference, new OobePowerToysModule() Modules.Insert((int)PowerToysModules.VideoConference, new OobePowerToysModule()
{ {
ModuleName = "VideoConference", ModuleName = "VideoConference",
IsNew = true, IsNew = false,
}); });
Modules.Insert((int)PowerToysModules.MeasureTool, new OobePowerToysModule() Modules.Insert((int)PowerToysModules.MeasureTool, new OobePowerToysModule()
{ {
ModuleName = "MeasureTool", ModuleName = "MeasureTool",
IsNew = true, IsNew = false,
}); });
Modules.Insert((int)PowerToysModules.Hosts, new OobePowerToysModule() Modules.Insert((int)PowerToysModules.Hosts, new OobePowerToysModule()
@@ -187,6 +192,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
case "Awake": NavigationFrame.Navigate(typeof(OobeAwake)); break; case "Awake": NavigationFrame.Navigate(typeof(OobeAwake)); break;
case "ColorPicker": NavigationFrame.Navigate(typeof(OobeColorPicker)); break; case "ColorPicker": NavigationFrame.Navigate(typeof(OobeColorPicker)); break;
case "FancyZones": NavigationFrame.Navigate(typeof(OobeFancyZones)); break; case "FancyZones": NavigationFrame.Navigate(typeof(OobeFancyZones)); break;
case "FileLocksmith": NavigationFrame.Navigate(typeof(OobeFileLocksmith)); break;
case "Run": NavigationFrame.Navigate(typeof(OobeRun)); break; case "Run": NavigationFrame.Navigate(typeof(OobeRun)); break;
case "ImageResizer": NavigationFrame.Navigate(typeof(OobeImageResizer)); break; case "ImageResizer": NavigationFrame.Navigate(typeof(OobeImageResizer)); break;
case "KBM": NavigationFrame.Navigate(typeof(OobeKBM)); break; case "KBM": NavigationFrame.Navigate(typeof(OobeKBM)); break;

View File

@@ -1469,6 +1469,9 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<data name="Oobe_FancyZones.Description" xml:space="preserve"> <data name="Oobe_FancyZones.Description" xml:space="preserve">
<value>FancyZones is a window manager that makes it easy to create complex window layouts and quickly position windows into those layouts.</value> <value>FancyZones is a window manager that makes it easy to create complex window layouts and quickly position windows into those layouts.</value>
</data> </data>
<data name="Oobe_FileLocksmith.Description" xml:space="preserve">
<value>File Locksmith lists which processes are using the selected files or directories and allows closing those processes.</value>
</data>
<data name="Oobe_FileExplorer.Description" xml:space="preserve"> <data name="Oobe_FileExplorer.Description" xml:space="preserve">
<value>PowerToys introduces add-ons to the Windows File Explorer that will enable files like Markdown (.md), PDF (.pdf), SVG (.svg), STL (.stl), G-code (.gcode) and developer files to be viewed in the preview pane. It introduces File Explorer thumbnail support for a number of these file types as well.</value> <value>PowerToys introduces add-ons to the Windows File Explorer that will enable files like Markdown (.md), PDF (.pdf), SVG (.svg), STL (.stl), G-code (.gcode) and developer files to be viewed in the preview pane. It introduces File Explorer thumbnail support for a number of these file types as well.</value>
</data> </data>
@@ -1523,6 +1526,12 @@ Take a moment to preview the various utilities listed or view our comprehensive
<data name="Oobe_FancyZones_TipsAndTricks.Text" xml:space="preserve"> <data name="Oobe_FancyZones_TipsAndTricks.Text" xml:space="preserve">
<value>Snap a window to multiple zones by holding the **Ctrl** key (while also holding **Shift**) when dragging a window.</value> <value>Snap a window to multiple zones by holding the **Ctrl** key (while also holding **Shift**) when dragging a window.</value>
</data> </data>
<data name="Oobe_FileLocksmith_HowToUse.Text" xml:space="preserve">
<value>In File Explorer, **right-clicking one or more selected files** and **clicking on "What's using this file?"** from the context menu.</value>
</data>
<data name="Oobe_FileLocksmith_TipsAndTricks.Text" xml:space="preserve">
<value>Press the Restart Elevated button from the File Locksmith UI to also get information on elevated processes that might be using the files.</value>
</data>
<data name="Oobe_FileExplorer_HowToEnable.Text" xml:space="preserve"> <data name="Oobe_FileExplorer_HowToEnable.Text" xml:space="preserve">
<value>Open File Explorer, **select the View tab** in the File Explorer ribbon, then **select Preview Pane**. <value>Open File Explorer, **select the View tab** in the File Explorer ribbon, then **select Preview Pane**.
From there, simply click on one of the supported files in the File Explorer and observe the content on the preview pane!</value> From there, simply click on one of the supported files in the File Explorer and observe the content on the preview pane!</value>
@@ -1596,6 +1605,9 @@ From there, simply click on one of the supported files in the File Explorer and
<value>FancyZones</value> <value>FancyZones</value>
<comment>Do not localize this string</comment> <comment>Do not localize this string</comment>
</data> </data>
<data name="Oobe_FileLocksmith.Title" xml:space="preserve">
<value>File Locksmith</value>
</data>
<data name="Oobe_ImageResizer.Title" xml:space="preserve"> <data name="Oobe_ImageResizer.Title" xml:space="preserve">
<value>Image Resizer</value> <value>Image Resizer</value>
<comment>Do not localize this string</comment> <comment>Do not localize this string</comment>
@@ -1807,6 +1819,9 @@ From there, simply click on one of the supported files in the File Explorer and
<value>Learn more about FancyZones</value> <value>Learn more about FancyZones</value>
<comment>FancyZones is a product name, do not loc</comment> <comment>FancyZones is a product name, do not loc</comment>
</data> </data>
<data name="LearnMore_FileLocksmith.Text" xml:space="preserve">
<value>Learn more about File Locksmith</value>
</data>
<data name="LearnMore_ImageResizer.Text" xml:space="preserve"> <data name="LearnMore_ImageResizer.Text" xml:space="preserve">
<value>Learn more about Image Resizer</value> <value>Learn more about Image Resizer</value>
<comment>Image Resizer is a product name, do not loc</comment> <comment>Image Resizer is a product name, do not loc</comment>
@@ -2646,6 +2661,20 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="Hosts_Toggle_LaunchAdministrator.Header" xml:space="preserve"> <data name="Hosts_Toggle_LaunchAdministrator.Header" xml:space="preserve">
<value>Launch as administrator</value> <value>Launch as administrator</value>
</data> </data>
<data name="FileLocksmith.ModuleDescription" xml:space="preserve">
<value>A Windows shell extension to find out which processes are using the selected files and directories.</value>
</data>
<data name="FileLocksmith.ModuleTitle" xml:space="preserve">
<value>File Locksmith</value>
</data>
<data name="Shell_FileLocksmith.Content" xml:space="preserve">
<value>File Locksmith</value>
<comment>Product name: Navigation view item name for FileLocksmith</comment>
</data>
<data name="FileLocksmith_Enable_FileLocksmith.Header" xml:space="preserve">
<value>Enable File Locksmith</value>
<comment>File Locksmith is the name of the utility</comment>
</data>
<data name="GPO_IsSettingForced.Title" xml:space="preserve"> <data name="GPO_IsSettingForced.Title" xml:space="preserve">
<value>The systems administrator is forcing this setting.</value> <value>The systems administrator is forcing this setting.</value>
</data> </data>

View File

@@ -0,0 +1,81 @@
// 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 global::PowerToys.GPOWrapper;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
public class FileLocksmithViewModel : Observable
{
private GeneralSettings GeneralSettingsConfig { get; set; }
public FileLocksmithViewModel(ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc)
{
// To obtain the general settings configurations of PowerToys Settings.
if (settingsRepository == null)
{
throw new ArgumentNullException(nameof(settingsRepository));
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
_enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredFileLocksmithEnabledValue();
if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
{
// Get the enabled state from GPO.
_enabledStateIsGPOConfigured = true;
_isFileLocksmithEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
}
else
{
_isFileLocksmithEnabled = GeneralSettingsConfig.Enabled.FileLocksmith;
}
// set the callback functions value to hangle outgoing IPC message.
SendConfigMSG = ipcMSGCallBackFunc;
}
public bool IsFileLocksmithEnabled
{
get => _isFileLocksmithEnabled;
set
{
if (_enabledStateIsGPOConfigured)
{
// If it's GPO configured, shouldn't be able to change this state.
return;
}
if (_isFileLocksmithEnabled != value)
{
_isFileLocksmithEnabled = value;
GeneralSettingsConfig.Enabled.FileLocksmith = value;
OnPropertyChanged(nameof(IsFileLocksmithEnabled));
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
SendConfigMSG(outgoing.ToString());
// TODO: Implement when this module has properties.
// NotifyPropertyChanged();
}
}
}
public bool IsEnabledGpoConfigured
{
get => _enabledStateIsGPOConfigured;
}
private Func<string, int> SendConfigMSG { get; }
private GpoRuleConfigured _enabledGpoRuleConfiguration;
private bool _enabledStateIsGPOConfigured;
private bool _isFileLocksmithEnabled;
}
}

View File

@@ -0,0 +1,42 @@
<Page
x:Class="Microsoft.PowerToys.Settings.UI.Views.FileLocksmithPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
xmlns:converters="using:CommunityToolkit.WinUI.UI.Converters"
AutomationProperties.LandmarkType="Main">
<Page.Resources>
<converters:BoolNegationConverter x:Key="BoolNegationConverter"/>
</Page.Resources>
<controls:SettingsPageControl x:Uid="FileLocksmith"
ModuleImageSource="ms-appx:///Assets/Modules/FileLocksmith.png">
<controls:SettingsPageControl.ModuleContent>
<StackPanel Orientation="Vertical">
<controls:Setting x:Uid="FileLocksmith_Enable_FileLocksmith" IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabledGpoConfigured, Converter={StaticResource BoolNegationConverter}}">
<controls:Setting.Icon>
<BitmapIcon UriSource="ms-appx:///Assets/FluentIcons/FluentIconsPowerToys.png" ShowAsMonochrome="False" />
</controls:Setting.Icon>
<controls:Setting.ActionContent>
<ToggleSwitch IsOn="{x:Bind ViewModel.IsFileLocksmithEnabled, Mode=TwoWay}" HorizontalAlignment="Right"/>
</controls:Setting.ActionContent>
</controls:Setting>
<InfoBar x:Uid="GPO_IsSettingForced"
Severity="Informational"
IsClosable="False"
IsOpen="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabledGpoConfigured}"
IsTabStop="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabledGpoConfigured}" />
</StackPanel>
</controls:SettingsPageControl.ModuleContent>
<controls:SettingsPageControl.PrimaryLinks>
<controls:PageLink x:Uid="LearnMore_FileLocksmith" Link="https://aka.ms/PowerToysOverview_FileLocksmith"/>
</controls:SettingsPageControl.PrimaryLinks>
</controls:SettingsPageControl>
</Page>

View File

@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.ViewModels;
using Microsoft.UI.Xaml.Controls;
namespace Microsoft.PowerToys.Settings.UI.Views
{
public sealed partial class FileLocksmithPage : Page
{
private FileLocksmithViewModel ViewModel { get; set; }
public FileLocksmithPage()
{
var settingsUtils = new SettingsUtils();
ViewModel = new FileLocksmithViewModel(SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage);
DataContext = ViewModel;
InitializeComponent();
}
}
}

View File

@@ -70,6 +70,12 @@
</NavigationViewItem.Icon> </NavigationViewItem.Icon>
</NavigationViewItem> </NavigationViewItem>
<NavigationViewItem x:Uid="Shell_FileLocksmith" helpers:NavHelper.NavigateTo="views:FileLocksmithPage">
<NavigationViewItem.Icon>
<BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsFileLocksmith.png" />
</NavigationViewItem.Icon>
</NavigationViewItem>
<NavigationViewItem x:Uid="Shell_PowerPreview" helpers:NavHelper.NavigateTo="views:PowerPreviewPage"> <NavigationViewItem x:Uid="Shell_PowerPreview" helpers:NavHelper.NavigateTo="views:PowerPreviewPage">
<NavigationViewItem.Icon> <NavigationViewItem.Icon>
<BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsFileExplorerPreview.png" /> <BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsFileExplorerPreview.png" />

View File

@@ -9,6 +9,7 @@ std::vector<std::wstring> processes =
L"PowerToys.Awake.exe", L"PowerToys.Awake.exe",
L"PowerToys.FancyZonesEditor.exe", L"PowerToys.FancyZonesEditor.exe",
L"PowerToys.FancyZones.exe", L"PowerToys.FancyZones.exe",
L"PowerToys.FileLocksmithUI.exe",
L"PowerToys.KeyboardManagerEngine.exe", L"PowerToys.KeyboardManagerEngine.exe",
L"PowerToys.KeyboardManagerEditor.exe", L"PowerToys.KeyboardManagerEditor.exe",
L"PowerToys.PowerAccent.exe", L"PowerToys.PowerAccent.exe",

View File

@@ -32,6 +32,7 @@ void ReportGPOValues(const std::filesystem::path& tmpDir)
report << "getConfiguredAwakeEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredAwakeEnabledValue()) << std::endl; report << "getConfiguredAwakeEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredAwakeEnabledValue()) << std::endl;
report << "getConfiguredColorPickerEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredColorPickerEnabledValue()) << std::endl; report << "getConfiguredColorPickerEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredColorPickerEnabledValue()) << std::endl;
report << "getConfiguredFancyZonesEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredFancyZonesEnabledValue()) << std::endl; report << "getConfiguredFancyZonesEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredFancyZonesEnabledValue()) << std::endl;
report << "getConfiguredFileLocksmithEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredFileLocksmithEnabledValue()) << std::endl;
report << "getConfiguredSvgPreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredSvgPreviewEnabledValue()) << std::endl; report << "getConfiguredSvgPreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredSvgPreviewEnabledValue()) << std::endl;
report << "getConfiguredMarkdownPreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredMarkdownPreviewEnabledValue()) << std::endl; report << "getConfiguredMarkdownPreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredMarkdownPreviewEnabledValue()) << std::endl;
report << "getConfiguredMonacoPreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredMonacoPreviewEnabledValue()) << std::endl; report << "getConfiguredMonacoPreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredMonacoPreviewEnabledValue()) << std::endl;