Skip to content

Commit f23fa3f

Browse files
[DSC] Implement Microsoft.PowerToys.Configure DSCResource & winget support (#30918)
* [DSC] Microsoft.PowerToys.Configure module + winget configuration file support * f: fix for an incorrect directory id reference * f: update comment * f: address review comments * f: file locksmith bug fix * f: add explorer preview switches in samples * f: remove debug * Sign DSC files * f: implement docs/samples generator * [ci]Sign FancyZonesEditorCommon.dll * Sign DSC files in the Generated folder * f: address review comments * f: update usable options * f: add autogenerated sample * [Installer] Don't use same GUID for different components * [Installer]Don't remove folders shared by other modules * Allow configuring PTRun MaximumNumberOfResults * Remove all settings DSC sample. Just random data * Allow configuring Hosts Run as Administrator * Revert "[Installer]Don't remove folders shared by other modules" This reverts commit 6da3d6c. * Add all PTRun plugins and Global and keyboard to DSC sample * Fix issues with context menu modules not disabling * Fix default enabled values when setting with DSC * Fix tests regarding default modules in Settings * Fix merge error * Restart PowerToys process if we stopped it --------- Co-authored-by: Andrey Nekrasov <[email protected]> Co-authored-by: Jaime Bernardo <[email protected]>
1 parent 818d3e3 commit f23fa3f

File tree

81 files changed

+2596
-253
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+2596
-253
lines changed

.github/actions/spell-check/expect.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,7 @@ NOSIZE
10291029
NOTIFICATIONSDLL
10301030
NOTIFYICONDATAW
10311031
NOTIMPL
1032+
notlike
10321033
NOTOPMOST
10331034
NOTRACK
10341035
NOTSRCCOPY

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ bld/
3333
# Visual Studio 2017 auto generated files
3434
Generated\ Files/
3535

36+
Generated/
37+
3638
# MSTest test Results
3739
[Tt]est[Rr]esult*/
3840
[Bb]uild[Ll]og.*

.pipelines/ESRPSigning_DSC.json

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"Version": "1.0.0",
3+
"UseMinimatch": false,
4+
"SignBatches": [
5+
{
6+
"MatchedPath": [
7+
"Microsoft.PowerToys.Configure.psm1",
8+
"Microsoft.PowerToys.Configure.psd1"
9+
],
10+
"SigningInfo": {
11+
"Operations": [
12+
{
13+
"KeyCode": "CP-230012",
14+
"OperationSetCode": "SigntoolSign",
15+
"Parameters": [
16+
{
17+
"parameterName": "OpusName",
18+
"parameterValue": "Microsoft"
19+
},
20+
{
21+
"parameterName": "OpusInfo",
22+
"parameterValue": "http://www.microsoft.com"
23+
},
24+
{
25+
"parameterName": "FileDigest",
26+
"parameterValue": "/fd \"SHA256\""
27+
},
28+
{
29+
"parameterName": "PageHash",
30+
"parameterValue": "/NPH"
31+
},
32+
{
33+
"parameterName": "TimeStamp",
34+
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
35+
}
36+
],
37+
"ToolName": "sign",
38+
"ToolVersion": "1.0"
39+
},
40+
{
41+
"KeyCode": "CP-230012",
42+
"OperationSetCode": "SigntoolVerify",
43+
"Parameters": [],
44+
"ToolName": "sign",
45+
"ToolVersion": "1.0"
46+
}
47+
]
48+
}
49+
}
50+
]
51+
}

.pipelines/ESRPSigning_core.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"*.resources.dll",
88

99
"WinUI3Apps\\Assets\\Settings\\Scripts\\*.ps1",
10-
10+
1111
"PowerToys.ActionRunner.exe",
1212
"PowerToys.Update.exe",
1313
"PowerToys.BackgroundActivatorDLL.dll",

.pipelines/release.yml

+9
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,15 @@ extends:
349349
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_core.json'
350350
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
351351

352+
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@3
353+
displayName: Sign DSC Powershell files
354+
inputs:
355+
ConnectedServiceName: 'Terminal/Console/WinAppDriver Team Code Signing Connection'
356+
FolderPath: 'src/dsc/Microsoft.PowerToys.Configure'
357+
signType: batchSigning
358+
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_DSC.json'
359+
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
360+
352361
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@3
353362
displayName: Sign x86 directshow VCM
354363
inputs:

PowerToys.sln

+20
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UITests-FancyZonesEditor",
574574
EndProject
575575
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FancyZonesEditorCommon", "src\modules\fancyzones\FancyZonesEditorCommon\FancyZonesEditorCommon.csproj", "{C0974915-8A1D-4BF0-977B-9587D3807AB7}"
576576
EndProject
577+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DSC", "DSC", "{557C4636-D7E1-4838-A504-7D19B725EE95}"
578+
EndProject
579+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerToys.Settings.DSC.Schema.Generator", "src\dsc\PowerToys.Settings.DSC.Schema.Generator\PowerToys.Settings.DSC.Schema.Generator.csproj", "{1D6893CB-BC0C-46A8-A76C-9728706CA51A}"
580+
ProjectSection(ProjectDependencies) = postProject
581+
{020A7474-3601-4160-A159-D7B70B77B15F} = {020A7474-3601-4160-A159-D7B70B77B15F}
582+
EndProjectSection
583+
EndProject
577584
Global
578585
GlobalSection(SolutionConfigurationPlatforms) = preSolution
579586
Debug|ARM64 = Debug|ARM64
@@ -2536,6 +2543,18 @@ Global
25362543
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Release|x64.Build.0 = Release|x64
25372544
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Release|x86.ActiveCfg = Release|x64
25382545
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Release|x86.Build.0 = Release|x64
2546+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|ARM64.ActiveCfg = Debug|ARM64
2547+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|ARM64.Build.0 = Debug|ARM64
2548+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|x64.ActiveCfg = Debug|x64
2549+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|x64.Build.0 = Debug|x64
2550+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|x86.ActiveCfg = Debug|x64
2551+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|x86.Build.0 = Debug|x64
2552+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|ARM64.ActiveCfg = Release|ARM64
2553+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|ARM64.Build.0 = Release|ARM64
2554+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|x64.ActiveCfg = Release|x64
2555+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|x64.Build.0 = Release|x64
2556+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|x86.ActiveCfg = Release|x64
2557+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|x86.Build.0 = Release|x64
25392558
EndGlobalSection
25402559
GlobalSection(SolutionProperties) = preSolution
25412560
HideSolutionNode = FALSE
@@ -2748,6 +2767,7 @@ Global
27482767
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
27492768
{3A9A791E-94A9-49F8-8401-C11CE288D5FB} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
27502769
{C0974915-8A1D-4BF0-977B-9587D3807AB7} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
2770+
{1D6893CB-BC0C-46A8-A76C-9728706CA51A} = {557C4636-D7E1-4838-A504-7D19B725EE95}
27512771
EndGlobalSection
27522772
GlobalSection(ExtensibilityGlobals) = postSolution
27532773
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# What is it
2+
3+
We would like to enable our users to use [`winget configure`](https://learn.microsoft.com/en-us/windows/package-manager/winget/configure) command to install PowerToys and configure its settings with a [Winget configuration file](https://learn.microsoft.com/en-us/windows/package-manager/configuration/create). For example:
4+
5+
```yaml
6+
properties:
7+
resources:
8+
- resource: Microsoft.WinGet.DSC/WinGetPackage
9+
directives:
10+
description: Install PowerToys
11+
allowPrerelease: true
12+
settings:
13+
id: PowerToys (Preview)
14+
source: winget
15+
16+
- resource: PowerToysConfigure
17+
directives:
18+
description: Configure PowerToys
19+
settings:
20+
ShortcutGuide:
21+
Enabled: false
22+
OverlayOpacity: 1
23+
FancyZones:
24+
Enabled: true
25+
FancyzonesEditorHotkey: "Shift+Ctrl+Alt+F"
26+
configurationVersion: 0.2.0
27+
```
28+
29+
This should install PowerToys and make `PowerToysConfigure` resource available. We can use it in the same file.
30+
31+
# How it works
32+
33+
`PowerToysConfigure` is a [class-based DSC resource](https://learn.microsoft.com/en-us/powershell/dsc/concepts/class-based-resources?view=dsc-2.0). It looks up whether each setting was specified or not by checking whether it's `$null` or `0` for `enum`s and invokes `PowerToys.Settings.exe` with the updated value like so:
34+
```
35+
PowerToys.Settings.exe set <ModuleName>.<SettingName> <SettingValue>
36+
```
37+
38+
So for the example the config above should perform 3 following invocations:
39+
```
40+
PowerToys.Settings.exe set ShortcutGuide.Enabled false
41+
PowerToys.Settings.exe set FancyZones.Enabled true
42+
PowerToys.Settings.exe set FancyZones.FancyzonesEditorHotkey "Shift+Ctrl+Alt+F"
43+
```
44+
45+
`PowerToys.Settings` uses dotnet reflection capabilities to determine `SettingName` type and tries to convert the supplied `SettingValue` string accordingly. We use `ICmdReprParsable` for custom setting types.
46+
47+
48+
# How DSC is implemented
49+
50+
We use `PowerToys.Settings.DSC.Schema.Generator` to generate the bulk of `PowerToysConfigure.psm1` file. It also uses dotnet reflection capabilities to inspect `PowerToys.Settings.UI.Lib.dll` assembly and generate properties for the modules we have. The actual generation is done as a `PowerToys.Settings.DSC.Schema.Generator.csproj` post-build action.
51+
52+
# Debugging DSC resources
53+
54+
First, make sure that PowerShell 7.4+ is installed. Then make sure that you have DSC installed:
55+
56+
```ps
57+
Install-Module -Name PSDesiredStateConfiguration -RequiredVersion 2.0.7
58+
```
59+
60+
After that, start a new `pwsh` session and `cd` to `src\dsc\Microsoft.PowerToys.Configure\Generated` directory. From there, you should execute:
61+
```ps
62+
$env:PSModulePath += ";$pwd"
63+
```
64+
65+
Now build `PowerToys.sln` and **move** `src\dsc\Microsoft.PowerToys.Configure\Microsoft.PowerToys.Configure.psd1` temporarily to `src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\0.0.1\` folder, so it's located alongside with the generated `Microsoft.PowerToys.Configure.psm1`.
66+
67+
This will allow DSC to discover our DSC Resource module. See [PSModulePath](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_psmodulepath?view=powershell-7.4#long-description) for more info.
68+
69+
If everything works, you should see that your module is discovered by executing the following command:
70+
71+
```ps
72+
Get-Module -ListAvailable | grep PowerToys
73+
```
74+
75+
The resource itself should also be available:
76+
```ps
77+
Get-DSCResource | grep PowerToys
78+
```
79+
80+
Otherwise, you can force-import the module to diagnose issues:
81+
82+
```
83+
Import-Module .\Microsoft.PowerToys.Configure.psd1
84+
```
85+
86+
If it's imported successfully, you could also try to invoke it directly:
87+
88+
```ps
89+
Invoke-DscResource -Name PowerToysConfigure -Method Set -ModuleName Microsoft.PowerToys.Configure -Property @{ Debug = $true; Awake = @{ Enabled = $false; Mode = "TIMED"; IntervalMinutes = "10" } }
90+
```
91+
92+
Note that we've supplied `Debug` option, so a `%TEMP\PowerToys.DSC.TestConfigure.txt` is created with the supplied properties, a current timestamp, and other debug output.
93+
94+
Finally, you can test it with winget by invoking it as such:
95+
96+
```ps
97+
winget configure .\configuration.dsc.yaml --accept-configuration-agreements --disable-interactivity
98+
```

installer/PowerToysSetup/Core.wxs

+44
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,49 @@
4646
</Component>
4747
</DirectoryRef>
4848

49+
<?if $(var.PerUser) = "true" ?>
50+
<DirectoryRef Id="PersonalFolder">
51+
<Directory Id="WindowsPowerShellFolder" Name="PowerShell">
52+
<Directory Id="PowerShellModulesFolder" Name="Modules">
53+
<Directory Id="PowerToysDscFolder" Name="Microsoft.PowerToys.Configure">
54+
<Directory Id="PowerToysDscVerFolder" Name="$(var.Version)">
55+
<Component Id="PowerToysDSC" Win64="yes" Guid="4A033E3B-6590-43FD-8FBD-27F9DF557F7F">
56+
<RegistryValue Root="HKCU"
57+
Key="Software\[Manufacturer]\[ProductName]"
58+
Name="DSCInstalled"
59+
Type="integer"
60+
Value="1"
61+
KeyPath="yes"/>
62+
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
63+
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psm1" Id="PTConf.psm1" />
64+
<RemoveFolder Id="RemoveThisFolder" On="uninstall" />
65+
<RemoveFolder Id="RemovePowerToysDscVerFolder" Directory="PowerToysDscVerFolder" On="uninstall" />
66+
<RemoveFolder Id="RemovePowerToysDscFolder" Directory="PowerToysDscFolder" On="uninstall" />
67+
<RemoveFolder Id="RemovePowerShellModulesFolder" Directory="PowerShellModulesFolder" On="uninstall" />
68+
<RemoveFolder Id="RemoveWindowsPowerShellFolder" Directory="WindowsPowerShellFolder" On="uninstall" />
69+
</Component>
70+
</Directory>
71+
</Directory>
72+
</Directory>
73+
</Directory>
74+
</DirectoryRef>
75+
<?else?>
76+
<DirectoryRef Id="ProgramFiles64Folder">
77+
<Directory Id="WindowsPowerShellFolder" Name="WindowsPowerShell">
78+
<Directory Id="PowerShellModulesFolder" Name="Modules">
79+
<Directory Id="PowerToysDscFolder" Name="Microsoft.PowerToys.Configure">
80+
<Directory Id="PowerToysDscVerFolder" Name="$(var.Version)">
81+
<Component Id="PowerToysDSC" Win64="yes" Guid="C52AECA0-DA73-49B8-BB49-31EF6640FF1F">
82+
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
83+
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psm1" Id="PTConf.psm1" />
84+
</Component>
85+
</Directory>
86+
</Directory>
87+
</Directory>
88+
</Directory>
89+
</DirectoryRef>
90+
<?endif?>
91+
4992
<DirectoryRef Id="ApplicationProgramsFolder">
5093
<Component Id="PowerToysStartMenuShortcut" >
5194
<Shortcut Id="ApplicationStartMenuShortcut"
@@ -101,6 +144,7 @@
101144
<ComponentRef Id="License_rtf" />
102145
<ComponentRef Id="Notice_md" />
103146
<ComponentRef Id="DesktopShortcut" />
147+
<ComponentRef Id="PowerToysDSC" />
104148
</ComponentGroup>
105149
</Fragment>
106150
</Wix>

installer/PowerToysSetup/Product.wxs

+1
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@
395395
<Directory Id="ApplicationProgramsFolder" Name="PowerToys (Preview)"/>
396396
</Directory>
397397
<Directory Id="DesktopFolder" Name="Desktop" />
398+
<Directory Id="PersonalFolder" Name="UserHomeDocuments" />
398399
</Directory>
399400
</Fragment>
400401
</Wix>

src/common/interop/interop.cpp

+14-9
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
#include <common/version/version.h>
2222

23-
2423
using namespace System;
2524
using namespace System::Runtime::InteropServices;
2625
using System::Collections::Generic::List;
@@ -40,11 +39,17 @@ public
4039
delete _map;
4140
}
4241

43-
String ^ GetKeyName(DWORD key) {
42+
String ^ GetKeyName(DWORD key)
43+
{
4444
return gcnew String(_map->GetKeyName(key).c_str());
4545
}
4646

47-
void Updatelayout()
47+
DWORD GetKeyValue(String ^ name)
48+
{
49+
return _map->GetKeyFromName(msclr::interop::marshal_as<std::wstring>(name));
50+
}
51+
52+
void Updatelayout()
4853
{
4954
_map->UpdateLayout();
5055
}
@@ -129,13 +134,13 @@ public
129134
}
130135

131136
static List<String ^> ^ GetAllActiveMicrophoneDeviceNames() {
132-
auto names = gcnew List<String ^>();
133-
for (const auto& device : MicrophoneDevice::getAllActive())
134-
{
135-
names->Add(gcnew String(device->name().data()));
137+
auto names = gcnew List<String ^>();
138+
for (const auto& device : MicrophoneDevice::getAllActive())
139+
{
140+
names->Add(gcnew String(device->name().data()));
141+
}
142+
return names;
136143
}
137-
return names;
138-
}
139144

140145
static List<String ^> ^
141146
GetAllVideoCaptureDeviceNames() {

0 commit comments

Comments
 (0)