mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-26 05:57:35 +01:00
Compare commits
29 Commits
dev/migrie
...
leilzh/fuz
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5048cef232 | ||
|
|
7158239235 | ||
|
|
6ac43eed0c | ||
|
|
f1f727519d | ||
|
|
4b41bc7dd6 | ||
|
|
9ea886b7d9 | ||
|
|
f97e3f0dbb | ||
|
|
00e3a812da | ||
|
|
6418df14b6 | ||
|
|
cc3f58a441 | ||
|
|
4f9e829155 | ||
|
|
397d5cf655 | ||
|
|
01584f33e1 | ||
|
|
67463abf98 | ||
|
|
e95a570d48 | ||
|
|
cb27874805 | ||
|
|
f65a3fc06f | ||
|
|
eafe60e850 | ||
|
|
93d2368813 | ||
|
|
e1db6dd6bc | ||
|
|
af64da23a0 | ||
|
|
c7789abf04 | ||
|
|
e700f86ace | ||
|
|
a16f784011 | ||
|
|
4e0db267dc | ||
|
|
0e98cbd57e | ||
|
|
f15bed5323 | ||
|
|
d3c4e808d0 | ||
|
|
8e051893e8 |
1
.github/actions/spell-check/allow/names.txt
vendored
1
.github/actions/spell-check/allow/names.txt
vendored
@@ -189,6 +189,7 @@ Zoltan
|
||||
Zykova
|
||||
Sameerjs
|
||||
ruslanlap
|
||||
vednig
|
||||
|
||||
# OTHERS
|
||||
|
||||
|
||||
14
.github/actions/spell-check/expect.txt
vendored
14
.github/actions/spell-check/expect.txt
vendored
@@ -173,6 +173,7 @@ CContext
|
||||
CDeclaration
|
||||
CElems
|
||||
CENTERALIGN
|
||||
cer
|
||||
certlm
|
||||
certmgr
|
||||
cfp
|
||||
@@ -343,6 +344,7 @@ DEVMODE
|
||||
DEVMODEW
|
||||
devpal
|
||||
DIALOGEX
|
||||
digicert
|
||||
dimm
|
||||
DISABLEASACTIONKEY
|
||||
DISABLENOSCROLL
|
||||
@@ -515,6 +517,7 @@ FRAMECHANGED
|
||||
frm
|
||||
Froml
|
||||
FROMTOUCH
|
||||
fsanitize
|
||||
fsmgmt
|
||||
FZE
|
||||
gacutil
|
||||
@@ -626,6 +629,7 @@ HOTKEYF
|
||||
hotkeys
|
||||
hotlight
|
||||
hotspot
|
||||
Hostx
|
||||
HPAINTBUFFER
|
||||
HRAWINPUT
|
||||
HREDRAW
|
||||
@@ -678,6 +682,7 @@ IGo
|
||||
iid
|
||||
Iindex
|
||||
Ijwhost
|
||||
ILD
|
||||
IMAGEHLP
|
||||
IMAGERESIZERCONTEXTMENU
|
||||
IMAGERESIZEREXT
|
||||
@@ -938,6 +943,7 @@ mpmc
|
||||
MRM
|
||||
MRT
|
||||
mru
|
||||
MSAL
|
||||
msc
|
||||
mscorlib
|
||||
msctls
|
||||
@@ -1071,6 +1077,7 @@ NOTRACK
|
||||
NOTSRCCOPY
|
||||
NOTSRCERASE
|
||||
NOTXORPEN
|
||||
notwindows
|
||||
NOZORDER
|
||||
NPH
|
||||
npmjs
|
||||
@@ -1391,6 +1398,7 @@ sacl
|
||||
safeprojectname
|
||||
SAMEKEYPREVIOUSLYMAPPED
|
||||
SAMESHORTCUTPREVIOUSLYMAPPED
|
||||
sancov
|
||||
SAVEFAILED
|
||||
scanled
|
||||
schedtasks
|
||||
@@ -1453,6 +1461,9 @@ SHELLDLL
|
||||
shellex
|
||||
SHELLEXECUTEINFO
|
||||
SHELLEXECUTEINFOW
|
||||
SHELLEXTENSION
|
||||
SHELLICONSIZE
|
||||
SHELLNEWVALUE
|
||||
SHFILEINFO
|
||||
SHFILEOPSTRUCT
|
||||
SHGDN
|
||||
@@ -1460,6 +1471,7 @@ SHGDNF
|
||||
SHGFI
|
||||
SHGFIICON
|
||||
SHGFILARGEICON
|
||||
SHIL
|
||||
shinfo
|
||||
shlwapi
|
||||
shobjidl
|
||||
@@ -1491,6 +1503,7 @@ SIDs
|
||||
siex
|
||||
sigdn
|
||||
SIGNINGSCENARIO
|
||||
signtool
|
||||
Signtool
|
||||
SINGLEKEY
|
||||
sipolicy
|
||||
@@ -1919,6 +1932,7 @@ WUX
|
||||
Wwanpp
|
||||
XAxis
|
||||
xclip
|
||||
xcopy
|
||||
XDocument
|
||||
XElement
|
||||
xfd
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.17" />
|
||||
<PackageVersion Include="CommunityToolkit.Common" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Collections" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Collections" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.Markdown" Version="7.1.2" />
|
||||
<PackageVersion Include="ControlzEx" Version="6.0.0" />
|
||||
|
||||
16
NOTICE.md
16
NOTICE.md
@@ -1400,14 +1400,14 @@ SOFTWARE.
|
||||
- Azure.AI.OpenAI 1.0.0-beta.17
|
||||
- CommunityToolkit.Common 8.4.0
|
||||
- CommunityToolkit.Mvvm 8.4.0
|
||||
- CommunityToolkit.WinUI.Animations 8.2.250129-preview2
|
||||
- CommunityToolkit.WinUI.Collections 8.2.250129-preview2
|
||||
- CommunityToolkit.WinUI.Controls.Primitives 8.2.250129-preview2
|
||||
- CommunityToolkit.WinUI.Controls.Segmented 8.2.250129-preview2
|
||||
- CommunityToolkit.WinUI.Controls.SettingsControls 8.2.250129-preview2
|
||||
- CommunityToolkit.WinUI.Controls.Sizers 8.2.250129-preview2
|
||||
- CommunityToolkit.WinUI.Converters 8.2.250129-preview2
|
||||
- CommunityToolkit.WinUI.Extensions 8.2.250129-preview2
|
||||
- CommunityToolkit.WinUI.Animations 8.2.250402
|
||||
- CommunityToolkit.WinUI.Collections 8.2.250402
|
||||
- CommunityToolkit.WinUI.Controls.Primitives 8.2.250402
|
||||
- CommunityToolkit.WinUI.Controls.Segmented 8.2.250402
|
||||
- CommunityToolkit.WinUI.Controls.SettingsControls 8.2.250402
|
||||
- CommunityToolkit.WinUI.Controls.Sizers 8.2.250402
|
||||
- CommunityToolkit.WinUI.Converters 8.2.250402
|
||||
- CommunityToolkit.WinUI.Extensions 8.2.250402
|
||||
- CommunityToolkit.WinUI.UI.Controls.DataGrid 7.1.2
|
||||
- CommunityToolkit.WinUI.UI.Controls.Markdown 7.1.2
|
||||
- ControlzEx 6.0.0
|
||||
|
||||
@@ -706,6 +706,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegistryPreview.FuzzTests",
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.System", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.System\Microsoft.CmdPal.Ext.System.csproj", "{64B88F02-CD88-4ED8-9624-989A800230F9}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalKeyboardService", "src\modules\cmdpal\CmdPalKeyboardService\CmdPalKeyboardService.vcxproj", "{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.FuzzingTest", "src\modules\powerrename\PowerRename.FuzzingTest\PowerRename.FuzzingTest.vcxproj", "{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
@@ -2574,14 +2578,24 @@ Global
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x64.Build.0 = Debug|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x86.Build.0 = Debug|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.ActiveCfg = Release|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.Build.0 = Release|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x86.ActiveCfg = Release|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x86.Build.0 = Release|x64
|
||||
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|x64.Build.0 = Debug|x64
|
||||
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Release|x64.ActiveCfg = Release|x64
|
||||
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Release|x64.Build.0 = Release|x64
|
||||
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Debug|x64.Build.0 = Debug|x64
|
||||
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.ActiveCfg = Release|x64
|
||||
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -2852,6 +2866,8 @@ Global
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
|
||||
{5702B3CC-8575-48D5-83D8-15BB42269CD3} = {929C1324-22E8-4412-A9A8-80E85F3985A5}
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
|
||||
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2} = {3846508C-77EB-4034-A702-F8BB263C4F79}
|
||||
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
||||
117
doc/Fuzzing/CppFuzzingGuide.md
Normal file
117
doc/Fuzzing/CppFuzzingGuide.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# 🧪 C++ Project Fuzzing Test Guide
|
||||
|
||||
This guide walks you through setting up a **fuzzing test** project for a C++ module using [libFuzzer](https://llvm.org/docs/LibFuzzer.html).
|
||||
.
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Step-by-Step Setup
|
||||
|
||||
### 1. Create a New C++ Project
|
||||
|
||||
- Use **Empty Project** template.
|
||||
- Name it `<ModuleName>.FuzzingTest`.
|
||||
|
||||
---
|
||||
|
||||
### 2. Update Build Configuration
|
||||
|
||||
- In **Configuration Manager**, Uncheck Build for both Release|ARM64, Debug|ARM64 and Debug|x64 configurations.
|
||||
- Note: ARM64 is not supported in this case, so leave ARM64 configurations build disabled.
|
||||
---
|
||||
|
||||
### 3. Enable ASan and libFuzzer in `.vcxproj`
|
||||
|
||||
Edit the project file to enable fuzzing:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<EnableASAN>true</EnableASAN>
|
||||
<EnableFuzzer>true</EnableFuzzer>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. Add Fuzzing Compiler Flags
|
||||
|
||||
Add this to `AdditionalOptions` under the `Fuzzing` configuration:
|
||||
|
||||
```xml
|
||||
/fsanitize=address
|
||||
/fsanitize-coverage=inline-8bit-counters
|
||||
/fsanitize-coverage=edge
|
||||
/fsanitize-coverage=trace-cmp
|
||||
/fsanitize-coverage=trace-div
|
||||
%(AdditionalOptions)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. Link the Sanitizer Coverage Runtime
|
||||
|
||||
In `Linker → Input → Additional Dependencies`, add:
|
||||
|
||||
```text
|
||||
$(VCToolsInstallDir)lib\$(Platform)\libsancov.lib
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. Copy Required Runtime DLL
|
||||
|
||||
Add a `PostBuildEvent` to copy the ASAN DLL:
|
||||
|
||||
```xml
|
||||
<Command>
|
||||
xcopy /y "$(VCToolsInstallDir)bin\Hostx64\x64\clang_rt.asan_dynamic-x86_64.dll" "$(OutDir)"
|
||||
</Command>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. Add Preprocessor Definitions
|
||||
|
||||
To avoid annotation issues, add these to the `Preprocessor Definitions`:
|
||||
|
||||
```text
|
||||
_DISABLE_VECTOR_ANNOTATION;_DISABLE_STRING_ANNOTATION
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧬 Required Code
|
||||
|
||||
### `LLVMFuzzerTestOneInput` Entry Point
|
||||
|
||||
Every fuzzing project must expose this function:
|
||||
|
||||
```cpp
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
std::string input(reinterpret_cast<const char*>(data), size);
|
||||
|
||||
try
|
||||
{
|
||||
// Call your module with the input here.
|
||||
}
|
||||
catch (...) {}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ [Test run in the cloud](https://eng.ms/docs/cloud-ai-platform/azure-edge-platform-aep/aep-security/epsf-edge-and-platform-security-fundamentals/the-onefuzz-service/onefuzz/faq/notwindows/walkthrough)
|
||||
|
||||
To submit a job to the cloud you can run with this command:
|
||||
|
||||
```
|
||||
oip submit --config .\OneFuzzConfig.json --drop-path <your_submission_directory> --platform windows --do-not-file-bugs --duration 1
|
||||
```
|
||||
You want to run with --do-not-file-bugs because if there is an issue with running the parser in the cloud (which is very possible), you don't want bugs to be created if there is an issue. The --duration task is the number of hours you want the task to run. I recommend just running for 1 hour to make sure things work initially. If you don't specify this parameter, it will default to 48 hours. You can find more about submitting a test job here.
|
||||
|
||||
OneFuzz will send you an email when the job has started.
|
||||
|
||||
---
|
||||
@@ -68,3 +68,4 @@ Below are community created plugins that target a website or software. They are
|
||||
| [Bilibili](https://github.com/Whuihuan/PowerToysRun-Bilibili) | [Whuihuan](https://github.com/Whuihuan) | Use AVID or BVID to parse and jump to Bilibili |
|
||||
| [YubicoOauthOTP](https://github.com/dlnilsson/Community.PowerToys.Run.Plugin.YubicoOauthOTP) | [dlnilsson](https://github.com/dlnilsson) | Display generated codes from OATH accounts stored on the YubiKey in powerToys Run |
|
||||
| [Firefox Bookmark](https://github.com/8LWXpg/PowerToysRun-FirefoxBookmark) | [8LWXpg](https://github.com/8LWXpg) | Open bookmarks in Firefox based browser |
|
||||
[Linear](https://github.com/vednig/powertoys-linear) | [vednig](https://github.com/vednig) | Create Linear Issues directly from Powertoys Run |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
@@ -63,8 +63,16 @@ using System.Diagnostics.CodeAnalysis;
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "<Dotnet port with style preservation>", Scope = "namespaceanddescendants", Target = "MouseWithoutBorders")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "<Dotnet port with style preservation>", Scope = "namespaceanddescendants", Target = "MouseWithoutBorders")]
|
||||
|
||||
// AOT
|
||||
// AOT MVVMTK0045
|
||||
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "AdvancedPaste.ViewModels")]
|
||||
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "HostsUILib")]
|
||||
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "EnvironmentVariablesUILib")]
|
||||
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.FilePreviewer")]
|
||||
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.UI")]
|
||||
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.UI.Views")]
|
||||
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib")]
|
||||
|
||||
// AOT MVVMTK0049
|
||||
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.FilePreviewer")]
|
||||
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "type", Target = "~T:Peek.UI.Views.TitleBar")]
|
||||
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib")]
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
EXPORTS
|
||||
DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
|
||||
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE
|
||||
@@ -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
|
||||
@@ -0,0 +1,187 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0"
|
||||
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
<CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
|
||||
<ProjectGuid>{5f63c743-f6ce-4dba-a200-2b3f8a14e8c2}</ProjectGuid>
|
||||
<ProjectName>CmdPalKeyboardService</ProjectName>
|
||||
<RootNamespace>CmdPalKeyboardService</RootNamespace>
|
||||
<AppxPackage>false</AppxPackage>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- BEGIN common.build.pre.props -->
|
||||
<PropertyGroup Label="Configuration">
|
||||
<EnableHybridCRT>true</EnableHybridCRT>
|
||||
<UseCrtSDKReferenceStaticWarning Condition="'$(EnableHybridCRT)'=='true'">false</UseCrtSDKReferenceStaticWarning>
|
||||
</PropertyGroup>
|
||||
<!-- END common.build.pre.props -->
|
||||
<!-- BEGIN cppwinrt.build.pre.props -->
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTEnabled>true</CppWinRTEnabled>
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>17.0</MinimumVisualStudioVersion>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<MinimalCoreWin>true</MinimalCoreWin>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<WindowsStoreApp>true</WindowsStoreApp>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<UseCrtSDKReference Condition="'$(EnableHybridCRT)'=='true'">false</UseCrtSDKReference> <!-- The SDK reference breaks the Hybrid CRT -->
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<!-- We have to use the Desktop platform for Hybrid CRT to work. -->
|
||||
<_VC_Target_Library_Platform>Desktop</_VC_Target_Library_Platform>
|
||||
<_NoWinAPIFamilyApp>true</_NoWinAPIFamilyApp>
|
||||
</PropertyGroup>
|
||||
<!-- END cppwinrt.build.pre.props -->
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="PropertySheet.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<TargetName>CmdPalKeyboardService</TargetName>
|
||||
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>../../..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
<ModuleDefinitionFile>CmdPalKeyboardService.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>Shell32.lib;user32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="KeyboardListener.h">
|
||||
<DependentUpon>KeyboardListener.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KeyboardListener.cpp">
|
||||
<DependentUpon>KeyboardListener.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="KeyboardListener.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="CmdPalKeyboardService.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PropertySheet.props" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="CmdPalKeyboardService.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\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.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
|
||||
<!-- BEGIN common.build.post.props -->
|
||||
<!--
|
||||
The Hybrid CRT model statically links the runtime and STL and dynamically
|
||||
links the UCRT instead of the VC++ CRT. The UCRT ships with Windows.
|
||||
WinAppSDK asserts that this is "supported according to the CRT maintainer."
|
||||
|
||||
This must come before Microsoft.Cpp.targets because it manipulates ClCompile.RuntimeLibrary.
|
||||
-->
|
||||
<ItemDefinitionGroup Condition="'$(EnableHybridCRT)'=='true' and '$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<!-- We use MultiThreadedDebug, rather than MultiThreadedDebugDLL, to avoid DLL dependencies on VCRUNTIME140d.dll and MSVCP140d.dll. -->
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
|
||||
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
|
||||
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
|
||||
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
|
||||
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrtd.lib</IgnoreSpecificDefaultLibraries>
|
||||
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrtd.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(EnableHybridCRT)'=='true' and ('$(Configuration)'=='Release' or '$(Configuration)'=='AuditMode')">
|
||||
<ClCompile>
|
||||
<!-- We use MultiThreaded, rather than MultiThreadedDLL, to avoid DLL dependencies on VCRUNTIME140.dll and MSVCP140.dll. -->
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
|
||||
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
|
||||
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
|
||||
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
|
||||
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrt.lib</IgnoreSpecificDefaultLibraries>
|
||||
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrt.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<!-- END common.build.post.props -->
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0"
|
||||
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Resources">
|
||||
<UniqueIdentifier>accd3aa8-1ba0-4223-9bbe-0c431709210b</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Generated Files">
|
||||
<UniqueIdentifier>{926ab91d-31b4-48c3-b9a4-e681349f27f0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="KeyboardListener.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CmdPalKeyboardService.def" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PropertySheet.props" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="CmdPalKeyboardService.rc">
|
||||
<Filter>Resources</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
165
src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.cpp
Normal file
165
src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
#include "pch.h"
|
||||
#include "KeyboardListener.h"
|
||||
#include "KeyboardListener.g.cpp"
|
||||
|
||||
// #include <common/logger/logger.h>
|
||||
// #include <common/utils/logger_helper.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
}
|
||||
|
||||
namespace winrt::CmdPalKeyboardService::implementation
|
||||
{
|
||||
KeyboardListener::KeyboardListener()
|
||||
{
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
void KeyboardListener::Start()
|
||||
{
|
||||
#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
|
||||
const bool hook_disabled = IsDebuggerPresent();
|
||||
#else
|
||||
const bool hook_disabled = false;
|
||||
#endif
|
||||
if (!hook_disabled)
|
||||
{
|
||||
if (!s_llKeyboardHook)
|
||||
{
|
||||
s_llKeyboardHook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc, NULL, NULL);
|
||||
if (!s_llKeyboardHook)
|
||||
{
|
||||
DWORD errorCode = GetLastError();
|
||||
show_last_error_message(L"SetWindowsHookEx", errorCode, L"CmdPalKeyboardService");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardListener::Stop()
|
||||
{
|
||||
if (s_llKeyboardHook && UnhookWindowsHookEx(s_llKeyboardHook))
|
||||
{
|
||||
s_llKeyboardHook = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardListener::SetHotkeyAction(bool win, bool ctrl, bool shift, bool alt, uint8_t key, hstring const& id)
|
||||
{
|
||||
Hotkey hotkey = { .win = win, .ctrl = ctrl, .shift = shift, .alt = alt, .key = key };
|
||||
std::unique_lock lock{ mutex };
|
||||
|
||||
HotkeyDescriptor desc = { .hotkey = hotkey, .id = std::wstring(id) };
|
||||
hotkeyDescriptors.insert(desc);
|
||||
}
|
||||
|
||||
void KeyboardListener::ClearHotkey(hstring const& id)
|
||||
{
|
||||
{
|
||||
std::unique_lock lock{ mutex };
|
||||
auto it = hotkeyDescriptors.begin();
|
||||
while (it != hotkeyDescriptors.end())
|
||||
{
|
||||
if (it->id == id)
|
||||
{
|
||||
it = hotkeyDescriptors.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardListener::ClearHotkeys()
|
||||
{
|
||||
{
|
||||
std::unique_lock lock{ mutex };
|
||||
auto it = hotkeyDescriptors.begin();
|
||||
while (it != hotkeyDescriptors.end())
|
||||
{
|
||||
it = hotkeyDescriptors.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardListener::SetProcessCommand(ProcessCommand processCommand)
|
||||
{
|
||||
m_processCommandCb = [trigger = std::move(processCommand)](hstring const& id) {
|
||||
trigger(id);
|
||||
};
|
||||
}
|
||||
|
||||
LRESULT KeyboardListener::DoLowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
const auto& keyPressInfo = *reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||
|
||||
if ((wParam != WM_KEYDOWN) && (wParam != WM_SYSKEYDOWN))
|
||||
{
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
Hotkey hotkey{
|
||||
.win = (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000),
|
||||
.ctrl = static_cast<bool>(GetAsyncKeyState(VK_CONTROL) & 0x8000),
|
||||
.shift = static_cast<bool>(GetAsyncKeyState(VK_SHIFT) & 0x8000),
|
||||
.alt = static_cast<bool>(GetAsyncKeyState(VK_MENU) & 0x8000),
|
||||
.key = static_cast<unsigned char>(keyPressInfo.vkCode)
|
||||
};
|
||||
|
||||
if (hotkey == Hotkey{})
|
||||
{
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
bool do_action = false;
|
||||
std::wstring actionId{};
|
||||
|
||||
{
|
||||
// Hold the lock for the shortest possible duration
|
||||
std::unique_lock lock{ mutex };
|
||||
HotkeyDescriptor dummy{ .hotkey = hotkey };
|
||||
auto it = hotkeyDescriptors.find(dummy);
|
||||
if (it != hotkeyDescriptors.end())
|
||||
{
|
||||
do_action = true;
|
||||
actionId = it->id;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_action)
|
||||
{
|
||||
m_processCommandCb(hstring{ actionId });
|
||||
|
||||
// After invoking the hotkey send a dummy key to prevent Start Menu from activating
|
||||
INPUT dummyEvent[1] = {};
|
||||
dummyEvent[0].type = INPUT_KEYBOARD;
|
||||
dummyEvent[0].ki.wVk = 0xFF;
|
||||
dummyEvent[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
SendInput(1, dummyEvent, sizeof(INPUT));
|
||||
|
||||
// Swallow the key press
|
||||
return 1;
|
||||
}
|
||||
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT KeyboardListener::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (s_instance == nullptr)
|
||||
{
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
if (nCode < 0)
|
||||
{
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
return s_instance->DoLowLevelKeyboardProc(nCode, wParam, lParam);
|
||||
}
|
||||
}
|
||||
67
src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.h
Normal file
67
src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include "KeyboardListener.g.h"
|
||||
#include <mutex>
|
||||
#include <spdlog/stopwatch.h>
|
||||
#include <set>
|
||||
|
||||
namespace winrt::CmdPalKeyboardService::implementation
|
||||
{
|
||||
struct KeyboardListener : KeyboardListenerT<KeyboardListener>
|
||||
{
|
||||
struct Hotkey
|
||||
{
|
||||
bool win = false;
|
||||
bool ctrl = false;
|
||||
bool shift = false;
|
||||
bool alt = false;
|
||||
unsigned char key = 0;
|
||||
|
||||
std::strong_ordering operator<=>(const Hotkey&) const = default;
|
||||
};
|
||||
|
||||
struct HotkeyDescriptor
|
||||
{
|
||||
Hotkey hotkey;
|
||||
std::wstring id;
|
||||
|
||||
bool operator<(const HotkeyDescriptor& other) const
|
||||
{
|
||||
return hotkey < other.hotkey;
|
||||
};
|
||||
};
|
||||
|
||||
KeyboardListener();
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void SetHotkeyAction(bool win, bool ctrl, bool shift, bool alt, uint8_t key, hstring const& id);
|
||||
void ClearHotkey(hstring const& id);
|
||||
void ClearHotkeys();
|
||||
void SetProcessCommand(ProcessCommand processCommand);
|
||||
|
||||
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
private:
|
||||
LRESULT CALLBACK DoLowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
static inline KeyboardListener* s_instance;
|
||||
HHOOK s_llKeyboardHook = nullptr;
|
||||
|
||||
// Max DWORD for key code to disable keys.
|
||||
const DWORD VK_DISABLED = 0x100;
|
||||
DWORD vkCodePressed = VK_DISABLED;
|
||||
|
||||
std::multiset<HotkeyDescriptor> hotkeyDescriptors;
|
||||
std::mutex mutex;
|
||||
|
||||
std::function<void(hstring const&)> m_processCommandCb;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::CmdPalKeyboardService::factory_implementation
|
||||
{
|
||||
struct KeyboardListener : KeyboardListenerT<KeyboardListener, implementation::KeyboardListener>
|
||||
{
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
namespace CmdPalKeyboardService
|
||||
{
|
||||
[version(1.0), uuid(78ab07cd-e128-4e73-86aa-e48e6b6d01ff)] delegate void ProcessCommand(String id);
|
||||
|
||||
[default_interface] runtimeclass KeyboardListener {
|
||||
KeyboardListener();
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
void SetHotkeyAction(Boolean win, Boolean ctrl, Boolean shift, Boolean alt, UInt8 key, String id);
|
||||
void ClearHotkey(String id);
|
||||
void ClearHotkeys();
|
||||
void SetProcessCommand(ProcessCommand processCommand);
|
||||
}
|
||||
}
|
||||
17
src/modules/cmdpal/CmdPalKeyboardService/PropertySheet.props
Normal file
17
src/modules/cmdpal/CmdPalKeyboardService/PropertySheet.props
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0"
|
||||
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<!--
|
||||
To customize common C++/WinRT project properties:
|
||||
* right-click the project node
|
||||
* expand the Common Properties item
|
||||
* select the C++/WinRT property page
|
||||
|
||||
For more advanced scenarios, and complete documentation, please see:
|
||||
https://github.com/Microsoft/cppwinrt/tree/master/nuget
|
||||
-->
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup />
|
||||
</Project>
|
||||
4
src/modules/cmdpal/CmdPalKeyboardService/packages.config
Normal file
4
src/modules/cmdpal/CmdPalKeyboardService/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
</packages>
|
||||
1
src/modules/cmdpal/CmdPalKeyboardService/pch.cpp
Normal file
1
src/modules/cmdpal/CmdPalKeyboardService/pch.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
4
src/modules/cmdpal/CmdPalKeyboardService/pch.h
Normal file
4
src/modules/cmdpal/CmdPalKeyboardService/pch.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <Unknwn.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
13
src/modules/cmdpal/CmdPalKeyboardService/resource.h
Normal file
13
src/modules/cmdpal/CmdPalKeyboardService/resource.h
Normal file
@@ -0,0 +1,13 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by PowerToys.MeasureToolCore.rc
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "CmdPalKeyboardService"
|
||||
#define INTERNAL_NAME "CmdPalKeyboardService"
|
||||
#define ORIGINAL_FILENAME "CmdPalKeyboardService.dll"
|
||||
|
||||
// Non-localizable
|
||||
//////////////////////////////
|
||||
@@ -207,12 +207,16 @@ public:
|
||||
|
||||
try
|
||||
{
|
||||
if (!package::GetRegisteredPackage(L"Microsoft.CommandPalette", false).has_value())
|
||||
std::wstring packageName = L"Microsoft.CommandPalette";
|
||||
#ifdef _DEBUG
|
||||
packageName = L"Microsoft.CommandPalette.Dev";
|
||||
#endif
|
||||
if (!package::GetRegisteredPackage(packageName, false).has_value())
|
||||
{
|
||||
Logger::info(L"CmdPal not installed. Installing...");
|
||||
|
||||
std::wstring installationFolder = get_module_folderpath();
|
||||
#if _DEBUG
|
||||
#ifdef _DEBUG
|
||||
std::wstring archSubdir = L"x64";
|
||||
#ifdef _M_ARM64
|
||||
archSubdir = L"ARM64";
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.IO.Compression;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
@@ -28,69 +29,69 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
|
||||
"body": [
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_page_title}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_page_title)}},
|
||||
"size": "large"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_page_text}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_page_text)}},
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_name_header}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_header)}},
|
||||
"weight": "bolder",
|
||||
"size": "default"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_name_description}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_description)}},
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"label": "{{Properties.Resources.builtin_create_extension_name_label}}",
|
||||
"label": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_label)}},
|
||||
"isRequired": true,
|
||||
"errorMessage": "{{Properties.Resources.builtin_create_extension_name_required}}",
|
||||
"errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_required)}},
|
||||
"id": "ExtensionName",
|
||||
"placeholder": "ExtensionName",
|
||||
"regex": "^[^\\s]+$"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_display_name_header}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_header)}},
|
||||
"weight": "bolder",
|
||||
"size": "default"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_display_name_description}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_description)}},
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"label": "{{Properties.Resources.builtin_create_extension_display_name_label}}",
|
||||
"label": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_label)}},
|
||||
"isRequired": true,
|
||||
"errorMessage": "{{Properties.Resources.builtin_create_extension_display_name_required}}",
|
||||
"errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_required)}},
|
||||
"id": "DisplayName",
|
||||
"placeholder": "My new extension"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_directory_header}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_header)}},
|
||||
"weight": "bolder",
|
||||
"size": "default"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_directory_description}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_description)}},
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"label": "{{Properties.Resources.builtin_create_extension_directory_label}}",
|
||||
"label": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_label)}},
|
||||
"isRequired": true,
|
||||
"errorMessage": "{{Properties.Resources.builtin_create_extension_directory_required}}",
|
||||
"errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_required)}},
|
||||
"id": "OutputPath",
|
||||
"placeholder": "C:\\users\\me\\dev"
|
||||
}
|
||||
@@ -98,7 +99,7 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "{{Properties.Resources.builtin_create_extension_submit}}",
|
||||
"title": {{FormatJsonString(Properties.Resources.builtin_create_extension_submit)}},
|
||||
"associatedInputs": "auto"
|
||||
}
|
||||
]
|
||||
@@ -192,4 +193,10 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
|
||||
// Delete the temp dir
|
||||
Directory.Delete(tempDir, true);
|
||||
}
|
||||
|
||||
private string FormatJsonString(string str)
|
||||
{
|
||||
// Escape the string for JSON
|
||||
return JsonSerializer.Serialize(str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
|
||||
public string SearchText { get; private set; } = string.Empty;
|
||||
|
||||
public string InitialSearchText { get; private set; } = string.Empty;
|
||||
|
||||
public CommandItemViewModel EmptyContent { get; private set; }
|
||||
|
||||
private bool _isDynamic;
|
||||
@@ -128,7 +130,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
|
||||
try
|
||||
{
|
||||
IListItem[] newItems = _model.Unsafe!.GetItems();
|
||||
var newItems = _model.Unsafe!.GetItems();
|
||||
|
||||
// Collect all the items into new viewmodels
|
||||
Collection<ListItemViewModel> newViewModels = [];
|
||||
@@ -136,7 +138,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
// TODO we can probably further optimize this by also keeping a
|
||||
// HashSet of every ExtensionObject we currently have, and only
|
||||
// building new viewmodels for the ones we haven't already built.
|
||||
foreach (IListItem? item in newItems)
|
||||
foreach (var item in newItems)
|
||||
{
|
||||
ListItemViewModel viewModel = new(item, new(this));
|
||||
|
||||
@@ -147,8 +149,8 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<ListItemViewModel> firstTwenty = newViewModels.Take(20);
|
||||
foreach (ListItemViewModel? item in firstTwenty)
|
||||
var firstTwenty = newViewModels.Take(20);
|
||||
foreach (var item in firstTwenty)
|
||||
{
|
||||
item?.SafeInitializeProperties();
|
||||
}
|
||||
@@ -233,7 +235,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
iterable = Items.ToArray();
|
||||
}
|
||||
|
||||
foreach (ListItemViewModel item in iterable)
|
||||
foreach (var item in iterable)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -266,8 +268,8 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
return 1;
|
||||
}
|
||||
|
||||
MatchResult nameMatch = StringMatcher.FuzzySearch(query, listItem.Title);
|
||||
MatchResult descriptionMatch = StringMatcher.FuzzySearch(query, listItem.Subtitle);
|
||||
var nameMatch = StringMatcher.FuzzySearch(query, listItem.Title);
|
||||
var descriptionMatch = StringMatcher.FuzzySearch(query, listItem.Subtitle);
|
||||
return new[] { nameMatch.Score, (descriptionMatch.Score - 4) / 2, 0 }.Max();
|
||||
}
|
||||
|
||||
@@ -280,7 +282,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
// Similarly stolen from ListHelpers.FilterList
|
||||
public static IEnumerable<ListItemViewModel> FilterList(IEnumerable<ListItemViewModel> items, string query)
|
||||
{
|
||||
IOrderedEnumerable<ScoredListItemViewModel> scores = items
|
||||
var scores = items
|
||||
.Where(i => !i.IsInErrorState)
|
||||
.Select(li => new ScoredListItemViewModel() { ViewModel = li, Score = ScoreListItem(query, li) })
|
||||
.Where(score => score.Score > 0)
|
||||
@@ -359,7 +361,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
{
|
||||
base.InitializeProperties();
|
||||
|
||||
IListPage? model = _model.Unsafe;
|
||||
var model = _model.Unsafe;
|
||||
if (model == null)
|
||||
{
|
||||
return; // throw?
|
||||
@@ -373,8 +375,9 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
_modelPlaceholderText = model.PlaceholderText;
|
||||
UpdateProperty(nameof(PlaceholderText));
|
||||
|
||||
SearchText = model.SearchText;
|
||||
InitialSearchText = SearchText = model.SearchText;
|
||||
UpdateProperty(nameof(SearchText));
|
||||
UpdateProperty(nameof(InitialSearchText));
|
||||
|
||||
EmptyContent = new(new(model.EmptyContent), PageContext);
|
||||
EmptyContent.SlowInitializeProperties();
|
||||
@@ -385,7 +388,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
|
||||
public void LoadMoreIfNeeded()
|
||||
{
|
||||
IListPage? model = this._model.Unsafe;
|
||||
var model = this._model.Unsafe;
|
||||
if (model == null)
|
||||
{
|
||||
return;
|
||||
@@ -412,7 +415,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
{
|
||||
base.FetchProperty(propertyName);
|
||||
|
||||
IListPage? model = this._model.Unsafe;
|
||||
var model = this._model.Unsafe;
|
||||
if (model == null)
|
||||
{
|
||||
return; // throw?
|
||||
@@ -475,13 +478,13 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
|
||||
lock (_listLock)
|
||||
{
|
||||
foreach (ListItemViewModel item in Items)
|
||||
foreach (var item in Items)
|
||||
{
|
||||
item.SafeCleanup();
|
||||
}
|
||||
|
||||
Items.Clear();
|
||||
foreach (ListItemViewModel item in FilteredItems)
|
||||
foreach (var item in FilteredItems)
|
||||
{
|
||||
item.SafeCleanup();
|
||||
}
|
||||
@@ -489,7 +492,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
FilteredItems.Clear();
|
||||
}
|
||||
|
||||
IListPage? model = _model.Unsafe;
|
||||
var model = _model.Unsafe;
|
||||
if (model != null)
|
||||
{
|
||||
model.ItemsChanged -= Model_ItemsChanged;
|
||||
|
||||
@@ -26,6 +26,8 @@ public partial class SettingsModel : ObservableObject
|
||||
|
||||
public HotkeySettings? Hotkey { get; set; } = DefaultActivationShortcut;
|
||||
|
||||
public bool UseLowLevelGlobalHotkey { get; set; }
|
||||
|
||||
public bool ShowAppDetails { get; set; }
|
||||
|
||||
public bool HotkeyGoesHome { get; set; }
|
||||
|
||||
@@ -27,6 +27,17 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseLowLevelGlobalHotkey
|
||||
{
|
||||
get => _settings.UseLowLevelGlobalHotkey;
|
||||
set
|
||||
{
|
||||
_settings.UseLowLevelGlobalHotkey = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hotkey)));
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowAppDetails
|
||||
{
|
||||
get => _settings.ShowAppDetails;
|
||||
|
||||
@@ -250,18 +250,31 @@ public sealed partial class SearchBar : UserControl,
|
||||
private void Page_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
var property = e.PropertyName;
|
||||
if (CurrentPageViewModel is ListViewModel list &&
|
||||
property == nameof(ListViewModel.SearchText))
|
||||
{
|
||||
// Only if the text actually changed...
|
||||
// (sometimes this triggers on a round-trip of the SearchText)
|
||||
if (FilterBox.Text != list.SearchText)
|
||||
{
|
||||
// ... Update our displayed text, and...
|
||||
FilterBox.Text = list.SearchText;
|
||||
|
||||
// ... Move the cursor to the end of the input
|
||||
FilterBox.Select(FilterBox.Text.Length, 0);
|
||||
if (CurrentPageViewModel is ListViewModel list)
|
||||
{
|
||||
if (property == nameof(ListViewModel.SearchText))
|
||||
{
|
||||
// Only if the text actually changed...
|
||||
// (sometimes this triggers on a round-trip of the SearchText)
|
||||
if (FilterBox.Text != list.SearchText)
|
||||
{
|
||||
// ... Update our displayed text, and...
|
||||
FilterBox.Text = list.SearchText;
|
||||
|
||||
// ... Move the cursor to the end of the input
|
||||
FilterBox.Select(FilterBox.Text.Length, 0);
|
||||
}
|
||||
}
|
||||
else if (property == nameof(ListViewModel.InitialSearchText))
|
||||
{
|
||||
// GH #38712:
|
||||
// The ListPage will notify us of the `InitialSearchText` when
|
||||
// we first load the viewmodel. We can use that as an
|
||||
// opportunity to immediately select the search text. That lets
|
||||
// the user start typing a new search without manually
|
||||
// selecting the old one.
|
||||
SelectSearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using CmdPalKeyboardService;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.Common.Helpers;
|
||||
using Microsoft.CmdPal.Common.Messages;
|
||||
@@ -52,6 +53,8 @@ public sealed partial class MainWindow : Window,
|
||||
#pragma warning restore SA1306 // Field names should begin with lower-case letter
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
|
||||
private readonly KeyboardListener _keyboardListener;
|
||||
|
||||
// Notification Area ("Tray") icon data
|
||||
private NOTIFYICONDATAW? _trayIconData;
|
||||
private DestroyIconSafeHandle? _largeIcon;
|
||||
@@ -66,6 +69,11 @@ public sealed partial class MainWindow : Window,
|
||||
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());
|
||||
CommandPaletteHost.SetHostHwnd((ulong)_hwnd.Value);
|
||||
|
||||
_keyboardListener = new KeyboardListener();
|
||||
_keyboardListener.Start();
|
||||
|
||||
_keyboardListener.SetProcessCommand(new CmdPalKeyboardService.ProcessCommand(HandleSummon));
|
||||
|
||||
// TaskbarCreated is the message that's broadcast when explorer.exe
|
||||
// restarts. We need to know when that happens to be able to bring our
|
||||
// notification area icon back
|
||||
@@ -278,16 +286,12 @@ public sealed partial class MainWindow : Window,
|
||||
ShowHwnd(message.Hwnd, settings.SummonOn);
|
||||
}
|
||||
|
||||
public void Receive(HideWindowMessage message)
|
||||
{
|
||||
PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE);
|
||||
}
|
||||
public void Receive(HideWindowMessage message) => PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE);
|
||||
|
||||
public void Receive(QuitMessage message) =>
|
||||
|
||||
public void Receive(QuitMessage message)
|
||||
{
|
||||
// This might come in on a background thread
|
||||
DispatcherQueue.TryEnqueue(() => Close());
|
||||
}
|
||||
|
||||
public void Receive(DismissMessage message) =>
|
||||
PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE);
|
||||
@@ -304,6 +308,8 @@ public sealed partial class MainWindow : Window,
|
||||
// Workaround by turning it off before shutdown.
|
||||
App.Current.DebugSettings.FailFastOnErrors = false;
|
||||
DisposeAcrylic();
|
||||
|
||||
_keyboardListener.Stop();
|
||||
}
|
||||
|
||||
private void DisposeAcrylic()
|
||||
@@ -368,12 +374,17 @@ public sealed partial class MainWindow : Window,
|
||||
// ... then don't hide the window when it loses focus.
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE);
|
||||
|
||||
PowerToysTelemetry.Log.WriteEvent(new CmdPalDismissedOnLostFocus());
|
||||
// Are we disabled? If we are, then we don't want to dismiss on focus lost.
|
||||
// This can happen if an extension wanted to show a modal dialog on top of our
|
||||
// window i.e. in the case of an MSAL auth window.
|
||||
if (PInvoke.IsWindowEnabled(_hwnd) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE);
|
||||
PowerToysTelemetry.Log.WriteEvent(new CmdPalDismissedOnLostFocus());
|
||||
}
|
||||
|
||||
if (_configurationSource != null)
|
||||
@@ -382,14 +393,13 @@ public sealed partial class MainWindow : Window,
|
||||
}
|
||||
}
|
||||
|
||||
public void Summon(string commandId)
|
||||
{
|
||||
public void Summon(string commandId) =>
|
||||
|
||||
// The actual showing and hiding of the window will be done by the
|
||||
// ShellPage. This is because we don't want to show the window if the
|
||||
// user bound a hotkey to just an invokable command, which we can't
|
||||
// know till the message is being handled.
|
||||
WeakReferenceMessenger.Default.Send<HotkeySummonMessage>(new(commandId, _hwnd));
|
||||
}
|
||||
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
private const uint DOT_KEY = 0xBE;
|
||||
@@ -398,6 +408,8 @@ public sealed partial class MainWindow : Window,
|
||||
|
||||
private void UnregisterHotkeys()
|
||||
{
|
||||
_keyboardListener.ClearHotkeys();
|
||||
|
||||
while (_hotkeys.Count > 0)
|
||||
{
|
||||
PInvoke.UnregisterHotKey(_hwnd, _hotkeys.Count - 1);
|
||||
@@ -412,19 +424,28 @@ public sealed partial class MainWindow : Window,
|
||||
var globalHotkey = settings.Hotkey;
|
||||
if (globalHotkey != null)
|
||||
{
|
||||
var vk = globalHotkey.Code;
|
||||
var modifiers =
|
||||
(globalHotkey.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) |
|
||||
(globalHotkey.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) |
|
||||
(globalHotkey.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) |
|
||||
(globalHotkey.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0)
|
||||
;
|
||||
|
||||
var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk);
|
||||
if (success)
|
||||
if (settings.UseLowLevelGlobalHotkey)
|
||||
{
|
||||
_keyboardListener.SetHotkeyAction(globalHotkey.Win, globalHotkey.Ctrl, globalHotkey.Shift, globalHotkey.Alt, (byte)globalHotkey.Code, string.Empty);
|
||||
|
||||
_hotkeys.Add(new(globalHotkey, string.Empty));
|
||||
}
|
||||
else
|
||||
{
|
||||
var vk = globalHotkey.Code;
|
||||
var modifiers =
|
||||
(globalHotkey.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) |
|
||||
(globalHotkey.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) |
|
||||
(globalHotkey.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) |
|
||||
(globalHotkey.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0)
|
||||
;
|
||||
|
||||
var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk);
|
||||
if (success)
|
||||
{
|
||||
_hotkeys.Add(new(globalHotkey, string.Empty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var commandHotkey in settings.CommandHotkeys)
|
||||
@@ -450,6 +471,26 @@ public sealed partial class MainWindow : Window,
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSummon(string commandId)
|
||||
{
|
||||
var isRootHotkey = string.IsNullOrEmpty(commandId);
|
||||
PowerToysTelemetry.Log.WriteEvent(new CmdPalHotkeySummoned(isRootHotkey));
|
||||
|
||||
// Note to future us: the wParam will have the index of the hotkey we registered.
|
||||
// We can use that in the future to differentiate the hotkeys we've pressed
|
||||
// so that we can bind hotkeys to individual commands
|
||||
if (!this.Visible || !isRootHotkey)
|
||||
{
|
||||
Activate();
|
||||
|
||||
Summon(commandId);
|
||||
}
|
||||
else if (isRootHotkey)
|
||||
{
|
||||
PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
private LRESULT HotKeyPrc(
|
||||
HWND hwnd,
|
||||
uint uMsg,
|
||||
@@ -464,22 +505,23 @@ public sealed partial class MainWindow : Window,
|
||||
if (hotkeyIndex < _hotkeys.Count)
|
||||
{
|
||||
var hotkey = _hotkeys[hotkeyIndex];
|
||||
var isRootHotkey = string.IsNullOrEmpty(hotkey.CommandId);
|
||||
PowerToysTelemetry.Log.WriteEvent(new CmdPalHotkeySummoned(isRootHotkey));
|
||||
HandleSummon(hotkey.CommandId);
|
||||
|
||||
// Note to future us: the wParam will have the index of the hotkey we registered.
|
||||
// We can use that in the future to differentiate the hotkeys we've pressed
|
||||
// so that we can bind hotkeys to individual commands
|
||||
if (!this.Visible || !isRootHotkey)
|
||||
{
|
||||
Activate();
|
||||
// var isRootHotkey = string.IsNullOrEmpty(hotkey.CommandId);
|
||||
|
||||
Summon(hotkey.CommandId);
|
||||
}
|
||||
else if (isRootHotkey)
|
||||
{
|
||||
PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_HIDE);
|
||||
}
|
||||
// // Note to future us: the wParam will have the index of the hotkey we registered.
|
||||
// // We can use that in the future to differentiate the hotkeys we've pressed
|
||||
// // so that we can bind hotkeys to individual commands
|
||||
// if (!this.Visible || !isRootHotkey)
|
||||
// {
|
||||
// Activate();
|
||||
|
||||
// Summon(hotkey.CommandId);
|
||||
// }
|
||||
// else if (isRootHotkey)
|
||||
// {
|
||||
// PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_HIDE);
|
||||
// }
|
||||
}
|
||||
|
||||
return (LRESULT)IntPtr.Zero;
|
||||
|
||||
@@ -23,13 +23,13 @@
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(CIBuild)'=='true'">
|
||||
<PropertyGroup>
|
||||
<GenerateAppxPackageOnBuild>true</GenerateAppxPackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- This lets us actually reference types from Microsoft.Terminal.UI -->
|
||||
<CsWinRTIncludes>Microsoft.Terminal.UI</CsWinRTIncludes>
|
||||
<CsWinRTIncludes>Microsoft.Terminal.UI;CmdPalKeyboardService</CsWinRTIncludes>
|
||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -123,6 +123,12 @@
|
||||
<Private>True</Private>
|
||||
<CopyLocalSatelliteAssemblies>True</CopyLocalSatelliteAssemblies>
|
||||
</ProjectReference>
|
||||
|
||||
<ProjectReference Include="..\CmdPalKeyboardService\CmdPalKeyboardService.vcxproj">
|
||||
<ReferenceOutputAssembly>True</ReferenceOutputAssembly>
|
||||
<Private>True</Private>
|
||||
<CopyLocalSatelliteAssemblies>True</CopyLocalSatelliteAssemblies>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -15,6 +15,7 @@ CallWindowProc
|
||||
ShowWindow
|
||||
SetForegroundWindow
|
||||
EnableWindow
|
||||
IsWindowEnabled
|
||||
SetFocus
|
||||
SetActiveWindow
|
||||
MonitorFromWindow
|
||||
|
||||
@@ -36,9 +36,18 @@
|
||||
|
||||
<TextBlock x:Uid="ActivationSettingsHeader" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<controls:SettingsCard x:Uid="Settings_GeneralPage_ActivationKey_SettingsExpander" HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<controls:SettingsExpander
|
||||
x:Uid="Settings_GeneralPage_ActivationKey_SettingsExpander"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ptControls:ShortcutControl HotkeySettings="{x:Bind viewModel.Hotkey, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard x:Uid="Settings_GeneralPage_LowLevelHook_SettingsCard">
|
||||
<ToggleSwitch IsOn="{x:Bind viewModel.UseLowLevelGlobalHotkey, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
<controls:SettingsCard x:Uid="Settings_GeneralPage_GoHome_SettingsCard" HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind viewModel.HotkeyGoesHome, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
@@ -322,6 +322,12 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<data name="Settings_GeneralPage_ActivationKey_SettingsExpander.Description" xml:space="preserve">
|
||||
<value>This key will open the Command Palette.</value>
|
||||
</data>
|
||||
<data name="Settings_GeneralPage_LowLevelHook_SettingsCard.Header" xml:space="preserve">
|
||||
<value>Use low-level keyboard hook</value>
|
||||
</data>
|
||||
<data name="Settings_GeneralPage_LowLevelHook_SettingsCard.Description" xml:space="preserve">
|
||||
<value>Try this if there are issues with the shortcut (Command Palette might not get focus when triggered from an elevated window)</value>
|
||||
</data>
|
||||
<data name="Settings_GeneralPage_GoHome_SettingsCard.Header" xml:space="preserve">
|
||||
<value>Go home when activated</value>
|
||||
</data>
|
||||
|
||||
@@ -57,7 +57,7 @@ public sealed partial class AllAppsPage : ListPage
|
||||
Stopwatch stopwatch = new();
|
||||
stopwatch.Start();
|
||||
|
||||
List<AppItem> apps = GetPrograms();
|
||||
var apps = GetPrograms();
|
||||
|
||||
this.allAppsSection = apps
|
||||
.Select((app) => new AppListItem(app, true))
|
||||
@@ -73,26 +73,15 @@ public sealed partial class AllAppsPage : ListPage
|
||||
|
||||
internal List<AppItem> GetPrograms()
|
||||
{
|
||||
IEnumerable<AppItem> uwpResults = AppCache.Instance.Value.UWPs
|
||||
var uwpResults = AppCache.Instance.Value.UWPs
|
||||
.Where((application) => application.Enabled)
|
||||
.Select(app =>
|
||||
new AppItem()
|
||||
{
|
||||
Name = app.Name,
|
||||
Subtitle = app.Description,
|
||||
Type = UWPApplication.Type(),
|
||||
IcoPath = app.LogoType != LogoType.Error ? app.LogoPath : string.Empty,
|
||||
DirPath = app.Location,
|
||||
UserModelId = app.UserModelId,
|
||||
IsPackaged = true,
|
||||
Commands = app.GetCommands(),
|
||||
});
|
||||
.Select(UwpToAppItem);
|
||||
|
||||
IEnumerable<AppItem> win32Results = AppCache.Instance.Value.Win32s
|
||||
var win32Results = AppCache.Instance.Value.Win32s
|
||||
.Where((application) => application.Enabled && application.Valid)
|
||||
.Select(app =>
|
||||
{
|
||||
string icoPath = string.IsNullOrEmpty(app.IcoPath) ?
|
||||
var icoPath = string.IsNullOrEmpty(app.IcoPath) ?
|
||||
(app.AppType == Win32Program.ApplicationType.InternetShortcutApplication ?
|
||||
app.IcoPath :
|
||||
app.FullPath) :
|
||||
@@ -116,4 +105,21 @@ public sealed partial class AllAppsPage : ListPage
|
||||
|
||||
return uwpResults.Concat(win32Results).OrderBy(app => app.Name).ToList();
|
||||
}
|
||||
|
||||
private AppItem UwpToAppItem(UWPApplication app)
|
||||
{
|
||||
var iconPath = app.LogoType != LogoType.Error ? app.LogoPath : string.Empty;
|
||||
var item = new AppItem()
|
||||
{
|
||||
Name = app.Name,
|
||||
Subtitle = app.Description,
|
||||
Type = UWPApplication.Type(),
|
||||
IcoPath = iconPath,
|
||||
DirPath = app.Location,
|
||||
UserModelId = app.UserModelId,
|
||||
IsPackaged = true,
|
||||
Commands = app.GetCommands(),
|
||||
};
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,13 @@ internal sealed partial class AppListItem : ListItem
|
||||
Tags = [_appTag];
|
||||
MoreCommands = _app.Commands!.ToArray();
|
||||
|
||||
_details = new Lazy<Details>(() => BuildDetails());
|
||||
_details = new Lazy<Details>(() =>
|
||||
{
|
||||
var t = BuildDetails();
|
||||
t.Wait();
|
||||
return t.Result;
|
||||
});
|
||||
|
||||
_icon = new Lazy<IconInfo>(() =>
|
||||
{
|
||||
var t = FetchIcon(useThumbnails);
|
||||
@@ -41,8 +47,9 @@ internal sealed partial class AppListItem : ListItem
|
||||
});
|
||||
}
|
||||
|
||||
private Details BuildDetails()
|
||||
private async Task<Details> BuildDetails()
|
||||
{
|
||||
// Build metadata, with app type, path, etc.
|
||||
var metadata = new List<DetailsElement>();
|
||||
metadata.Add(new DetailsElement() { Key = "Type", Data = new DetailsTags() { Tags = [new Tag(_app.Type)] } });
|
||||
if (!_app.IsPackaged)
|
||||
@@ -50,10 +57,33 @@ internal sealed partial class AppListItem : ListItem
|
||||
metadata.Add(new DetailsElement() { Key = "Path", Data = new DetailsLink() { Text = _app.ExePath } });
|
||||
}
|
||||
|
||||
// Icon
|
||||
IconInfo? heroImage = null;
|
||||
if (_app.IsPackaged)
|
||||
{
|
||||
heroImage = new IconInfo(_app.IcoPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
var stream = await ThumbnailHelper.GetThumbnail(_app.ExePath, true);
|
||||
if (stream != null)
|
||||
{
|
||||
heroImage = IconInfo.FromStream(stream);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// do nothing if we fail to load an icon.
|
||||
// Logging it would be too NOISY, there's really no need.
|
||||
}
|
||||
}
|
||||
|
||||
return new Details()
|
||||
{
|
||||
Title = this.Title,
|
||||
HeroImage = this.Icon ?? new IconInfo(string.Empty),
|
||||
HeroImage = heroImage ?? this.Icon ?? new IconInfo(string.Empty),
|
||||
Metadata = metadata.ToArray(),
|
||||
};
|
||||
}
|
||||
@@ -64,11 +94,6 @@ internal sealed partial class AppListItem : ListItem
|
||||
if (_app.IsPackaged)
|
||||
{
|
||||
icon = new IconInfo(_app.IcoPath);
|
||||
if (_details.IsValueCreated)
|
||||
{
|
||||
_details.Value.HeroImage = icon;
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
@@ -94,11 +119,6 @@ internal sealed partial class AppListItem : ListItem
|
||||
icon = new IconInfo(_app.IcoPath);
|
||||
}
|
||||
|
||||
if (_details.IsValueCreated)
|
||||
{
|
||||
_details.Value.HeroImage = icon;
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,11 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Microsoft.CmdPal.Ext.Apps.Commands;
|
||||
using Microsoft.CmdPal.Ext.Apps.Properties;
|
||||
using Microsoft.CmdPal.Ext.Apps.Utils;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
|
||||
using PackageVersion = Microsoft.CmdPal.Ext.Apps.Programs.UWP.PackageVersion;
|
||||
@@ -314,7 +312,12 @@ public class UWPApplication : IProgram
|
||||
}
|
||||
}
|
||||
|
||||
var selectedIconPath = paths.FirstOrDefault(File.Exists);
|
||||
// By working from the highest resolution to the lowest, we make
|
||||
// sure that we use the highest quality possible icon for the app.
|
||||
//
|
||||
// FirstOrDefault would result in us using the 1x scaled icon
|
||||
// always, which is usually too small for our needs.
|
||||
var selectedIconPath = paths.LastOrDefault(File.Exists);
|
||||
if (!string.IsNullOrEmpty(selectedIconPath))
|
||||
{
|
||||
LogoPath = selectedIconPath;
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace Microsoft.CmdPal.Ext.WindowsServices.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open services (Ctrl+O).
|
||||
/// Looks up a localized string similar to Open services.
|
||||
/// </summary>
|
||||
internal static string wox_plugin_service_open_services {
|
||||
get {
|
||||
@@ -133,7 +133,7 @@ namespace Microsoft.CmdPal.Ext.WindowsServices.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Restart (Ctrl+R).
|
||||
/// Looks up a localized string similar to Restart.
|
||||
/// </summary>
|
||||
internal static string wox_plugin_service_restart {
|
||||
get {
|
||||
|
||||
@@ -27,4 +27,10 @@ internal sealed class NativeMethods
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern bool DestroyIcon(IntPtr hIcon);
|
||||
|
||||
[DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern int SHGetImageList(int iImageList, ref Guid riid, out IntPtr ppv);
|
||||
|
||||
[DllImport("comctl32.dll", SetLastError = true)]
|
||||
internal static extern int ImageList_GetIcon(IntPtr himl, int i, int flags);
|
||||
}
|
||||
|
||||
@@ -118,6 +118,10 @@ public sealed partial class Settings : ICommandSettings
|
||||
_settings = settings;
|
||||
Name = "Settings";
|
||||
Icon = new IconInfo("\uE713"); // Settings icon
|
||||
|
||||
// When our settings change, make sure to let CmdPal know to
|
||||
// retrieve the new forms
|
||||
_settings.SettingsChanged += (s, e) => RaiseItemsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,12 @@ public partial class SettingsForm : FormContent
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
|
||||
// Re-render the current value of the settings to a card. The
|
||||
// SettingsContentPage will raise an ItemsChanged in its own
|
||||
// SettingsChange handler, so we need to be prepared to return the
|
||||
// current settings value.
|
||||
TemplateJson = _settings.ToFormJson();
|
||||
|
||||
_settings.Update(inputs);
|
||||
_settings.RaiseSettingsChanged();
|
||||
|
||||
|
||||
@@ -24,19 +24,12 @@ public class ThumbnailHelper
|
||||
".ico",
|
||||
];
|
||||
|
||||
public static Task<IRandomAccessStream?> GetThumbnail(string path)
|
||||
public static Task<IRandomAccessStream?> GetThumbnail(string path, bool jumbo = false)
|
||||
{
|
||||
var extension = Path.GetExtension(path).ToLower(CultureInfo.InvariantCulture);
|
||||
try
|
||||
{
|
||||
if (ImageExtensions.Contains(extension))
|
||||
{
|
||||
return GetImageThumbnailAsync(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetFileIconStream(path);
|
||||
}
|
||||
return ImageExtensions.Contains(extension) ? GetImageThumbnailAsync(path) : GetFileIconStream(path, jumbo);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -45,9 +38,19 @@ public class ThumbnailHelper
|
||||
return Task.FromResult<IRandomAccessStream?>(null);
|
||||
}
|
||||
|
||||
private const uint SHGFIICON = 0x000000100;
|
||||
private const uint SHGFILARGEICON = 0x000000000;
|
||||
// these are windows constants and mangling them is goofy
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
#pragma warning disable SA1306 // Field names should begin with lower-case letter
|
||||
private const uint SHGFI_ICON = 0x000000100;
|
||||
private const uint SHGFI_SHELLICONSIZE = 0x000000004;
|
||||
private const int SHGFI_SYSICONINDEX = 0x000004000;
|
||||
private const int SHIL_JUMBO = 4;
|
||||
private const int ILD_TRANSPARENT = 1;
|
||||
#pragma warning restore SA1306 // Field names should begin with lower-case letter
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
|
||||
// This will call DestroyIcon on the hIcon passed in.
|
||||
// Duplicate it if you need it again after this.
|
||||
private static MemoryStream GetMemoryStreamFromIcon(IntPtr hIcon)
|
||||
{
|
||||
var memoryStream = new MemoryStream();
|
||||
@@ -65,19 +68,40 @@ public class ThumbnailHelper
|
||||
return memoryStream;
|
||||
}
|
||||
|
||||
private static async Task<IRandomAccessStream?> GetFileIconStream(string filePath)
|
||||
private static async Task<IRandomAccessStream?> GetFileIconStream(string filePath, bool jumbo)
|
||||
{
|
||||
var shinfo = default(NativeMethods.SHFILEINFO);
|
||||
var hr = NativeMethods.SHGetFileInfo(filePath, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFIICON | SHGFILARGEICON);
|
||||
nint hIcon = 0;
|
||||
|
||||
if (hr == 0 || shinfo.hIcon == 0)
|
||||
// If requested, look up the Jumbo icon
|
||||
if (jumbo)
|
||||
{
|
||||
hIcon = GetLargestIcon(filePath);
|
||||
}
|
||||
|
||||
// If we didn't want the JUMBO icon, or didn't find it, fall back to
|
||||
// the normal icon lookup
|
||||
if (hIcon == 0)
|
||||
{
|
||||
var shinfo = default(NativeMethods.SHFILEINFO);
|
||||
|
||||
var hr = NativeMethods.SHGetFileInfo(filePath, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_SHELLICONSIZE);
|
||||
|
||||
if (hr == 0 || shinfo.hIcon == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
hIcon = shinfo.hIcon;
|
||||
}
|
||||
|
||||
if (hIcon == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var stream = new InMemoryRandomAccessStream();
|
||||
|
||||
using var memoryStream = GetMemoryStreamFromIcon(shinfo.hIcon);
|
||||
using var memoryStream = GetMemoryStreamFromIcon(hIcon); // this will DestroyIcon hIcon
|
||||
using var outputStream = stream.GetOutputStreamAt(0);
|
||||
using (var dataWriter = new DataWriter(outputStream))
|
||||
{
|
||||
@@ -95,4 +119,21 @@ public class ThumbnailHelper
|
||||
var thumbnail = await file.GetThumbnailAsync(ThumbnailMode.PicturesView);
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
private static nint GetLargestIcon(string path)
|
||||
{
|
||||
var shinfo = default(NativeMethods.SHFILEINFO);
|
||||
NativeMethods.SHGetFileInfo(path, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_SYSICONINDEX);
|
||||
|
||||
var hIcon = IntPtr.Zero;
|
||||
var iID_IImageList = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950");
|
||||
IntPtr imageListPtr;
|
||||
|
||||
if (NativeMethods.SHGetImageList(SHIL_JUMBO, ref iID_IImageList, out imageListPtr) == 0 && imageListPtr != IntPtr.Zero)
|
||||
{
|
||||
hIcon = NativeMethods.ImageList_GetIcon(imageListPtr, shinfo.iIcon, ILD_TRANSPARENT);
|
||||
}
|
||||
|
||||
return hIcon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,9 +185,10 @@ namespace PowerLauncher.Plugin
|
||||
|
||||
if (!failedPlugins.IsEmpty)
|
||||
{
|
||||
var failed = string.Join(", ", failedPlugins.Select(x => x.Metadata.Name));
|
||||
string title = Resources.FailedToInitializePluginsTitle.ToString().Replace("{0}", Constant.Version);
|
||||
var failed = string.Join(", ", failedPlugins.Select(x => $"{x.Metadata.Name} ({x.Metadata.ExecuteFileVersion})"));
|
||||
var description = $"{string.Format(CultureInfo.CurrentCulture, FailedToInitializePluginsDescription, failed)}\n\n{Resources.FailedToInitializePluginsDescriptionPartTwo}";
|
||||
Application.Current.Dispatcher.InvokeAsync(() => API.ShowMsg(Resources.FailedToInitializePluginsTitle, description, string.Empty, false));
|
||||
Application.Current.Dispatcher.InvokeAsync(() => API.ShowMsg(title, description, string.Empty, false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace PowerLauncher.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to PowerToys Run - Plugin Initialization Error.
|
||||
/// Looks up a localized string similar to PowerToys Run {0} - Plugin Initialization Error.
|
||||
/// </summary>
|
||||
public static string FailedToInitializePluginsTitle {
|
||||
get {
|
||||
|
||||
@@ -189,7 +189,7 @@
|
||||
<value>Fail to initialize plugins: {0}</value>
|
||||
</data>
|
||||
<data name="FailedToInitializePluginsTitle" xml:space="preserve">
|
||||
<value>PowerToys Run - Plugin Initialization Error</value>
|
||||
<value>PowerToys Run {0} - Plugin Initialization Error</value>
|
||||
<comment>Don't translate "PowerToys Run". This is a product name.</comment>
|
||||
</data>
|
||||
<data name="ContextMenuItemsAvailable" xml:space="preserve">
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
@@ -252,7 +251,7 @@ namespace PowerLauncher
|
||||
Id = x.Metadata.ID,
|
||||
Name = x.Plugin == null ? x.Metadata.Name : x.Plugin.Name,
|
||||
Description = x.Plugin?.Description,
|
||||
Version = FileVersionInfo.GetVersionInfo(x.Metadata.ExecuteFilePath).FileVersion,
|
||||
Version = x.Metadata.ExecuteFileVersion,
|
||||
Author = x.Metadata.Author,
|
||||
Website = x.Metadata.Website,
|
||||
Disabled = x.Metadata.Disabled,
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
@@ -42,6 +44,9 @@ namespace Wox.Plugin
|
||||
|
||||
public string ExecuteFileName { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string ExecuteFileVersion { get; private set; }
|
||||
|
||||
public string PluginDirectory
|
||||
{
|
||||
get
|
||||
@@ -53,6 +58,7 @@ namespace Wox.Plugin
|
||||
{
|
||||
_pluginDirectory = value;
|
||||
ExecuteFilePath = Path.Combine(value, ExecuteFileName);
|
||||
SetExecutableVersion();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,5 +90,19 @@ namespace Wox.Plugin
|
||||
|
||||
[JsonIgnore]
|
||||
public int QueryCount { get; set; }
|
||||
|
||||
private void SetExecutableVersion()
|
||||
{
|
||||
// Using version from plugin metadata json as fallback
|
||||
try
|
||||
{
|
||||
var v = FileVersionInfo.GetVersionInfo(ExecuteFilePath).FileVersion;
|
||||
ExecuteFileVersion = (v is null or "0.0.0.0") ? Version : v;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
ExecuteFileVersion = Version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +82,9 @@ namespace Wox.Plugin
|
||||
|
||||
if (!IsPluginInitialized)
|
||||
{
|
||||
string description = $"{Resources.FailedToLoadPluginDescription} {Metadata.Name}\n\n{Resources.FailedToLoadPluginDescriptionPartTwo}";
|
||||
Application.Current.Dispatcher.InvokeAsync(() => api.ShowMsg(Resources.FailedToLoadPluginTitle, description, string.Empty, false));
|
||||
string title = Resources.FailedToLoadPluginTitle.ToString().Replace("{0}", Constant.Version);
|
||||
string description = $"{Resources.FailedToLoadPluginDescription} {Metadata.Name} ({Metadata.ExecuteFileVersion})\n\n{Resources.FailedToLoadPluginDescriptionPartTwo}";
|
||||
Application.Current.Dispatcher.InvokeAsync(() => api.ShowMsg(title, description, string.Empty, false));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Wox.Plugin.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to PowerToys Run - Plugin Loading Error.
|
||||
/// Looks up a localized string similar to PowerToys Run {0} - Plugin Loading Error.
|
||||
/// </summary>
|
||||
public static string FailedToLoadPluginTitle {
|
||||
get {
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
<comment>"https://aka.ms/powerToysReportBug" is a web uri.</comment>
|
||||
</data>
|
||||
<data name="FailedToLoadPluginTitle" xml:space="preserve">
|
||||
<value>PowerToys Run - Plugin Loading Error</value>
|
||||
<value>PowerToys Run {0} - Plugin Loading Error</value>
|
||||
<comment>Don't translate "PowerToys Run". This is a product name.</comment>
|
||||
</data>
|
||||
<data name="VirtualDesktopHelper_AllDesktops" xml:space="preserve">
|
||||
|
||||
@@ -169,6 +169,14 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation
|
||||
|
||||
if (std::find(letters.begin(), letters.end(), letterKey) != cend(letters) && m_isLanguageLetterCb(letterKey))
|
||||
{
|
||||
if (m_toolbarVisible && letterPressed == letterKey)
|
||||
{
|
||||
// On-screen keyboard continuously sends WM_KEYDOWN when a key is held down
|
||||
// If Quick Accent is visible, prevent the letter key from being processed
|
||||
// https://github.com/microsoft/PowerToys/issues/36853
|
||||
return true;
|
||||
}
|
||||
|
||||
m_stopwatch.reset();
|
||||
letterPressed = letterKey;
|
||||
}
|
||||
@@ -282,12 +290,11 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation
|
||||
|
||||
LRESULT KeyboardListener::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (nCode == HC_ACTION && s_instance != nullptr)
|
||||
{
|
||||
if (nCode == HC_ACTION && s_instance != nullptr)
|
||||
KBDLLHOOKSTRUCT* key = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||
switch (wParam)
|
||||
{
|
||||
KBDLLHOOKSTRUCT* key = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||
switch (wParam)
|
||||
{
|
||||
case WM_KEYDOWN:
|
||||
{
|
||||
if (s_instance->OnKeyDown(*key))
|
||||
@@ -304,10 +311,9 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"configVersion": 3,
|
||||
"entries": [
|
||||
{
|
||||
"Fuzzer": {
|
||||
"$type": "libfuzzer",
|
||||
"FuzzingHarnessExecutableName": "PowerRename.FuzzingTest.exe"
|
||||
},
|
||||
"adoTemplate": {
|
||||
// supply the values appropriate to your
|
||||
// project, where bugs will be filed
|
||||
"org": "microsoft",
|
||||
"project": "OS",
|
||||
"AssignedTo": "leilzh@microsoft.com",
|
||||
"AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\DIVE\\SALT",
|
||||
"IterationPath": "OS\\Future"
|
||||
},
|
||||
"jobNotificationEmail": "PowerToys@microsoft.com",
|
||||
"skip": false,
|
||||
"rebootAfterSetup": false,
|
||||
"oneFuzzJobs": [
|
||||
// at least one job is required
|
||||
{
|
||||
"projectName": "PowerToys.PowerRename",
|
||||
"targetName": "PowerRename_Fuzzer"
|
||||
}
|
||||
],
|
||||
"jobDependencies": [
|
||||
// this should contain, at minimum,
|
||||
// the DLL and PDB files
|
||||
// you will need to add any other files required
|
||||
// (globs are supported)
|
||||
"PowerRename.FuzzingTest.exe",
|
||||
"PowerRename.FuzzingTest.pdb",
|
||||
"PowerRename.FuzzingTest.lib",
|
||||
"clang_rt.asan_dynamic-x86_64.dll"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Test.cpp : This file contains the 'main' function. Program execution begins and ends there.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <PowerRenameRegEx.h>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
if (size < 6)
|
||||
return 0;
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
size_t input_len = size / 3;
|
||||
size_t find_len = size / 3;
|
||||
size_t replace_len = size - input_len - find_len;
|
||||
|
||||
auto read_wstring = [&](size_t len) -> std::wstring {
|
||||
std::wstring result;
|
||||
if (offset + len > size)
|
||||
len = size - offset;
|
||||
|
||||
result.assign(reinterpret_cast<const wchar_t*>(data + offset), len / sizeof(wchar_t));
|
||||
offset += len;
|
||||
return result;
|
||||
};
|
||||
|
||||
std::wstring input = read_wstring(input_len);
|
||||
std::wstring find = read_wstring(find_len);
|
||||
std::wstring replace = read_wstring(replace_len);
|
||||
|
||||
if (find.empty() || replace.empty())
|
||||
return 0;
|
||||
|
||||
CComPtr<IPowerRenameRegEx> renamer;
|
||||
CPowerRenameRegEx::s_CreateInstance(&renamer);
|
||||
|
||||
renamer->PutFlags(UseRegularExpressions | CaseSensitive);
|
||||
|
||||
renamer->PutSearchTerm(find.c_str());
|
||||
renamer->PutReplaceTerm(replace.c_str());
|
||||
|
||||
PWSTR result = nullptr;
|
||||
unsigned long index = 0;
|
||||
HRESULT hr = renamer->Replace(input.c_str(), &result, index);
|
||||
if (SUCCEEDED(hr) && result != nullptr)
|
||||
{
|
||||
CoTaskMemFree(result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_FOR_FUZZING
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char8_t raw[] = u8"test_string";
|
||||
|
||||
std::vector<uint8_t> data(reinterpret_cast<const uint8_t*>(raw), reinterpret_cast<const uint8_t*>(raw) + sizeof(raw) - 1);
|
||||
|
||||
LLVMFuzzerTestOneInput(data.data(), data.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
<?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="PowerRename.FuzzingTest.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CopyFileToFolders Include="OneFuzzConfig.json" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,107 @@
|
||||
<?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.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{2694e2fb-dcd5-4bff-a418-b6c3c7ce3b8e}</ProjectGuid>
|
||||
<RootNamespace>Test</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.22621.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>PowerRename.FuzzingTest</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<EnableASAN>true</EnableASAN>
|
||||
<EnableFuzzer>true</EnableFuzzer>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup 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>
|
||||
<LibraryPath>$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(VCToolsInstallDir)\lib\$(Platform)</LibraryPath>
|
||||
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\tests\PowerRename.FuzzTests\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;DISABLE_FOR_FUZZING;%(PreprocessorDefinitions);_DISABLE_VECTOR_ANNOTATION;_DISABLE_STRING_ANNOTATION</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<AdditionalOptions>/fsanitize=address /fsanitize-coverage=inline-8bit-counters /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div %(AdditionalOptions)</AdditionalOptions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>..\;..\lib\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>legacy_stdio_definitions.lib;$(VCToolsInstallDir)lib\$(Platform)\libsancov.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /y "$(VCToolsInstallDir)bin\Hostx64\x64\clang_rt.asan_dynamic-x86_64.dll" "$(OutDir)"</Command>
|
||||
<Message>Copy the required ASan runtime DLL to the output directory.</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>..\;..\lib\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>$(OutDir)\..\..\WinUI3Apps\PowerRenameLib.lib;comctl32.lib;pathcch.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="PowerRename.FuzzingTest.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CopyFileToFolders Include="OneFuzzConfig.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\lib\PowerRenameLib.vcxproj" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Project>{51920f1f-c28c-4adf-8660-4238766796c2}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost.1.84.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.84.0\build\boost.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.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.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost.1.84.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.84.0\build\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -204,12 +204,12 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost.1.84.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.84.0\build\boost.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.250205002\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.250205002\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost.1.87.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -217,14 +217,14 @@
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost.1.84.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.84.0\build\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.250205002\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.250205002\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.250205002\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.250205002\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.87.0\build\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AddWildCardItems" AfterTargets="BuildGenerateSources">
|
||||
<ItemGroup>
|
||||
|
||||
@@ -195,8 +195,15 @@ namespace winrt::PowerRenameUI::implementation
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$DDD", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_DayNameAbbr").ValueAsString()));
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$DD", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_DayDigitLZero").ValueAsString()));
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$D", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_DayDigit").ValueAsString()));
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$hh", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_HoursLZero").ValueAsString()));
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$h", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_Hours").ValueAsString()));
|
||||
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$HH", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_Hours12LZero").ValueAsString()));
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$H", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_Hours12").ValueAsString()));
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$TT", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_AMPMUpperCase").ValueAsString()));
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$tt", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_AMPMLowerCase").ValueAsString()));
|
||||
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$hh", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_Hours24LZero").ValueAsString()));
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$h", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_Hours24").ValueAsString()));
|
||||
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$mm", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_MinutesLZero").ValueAsString()));
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$m", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_Minutes").ValueAsString()));
|
||||
m_dateTimeShortcuts.Append(winrt::make<PatternSnippet>(L"$ss", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_SecondsLZero").ValueAsString()));
|
||||
|
||||
@@ -219,11 +219,23 @@
|
||||
<data name="DateTimeCheatSheet_DayDigit" xml:space="preserve">
|
||||
<value>Day of the month as digits without leading zeros for single-digit days.</value>
|
||||
</data>
|
||||
<data name="DateTimeCheatSheet_HoursLZero" xml:space="preserve">
|
||||
<value>Hours with leading zeros for single-digit hours.</value>
|
||||
<data name="DateTimeCheatSheet_Hours12LZero" xml:space="preserve">
|
||||
<value>Hours in 12-hour format (01-12) with leading zero.</value>
|
||||
</data>
|
||||
<data name="DateTimeCheatSheet_Hours" xml:space="preserve">
|
||||
<value>Hours without leading zeros for single-digit hours.</value>
|
||||
<data name="DateTimeCheatSheet_Hours12" xml:space="preserve">
|
||||
<value>Hours in 12-hour format (1-12) without leading zero.</value>
|
||||
</data>
|
||||
<data name="DateTimeCheatSheet_AMPMUpperCase" xml:space="preserve">
|
||||
<value>AM/PM indicator in uppercase (AM or PM).</value>
|
||||
</data>
|
||||
<data name="DateTimeCheatSheet_AMPMLowerCase" xml:space="preserve">
|
||||
<value>AM/PM indicator in lowercase (am or pm).</value>
|
||||
</data>
|
||||
<data name="DateTimeCheatSheet_Hours24LZero" xml:space="preserve">
|
||||
<value>Hours in 24-hour format (00-23) with leading zero.</value>
|
||||
</data>
|
||||
<data name="DateTimeCheatSheet_Hours24" xml:space="preserve">
|
||||
<value>Hours in 24-hour format (0-23) without leading zero.</value>
|
||||
</data>
|
||||
<data name="DateTimeCheatSheet_MinutesLZero" xml:space="preserve">
|
||||
<value>Minutes with leading zeros for single-digit minutes.</value>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="boost" version="1.84.0" targetFramework="native" />
|
||||
<package id="boost_regex-vc143" version="1.84.0" targetFramework="native" />
|
||||
<package id="boost" version="1.87.0" targetFramework="native" />
|
||||
<package id="boost_regex-vc143" version="1.87.0" targetFramework="native" />
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
|
||||
@@ -77,8 +77,8 @@
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost.1.84.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.84.0\build\boost.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost.1.87.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -86,7 +86,7 @@
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost.1.84.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.84.0\build\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.87.0\build\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="boost" version="1.84.0" targetFramework="native" />
|
||||
<package id="boost_regex-vc143" version="1.84.0" targetFramework="native" />
|
||||
<package id="boost" version="1.87.0" targetFramework="native" />
|
||||
<package id="boost_regex-vc143" version="1.87.0" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -248,7 +248,19 @@ HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR sour
|
||||
bool isFileTimeUsed(_In_ PCWSTR source)
|
||||
{
|
||||
bool used = false;
|
||||
static const std::array patterns = { std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$Y" }, std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$M" }, std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$D" }, std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$h" }, std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$m" }, std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$s" }, std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$f" } };
|
||||
static const std::array patterns = {
|
||||
std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$Y" },
|
||||
std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$M" },
|
||||
std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$D" },
|
||||
std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$h" },
|
||||
std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$m" },
|
||||
std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$s" },
|
||||
std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$f" },
|
||||
std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$H" },
|
||||
std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$T" },
|
||||
std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$t" }
|
||||
};
|
||||
|
||||
for (size_t i = 0; !used && i < patterns.size(); i++)
|
||||
{
|
||||
if (std::regex_search(source, patterns[i]))
|
||||
@@ -275,6 +287,12 @@ HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SY
|
||||
StringCchCopy(localeName, LOCALE_NAME_MAX_LENGTH, L"en_US");
|
||||
}
|
||||
|
||||
int hour12 = (fileTime.wHour % 12);
|
||||
if (hour12 == 0)
|
||||
{
|
||||
hour12 = 12;
|
||||
}
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%04d"), L"$01", fileTime.wYear);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$YYYY"), replaceTerm);
|
||||
|
||||
@@ -316,6 +334,18 @@ HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SY
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wDay);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$D"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", hour12);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$HH"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", hour12);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$H"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", (fileTime.wHour < 12) ? L"AM" : L"PM");
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$TT"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", (fileTime.wHour < 12) ? L"am" : L"pm");
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$tt"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wHour);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$hh"), replaceTerm);
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost.1.84.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.84.0\build\boost.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost.1.87.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -88,7 +88,7 @@
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost.1.84.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.84.0\build\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.87.0\build\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="boost" version="1.84.0" targetFramework="native" />
|
||||
<package id="boost_regex-vc143" version="1.84.0" targetFramework="native" />
|
||||
<package id="boost" version="1.87.0" targetFramework="native" />
|
||||
<package id="boost_regex-vc143" version="1.87.0" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -67,8 +67,8 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost.1.84.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.84.0\build\boost.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost.1.87.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -76,7 +76,7 @@
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost.1.84.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.84.0\build\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.87.0\build\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="boost" version="1.84.0" targetFramework="native" />
|
||||
<package id="boost_regex-vc143" version="1.84.0" targetFramework="native" />
|
||||
<package id="boost" version="1.87.0" targetFramework="native" />
|
||||
<package id="boost_regex-vc143" version="1.87.0" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -77,8 +77,8 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost.1.84.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.84.0\build\boost.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost.1.87.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -86,7 +86,7 @@
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost.1.84.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.84.0\build\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.84.0\build\boost_regex-vc143.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.87.0\build\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -127,5 +127,53 @@ TEST_METHOD(VerifyLookbehind)
|
||||
CoTaskMemFree(result);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD (Verify12and24HourTimeFormats)
|
||||
{
|
||||
CComPtr<IPowerRenameRegEx> renameRegEx;
|
||||
Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
|
||||
DWORD flags = MatchAllOccurrences | UseRegularExpressions;
|
||||
Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
|
||||
|
||||
struct TimeTestCase {
|
||||
SYSTEMTIME time; // Input time
|
||||
PCWSTR formatString; // Format pattern
|
||||
PCWSTR expectedResult; // Expected output
|
||||
PCWSTR description; // Description of what we're testing
|
||||
};
|
||||
|
||||
struct TimeTestCase testCases[] = {
|
||||
// Midnight (00:00 / 12:00 AM)
|
||||
{ { 2025, 4, 4, 10, 0, 0, 0, 0 }, L"[$hh:$mm] [$H:$mm $tt]", L"[00:00] [12:00 am]", L"Midnight formatting" },
|
||||
|
||||
// Noon (12:00 / 12:00 PM)
|
||||
{ { 2025, 4, 4, 10, 12, 0, 0, 0 }, L"[$hh:$mm] [$H:$mm $tt]", L"[12:00] [12:00 pm]", L"Noon formatting" },
|
||||
|
||||
// 1:05 AM
|
||||
{ { 2025, 4, 4, 10, 1, 5, 0, 0 }, L"[$h:$m] [$H:$m $tt] [$hh:$mm] [$HH:$mm $TT]",
|
||||
L"[1:5] [1:5 am] [01:05] [01:05 AM]", L"1 AM with various formats" },
|
||||
|
||||
// 11 PM
|
||||
{ { 2025, 4, 4, 10, 23, 45, 0, 0 }, L"[$h:$m] [$H:$m $tt] [$hh:$mm] [$HH:$mm $TT]",
|
||||
L"[23:45] [11:45 pm] [23:45] [11:45 PM]", L"11 PM with various formats" },
|
||||
|
||||
// Mixed formats in complex pattern
|
||||
{ { 2025, 4, 4, 10, 14, 30, 0, 0 }, L"Date: $YYYY-$MM-$DD Time: $hh:$mm (24h) / $H:$mm $tt (12h)",
|
||||
L"Date: 2025-04-10 Time: 14:30 (24h) / 2:30 pm (12h)", L"Complex combined format" },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(testCases); i++)
|
||||
{
|
||||
PWSTR result = nullptr;
|
||||
Assert::IsTrue(renameRegEx->PutSearchTerm(L"test") == S_OK);
|
||||
Assert::IsTrue(renameRegEx->PutReplaceTerm(testCases[i].formatString) == S_OK);
|
||||
Assert::IsTrue(renameRegEx->PutFileTime(testCases[i].time) == S_OK);
|
||||
unsigned long index = {};
|
||||
Assert::IsTrue(renameRegEx->Replace(L"test", &result, index) == S_OK);
|
||||
Assert::IsTrue(wcscmp(result, testCases[i].expectedResult) == 0,
|
||||
(std::wstring(L"Failed test case: ") + testCases[i].description).c_str());
|
||||
CoTaskMemFree(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -207,5 +207,53 @@ TEST_METHOD(VerifyLookbehindFails)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD (Verify12and24HourTimeFormats)
|
||||
{
|
||||
CComPtr<IPowerRenameRegEx> renameRegEx;
|
||||
Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
|
||||
DWORD flags = MatchAllOccurrences | UseRegularExpressions;
|
||||
Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
|
||||
|
||||
struct TimeTestCase {
|
||||
SYSTEMTIME time; // Input time
|
||||
PCWSTR formatString; // Format pattern
|
||||
PCWSTR expectedResult; // Expected output
|
||||
PCWSTR description; // Description of what we're testing
|
||||
};
|
||||
|
||||
struct TimeTestCase testCases[] = {
|
||||
// Midnight (00:00 / 12:00 AM)
|
||||
{ { 2025, 4, 4, 10, 0, 0, 0, 0 }, L"[$hh:$mm] [$H:$mm $tt]", L"[00:00] [12:00 am]", L"Midnight formatting" },
|
||||
|
||||
// Noon (12:00 / 12:00 PM)
|
||||
{ { 2025, 4, 4, 10, 12, 0, 0, 0 }, L"[$hh:$mm] [$H:$mm $tt]", L"[12:00] [12:00 pm]", L"Noon formatting" },
|
||||
|
||||
// 1:05 AM
|
||||
{ { 2025, 4, 4, 10, 1, 5, 0, 0 }, L"[$h:$m] [$H:$m $tt] [$hh:$mm] [$HH:$mm $TT]",
|
||||
L"[1:5] [1:5 am] [01:05] [01:05 AM]", L"1 AM with various formats" },
|
||||
|
||||
// 11 PM
|
||||
{ { 2025, 4, 4, 10, 23, 45, 0, 0 }, L"[$h:$m] [$H:$m $tt] [$hh:$mm] [$HH:$mm $TT]",
|
||||
L"[23:45] [11:45 pm] [23:45] [11:45 PM]", L"11 PM with various formats" },
|
||||
|
||||
// Mixed formats in complex pattern
|
||||
{ { 2025, 4, 4, 10, 14, 30, 0, 0 }, L"Date: $YYYY-$MM-$DD Time: $hh:$mm (24h) / $H:$mm $tt (12h)",
|
||||
L"Date: 2025-04-10 Time: 14:30 (24h) / 2:30 pm (12h)", L"Complex combined format" },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(testCases); i++)
|
||||
{
|
||||
PWSTR result = nullptr;
|
||||
Assert::IsTrue(renameRegEx->PutSearchTerm(L"test") == S_OK);
|
||||
Assert::IsTrue(renameRegEx->PutReplaceTerm(testCases[i].formatString) == S_OK);
|
||||
Assert::IsTrue(renameRegEx->PutFileTime(testCases[i].time) == S_OK);
|
||||
unsigned long index = {};
|
||||
Assert::IsTrue(renameRegEx->Replace(L"test", &result, index) == S_OK);
|
||||
Assert::IsTrue(wcscmp(result, testCases[i].expectedResult) == 0,
|
||||
(std::wstring(L"Failed test case: ") + testCases[i].description).c_str());
|
||||
CoTaskMemFree(result);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="boost" version="1.84.0" targetFramework="native" />
|
||||
<package id="boost_regex-vc143" version="1.84.0" targetFramework="native" />
|
||||
<package id="boost" version="1.87.0" targetFramework="native" />
|
||||
<package id="boost_regex-vc143" version="1.87.0" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -4,10 +4,17 @@ Param(
|
||||
[string]$scriptPath
|
||||
)
|
||||
|
||||
Write-Host "Enabling experimental feature: PSFeedbackProvider"
|
||||
Enable-ExperimentalFeature PSFeedbackProvider
|
||||
Write-Host "Enabling experimental feature: PSCommandNotFoundSuggestion"
|
||||
Enable-ExperimentalFeature PSCommandNotFoundSuggestion
|
||||
$experimentalFeatures = Get-ExperimentalFeature;
|
||||
if ($experimentalFeatures.Name -contains "PSFeedbackProvider")
|
||||
{
|
||||
Write-Host "Enabling experimental feature: PSFeedbackProvider"
|
||||
Enable-ExperimentalFeature PSFeedbackProvider
|
||||
}
|
||||
if ($experimentalFeatures.Name -contains "PSCommandNotFoundSuggestion")
|
||||
{
|
||||
Write-Host "Enabling experimental feature: PSCommandNotFoundSuggestion"
|
||||
Enable-ExperimentalFeature PSCommandNotFoundSuggestion
|
||||
}
|
||||
|
||||
$wingetModules = Get-Module -ListAvailable -Name Microsoft.WinGet.Client
|
||||
if ($wingetModules) {
|
||||
|
||||
179
tools/build/self-sign.ps1
Normal file
179
tools/build/self-sign.ps1
Normal file
@@ -0,0 +1,179 @@
|
||||
#https://learn.microsoft.com/en-us/windows/msix/package/signing-known-issues
|
||||
# 1. Build the powertoys as usual.
|
||||
# 2. Call this script to sign the msix package.
|
||||
# First time run needs admin permission to trust the certificate.
|
||||
|
||||
param (
|
||||
[string]$architecture = "x64", # Default to x64 if not provided
|
||||
[string]$buildConfiguration = "Debug" # Default to Debug if not provided
|
||||
)
|
||||
|
||||
$signToolPath = $null
|
||||
$kitsRootPaths = @(
|
||||
"C:\Program Files (x86)\Windows Kits\10\bin",
|
||||
"C:\Program Files\Windows Kits\10\bin"
|
||||
)
|
||||
|
||||
$signToolAvailable = Get-Command "signtool" -ErrorAction SilentlyContinue
|
||||
if ($signToolAvailable) {
|
||||
Write-Host "SignTool is available in the system PATH."
|
||||
$signToolPath = "signtool"
|
||||
}
|
||||
else {
|
||||
Write-Host "Searching for latest SignTool matching architecture: $architecture"
|
||||
|
||||
foreach ($root in $kitsRootPaths) {
|
||||
if (Test-Path $root) {
|
||||
$versions = Get-ChildItem -Path $root -Directory | Where-Object {
|
||||
$_.Name -match '^\d+\.\d+\.\d+\.\d+$'
|
||||
} | Sort-Object Name -Descending
|
||||
|
||||
foreach ($version in $versions) {
|
||||
$candidatePath = Join-Path -Path $version.FullName -ChildPath "x86"
|
||||
$exePath = Join-Path -Path $candidatePath -ChildPath "signtool.exe"
|
||||
if (Test-Path $exePath) {
|
||||
Write-Host "Found SignTool at: $exePath"
|
||||
$signToolPath = $exePath
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ($signToolPath) { break }
|
||||
}
|
||||
}
|
||||
|
||||
if (!$signToolPath) {
|
||||
Write-Host "SignTool not found. Please ensure Windows SDK is installed."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "`nUsing SignTool: $signToolPath"
|
||||
|
||||
# Set the certificate subject and the ECDSA curve
|
||||
$certSubject = "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
|
||||
|
||||
# Check if the certificate already exists in the current user's certificate store
|
||||
$existingCert = Get-ChildItem -Path Cert:\CurrentUser\My |
|
||||
Where-Object { $_.Subject -eq $certSubject } |
|
||||
Sort-Object NotAfter -Descending |
|
||||
Select-Object -First 1
|
||||
|
||||
if ($existingCert) {
|
||||
# If the certificate exists, use the existing certificate
|
||||
Write-Host "Certificate already exists, using the existing certificate"
|
||||
$cert = $existingCert
|
||||
}
|
||||
else {
|
||||
# If the certificate doesn't exist, create a new self-signed certificate
|
||||
Write-Host "Certificate does not exist, creating a new certificate..."
|
||||
$cert = New-SelfSignedCertificate -Subject $certSubject `
|
||||
-CertStoreLocation "Cert:\CurrentUser\My" `
|
||||
-KeyAlgorithm RSA `
|
||||
-Type CodeSigningCert `
|
||||
-HashAlgorithm SHA256
|
||||
}
|
||||
|
||||
function Import-And-VerifyCertificate {
|
||||
param (
|
||||
[string]$cerPath,
|
||||
[string]$storePath
|
||||
)
|
||||
|
||||
$thumbprint = (Get-PfxCertificate -FilePath $cerPath).Thumbprint
|
||||
|
||||
# ✅ Step 1: Check if already exists in store
|
||||
$existingCert = Get-ChildItem -Path $storePath | Where-Object { $_.Thumbprint -eq $thumbprint }
|
||||
|
||||
if ($existingCert) {
|
||||
Write-Host "✅ Certificate already exists in $storePath"
|
||||
return $true
|
||||
}
|
||||
|
||||
# 🚀 Step 2: Try to import if not already there
|
||||
try {
|
||||
$null = Import-Certificate -FilePath $cerPath -CertStoreLocation $storePath -ErrorAction Stop
|
||||
}
|
||||
catch {
|
||||
Write-Warning "❌ Failed to import certificate to $storePath : $_"
|
||||
return $false
|
||||
}
|
||||
|
||||
# 🔁 Step 3: Verify again
|
||||
$imported = Get-ChildItem -Path $storePath | Where-Object { $_.Thumbprint -eq $thumbprint }
|
||||
|
||||
if ($imported) {
|
||||
Write-Host "✅ Certificate successfully imported to $storePath"
|
||||
return $true
|
||||
}
|
||||
else {
|
||||
Write-Warning "❌ Certificate not found in $storePath after import"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
$cerPath = "$env:TEMP\temp_cert.cer"
|
||||
Export-Certificate -Cert $cert -FilePath $cerPath -Force
|
||||
# used for sign code/msix
|
||||
# CurrentUser\TrustedPeople
|
||||
if (-not (Import-And-VerifyCertificate -cerPath $cerPath -storePath "Cert:\CurrentUser\TrustedPeople")) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# CurrentUser\Root
|
||||
if (-not (Import-And-VerifyCertificate -cerPath $cerPath -storePath "Cert:\CurrentUser\Root")) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# LocalMachine\Root
|
||||
if (-not (Import-And-VerifyCertificate -cerPath $cerPath -storePath "Cert:\LocalMachine\Root")) {
|
||||
Write-Warning "⚠️ Failed to import to LocalMachine\Root (admin may be required)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
# Output the thumbprint of the certificate (to confirm which certificate is being used)
|
||||
Write-Host "Using certificate with thumbprint: $($cert.Thumbprint)"
|
||||
|
||||
|
||||
$rootDirectory = (Split-Path -Parent(Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)))
|
||||
|
||||
# Dynamically build the directory path based on architecture and build configuration
|
||||
# $directoryPath = Join-Path $rootDirectory "$architecture\$buildConfiguration\WinUI3Apps\CmdPal\"
|
||||
$directoryPath = Join-Path $rootDirectory "$architecture\$buildConfiguration\WinUI3Apps\CmdPal\"
|
||||
|
||||
if (-not (Test-Path $directoryPath)) {
|
||||
Write-Error "Path to search for msix files does not exist: $directoryPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Directory path to search for .msix and .appx files: $directoryPath"
|
||||
|
||||
# Get all .msix and .appx files from the specified directory
|
||||
$filePaths = Get-ChildItem -Path $directoryPath -Recurse | Where-Object {
|
||||
($_.Extension -eq ".msix" -or $_.Extension -eq ".appx") -and
|
||||
($_.Name -like "*$architecture*")
|
||||
}
|
||||
|
||||
if ($filePaths.Count -eq 0) {
|
||||
Write-Host "No .msix or .appx files found in the directory."
|
||||
}
|
||||
else {
|
||||
# Iterate through each file and sign it
|
||||
foreach ($file in $filePaths) {
|
||||
Write-Host "Signing file: $($file.FullName)"
|
||||
|
||||
# Use SignTool to sign the file
|
||||
$signToolCommand = "& `"$signToolPath`" sign /sha1 $($cert.Thumbprint) /fd SHA256 /t http://timestamp.digicert.com `"$($file.FullName)`""
|
||||
|
||||
# Execute the sign command
|
||||
try {
|
||||
Invoke-Expression $signToolCommand
|
||||
}
|
||||
catch {
|
||||
Write-Host "Error signing file $($file.Name): $_"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Signing process completed."
|
||||
Reference in New Issue
Block a user