Compare commits

...

21 Commits

Author SHA1 Message Date
Chris Titus
751b7ef79c inital modification success 2026-02-22 18:51:21 -06:00
Chris Titus
689cf656c0 autounattended creation 2026-02-22 18:05:44 -06:00
Chris Titus
114f671237 scaffold outline for the iso tab 2026-02-22 17:49:45 -06:00
Chris Titus
81aee4ead9 Tab creation 2026-02-22 17:37:03 -06:00
Gabi
349889b194 Minor format changes for Invoke-WPFSystemRepair.ps1 (#4100)
* Format changes

* Update Invoke-WPFSystemRepair.ps1
2026-02-22 17:31:54 -06:00
Raphaël
c619f3a830 Add more development tools to applications.json (#4093)
* Add .NET Desktop Runtime 10 to applications.json

* Add LLVM to applications.json

* Add NASM to applications.json
2026-02-22 17:30:43 -06:00
KamaleiZestri
d005a225a2 NoUI mode and Concurrency edits (#4092)
* Autorun changes

* Missed a handle

* Remove todo

---------

Co-authored-by: Chris Titus <dfm.titus@gmail.com>
2026-02-22 17:28:55 -06:00
Gabi
e79e946e76 Remove incorrect infromaiton from WPFTweaksRazerBlock (#4082) 2026-02-22 17:23:19 -06:00
github-actions[bot]
3b957e1a77 Update sponsors in README (#4095)
Co-authored-by: ChrisTitusTech <7896101+ChrisTitusTech@users.noreply.github.com>
2026-02-22 17:21:52 -06:00
Gabi
d078d67b1d Remove debug option (#4094)
* Remove debug option

* Remove debug option
2026-02-22 17:21:32 -06:00
Gabi
191fe95572 Update-KnownIssues.md (#4096)
* Update KnownIssues.md

* Update KnownIssues.md
2026-02-22 17:20:39 -06:00
Sean (ANGRYxScotsman)
d10c9413ac Update devdocs-generator (#4091)
* Update devdocs-generator.ps1

* made new dir

* Update devdocs-generator.ps1

* Update devdocs-generator.ps1
2026-02-22 17:19:43 -06:00
Chris Titus
e93753e5c9 Json-Cleanup-Expansion (#4090)
* initial cleanup

* remove winutilgpu
2026-02-22 17:19:04 -06:00
Gabi
5eec99df1c Fix title screen image url (#4087) 2026-02-18 15:29:41 -06:00
Gabi
7e2ba41b8b Update inputXML.xaml (#4089) 2026-02-18 15:29:14 -06:00
Chris Titus
d3dc3e7976 Merge branches 'main' and 'main' of https://github.com/ChrisTitusTech/winutil 2026-02-18 14:44:33 -06:00
Gabi
5a4d822710 Fix more images in CONTRIBUTING.md (#4086) 2026-02-18 14:44:19 -06:00
Chris Titus
2625478ef3 assign docs label 2026-02-18 14:41:00 -06:00
github-actions[bot]
956a544c2e Update generated documentation (#4084)
Co-authored-by: ChrisTitusTech <7896101+ChrisTitusTech@users.noreply.github.com>
2026-02-18 14:33:39 -06:00
Gabi
50dbf5bd05 Fix fork image (#4085) 2026-02-18 14:32:29 -06:00
Gabi
338c3e7e57 Add warning to WPFToggleStartMenuRecommendations (#4083) 2026-02-18 14:31:38 -06:00
83 changed files with 1974 additions and 441 deletions

View File

@@ -68,9 +68,9 @@ graph TD
### Fork the Repo ### Fork the Repo
* Fork the WinUtil Repository [here](https://github.com/ChrisTitusTech/winutil) to create a copy that will be available in your repository list. * Fork the WinUtil Repository [here](https://github.com/ChrisTitusTech/winutil) to create a copy that will be available in your repository list.
![Fork Image](assets/Fork-Button-Dark.png#only-dark#gh-dark-mode-only) ![Fork Image](/docs/assets/images/Fork-Button-Dark.png#gh-dark-mode-only)
![Fork Image](assets/Fork-Button-Light.png#only-light#gh-light-mode-only) ![Fork Image](/docs/assets/images/Fork-Button-Light.png#only-light#gh-light-mode-only)
### Clone the Fork ### Clone the Fork
!!! tip !!! tip
@@ -94,22 +94,22 @@ graph TD
* Run the following command to compile and run WinUtil: * Run the following command to compile and run WinUtil:
* `.\Compile.ps1 -run` * `.\Compile.ps1 -run`
![Compile](assets/Compile.png) ![Compile](/docs/assets/images/Complie.png)
* After seeing that your changes work properly, feel free to commit the changes to the repository and make a PR. For help on that, follow the documentation below. * After seeing that your changes work properly, feel free to commit the changes to the repository and make a PR. For help on that, follow the documentation below.
### Committing the changes ### Committing the changes
* Before committing your changes, please discard changes made to the `winutil.ps1` file, like the following: * Before committing your changes, please discard changes made to the `winutil.ps1` file, like the following:
![Push Commit Image](assets/Discard-GHD.png) ![Push Commit Image](/docs/assets/images/Discard-GHD.png)
* Now, commit your changes once you are happy with the result. * Now, commit your changes once you are happy with the result.
![Commit Image](assets/Commit-GHD.png) ![Commit Image](/docs/assets/images/Commit-GHD.png)
* Push the changes to upload them to your fork on github.com. * Push the changes to upload them to your fork on github.com.
![Push Commit Image](assets/Push-Commit.png) ![Push Commit Image](/docs/assets/images/Push-Commit.png)
### Making a PR ### Making a PR
* To make a PR on your repo under a new branch linking to the main branch, a button will show and say Preview and Create pull request. Click that button and fill in all the information that is provided on the template. Once all the information is filled in correctly, check your PR to make sure there is not a WinUtil.ps1 file attached to the PR. Once everything is good, make the PR and wait for Chris (the maintainer) to accept or deny your PR. Once it is accepted by Chris, you will be able to see your changes in the "/windev" build. * To make a PR on your repo under a new branch linking to the main branch, a button will show and say Preview and Create pull request. Click that button and fill in all the information that is provided on the template. Once all the information is filled in correctly, check your PR to make sure there is not a WinUtil.ps1 file attached to the PR. Once everything is good, make the PR and wait for Chris (the maintainer) to accept or deny your PR. Once it is accepted by Chris, you will be able to see your changes in the "/windev" build.

View File

@@ -63,7 +63,9 @@ jobs:
body: 'Automated update of generated documentation from JSON sources' body: 'Automated update of generated documentation from JSON sources'
branch: docs-update branch: docs-update
delete-branch: true delete-branch: true
labels: automated labels: |
automated
documentation
- name: Check outputs - name: Check outputs
run: | run: |

View File

@@ -1,5 +1,4 @@
param ( param (
[switch]$Debug,
[switch]$Run, [switch]$Run,
[string]$Arguments [string]$Arguments
) )
@@ -104,19 +103,26 @@ $xaml
'@ '@
"@) "@)
Update-Progress "Adding: autounattend.xml" 95
$autounattendRaw = Get-Content "$workingdir\tools\autounattend.xml" -Raw
# Strip XML comments (<!-- ... -->, including multi-line)
$autounattendRaw = [regex]::Replace($autounattendRaw, '<!--.*?-->', '', [System.Text.RegularExpressions.RegexOptions]::Singleline)
# Drop blank lines and trim trailing whitespace per line
$autounattendXml = ($autounattendRaw -split "`r?`n" |
Where-Object { $_.Trim() -ne '' } |
ForEach-Object { $_.TrimEnd() }) -join "`r`n"
$script_content.Add(@"
`$WinUtilAutounattendXml = @'
$autounattendXml
'@
"@)
$script_content.Add($(Get-Content "scripts\main.ps1")) $script_content.Add($(Get-Content "scripts\main.ps1"))
if ($Debug) { Update-Progress "Removing temporary files" 99
Update-Progress "Writing debug files" 95 Remove-Item "xaml\inputApp.xaml" -ErrorAction SilentlyContinue
$appXamlContent | Out-File -FilePath "xaml\inputApp.xaml" -Encoding ascii Remove-Item "xaml\inputTweaks.xaml" -ErrorAction SilentlyContinue
$tweaksXamlContent | Out-File -FilePath "xaml\inputTweaks.xaml" -Encoding ascii Remove-Item "xaml\inputFeatures.xaml" -ErrorAction SilentlyContinue
$featuresXamlContent | Out-File -FilePath "xaml\inputFeatures.xaml" -Encoding ascii
} else {
Update-Progress "Removing temporary files" 99
Remove-Item "xaml\inputApp.xaml" -ErrorAction SilentlyContinue
Remove-Item "xaml\inputTweaks.xaml" -ErrorAction SilentlyContinue
Remove-Item "xaml\inputFeatures.xaml" -ErrorAction SilentlyContinue
}
Set-Content -Path "$scriptname" -Value ($script_content -join "`r`n") -Encoding ascii Set-Content -Path "$scriptname" -Value ($script_content -join "`r`n") -Encoding ascii
Write-Progress -Activity "Compiling" -Completed Write-Progress -Activity "Compiling" -Completed

View File

@@ -7,7 +7,7 @@
This utility is a compilation of Windows tasks I perform on each Windows system I use. It is meant to streamline *installs*, debloat with *tweaks*, troubleshoot with *config*, and fix Windows *updates*. I am extremely picky about any contributions to keep this project clean and efficient. This utility is a compilation of Windows tasks I perform on each Windows system I use. It is meant to streamline *installs*, debloat with *tweaks*, troubleshoot with *config*, and fix Windows *updates*. I am extremely picky about any contributions to keep this project clean and efficient.
![screen-install](https://raw.githubusercontent.com/Chris-Titus-Docs/winutil-docs/refs/heads/main/assets/images/Title-Screen.png) ![screen-install](/docs/assets/images/Title-Screen.png)
## 💡 Usage ## 💡 Usage
@@ -76,7 +76,7 @@ You'll see a new file named `winutil.ps1`, which's created by `Compile.ps1` scri
These are the sponsors that help keep this project alive with monthly contributions. These are the sponsors that help keep this project alive with monthly contributions.
<!-- sponsors --><a href="https://github.com/dwelfusius"><img src="https:&#x2F;&#x2F;github.com&#x2F;dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https:&#x2F;&#x2F;github.com&#x2F;mews-se.png" width="60px" alt="User avatar: Martin Stockzell" /></a><a href="https://github.com/jdiegmueller"><img src="https:&#x2F;&#x2F;github.com&#x2F;jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https:&#x2F;&#x2F;github.com&#x2F;robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/KenichiQaz"><img src="https:&#x2F;&#x2F;github.com&#x2F;KenichiQaz.png" width="60px" alt="User avatar: Stefan" /></a><a href="https://github.com/paulsheets"><img src="https:&#x2F;&#x2F;github.com&#x2F;paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https:&#x2F;&#x2F;github.com&#x2F;djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https:&#x2F;&#x2F;github.com&#x2F;anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https:&#x2F;&#x2F;github.com&#x2F;FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https:&#x2F;&#x2F;github.com&#x2F;DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/quaszi"><img src="https:&#x2F;&#x2F;github.com&#x2F;quaszi.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https:&#x2F;&#x2F;github.com&#x2F;DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https:&#x2F;&#x2F;github.com&#x2F;KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/andrewpayne68"><img src="https:&#x2F;&#x2F;github.com&#x2F;andrewpayne68.png" width="60px" alt="User avatar: Andrew P" /></a><!-- sponsors --> <!-- sponsors --><a href="https://github.com/dwelfusius"><img src="https:&#x2F;&#x2F;github.com&#x2F;dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https:&#x2F;&#x2F;github.com&#x2F;mews-se.png" width="60px" alt="User avatar: Martin Stockzell" /></a><a href="https://github.com/jdiegmueller"><img src="https:&#x2F;&#x2F;github.com&#x2F;jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https:&#x2F;&#x2F;github.com&#x2F;robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/KenichiQaz"><img src="https:&#x2F;&#x2F;github.com&#x2F;KenichiQaz.png" width="60px" alt="User avatar: Stefan" /></a><a href="https://github.com/paulsheets"><img src="https:&#x2F;&#x2F;github.com&#x2F;paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https:&#x2F;&#x2F;github.com&#x2F;djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https:&#x2F;&#x2F;github.com&#x2F;anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https:&#x2F;&#x2F;github.com&#x2F;FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https:&#x2F;&#x2F;github.com&#x2F;DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/quaszi"><img src="https:&#x2F;&#x2F;github.com&#x2F;quaszi.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https:&#x2F;&#x2F;github.com&#x2F;DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https:&#x2F;&#x2F;github.com&#x2F;KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/andrewpayne68"><img src="https:&#x2F;&#x2F;github.com&#x2F;andrewpayne68.png" width="60px" alt="User avatar: Andrew P" /></a><a href="https://github.com/nathanzilgo"><img src="https:&#x2F;&#x2F;github.com&#x2F;nathanzilgo.png" width="60px" alt="User avatar: Nathan Fernandes Pedroza" /></a><!-- sponsors -->
## 🏅 Thanks to all Contributors ## 🏅 Thanks to all Contributors
Thanks a lot for spending your time helping Winutil grow. Thanks a lot! Keep rocking 🍻. Thanks a lot for spending your time helping Winutil grow. Thanks a lot! Keep rocking 🍻.

View File

@@ -496,6 +496,14 @@
"link": "https://dotnet.microsoft.com/download/dotnet/9.0", "link": "https://dotnet.microsoft.com/download/dotnet/9.0",
"winget": "Microsoft.DotNet.DesktopRuntime.9" "winget": "Microsoft.DotNet.DesktopRuntime.9"
}, },
"dotnet10": {
"category": "Microsoft Tools",
"choco": "dotnet-10.0-runtime",
"content": ".NET Desktop Runtime 10",
"description": ".NET Desktop Runtime 10 is a runtime environment required for running applications developed with .NET 10.",
"link": "https://dotnet.microsoft.com/download/dotnet/10.0",
"winget": "Microsoft.DotNet.DesktopRuntime.10"
},
"dmt": { "dmt": {
"winget": "GNE.DualMonitorTools", "winget": "GNE.DualMonitorTools",
"choco": "dual-monitor-tools", "choco": "dual-monitor-tools",
@@ -3226,5 +3234,23 @@
"link": "https://zed.dev/", "link": "https://zed.dev/",
"winget": "ZedIndustries.Zed", "winget": "ZedIndustries.Zed",
"foss": true "foss": true
},
"LLLVM": {
"category": "Development",
"choco": "llvm",
"winget": "LLVM.LLVM",
"description": "A collection of modular and reusable compiler and toolchain technologies.",
"content": "LLVM",
"link": "https://llvm.org",
"foss": true
},
"NASM": {
"category": "Development",
"choco": "nasm",
"winget": "NASM.NASM",
"description": "A powerful assembler for the x86 platform.",
"content": "NASM",
"link": "https://nasm.us",
"foss": true
} }
} }

View File

@@ -4,7 +4,6 @@
"Description": ".NET and .NET Framework is a developer platform made up of tools, programming languages, and libraries for building many different types of applications.", "Description": ".NET and .NET Framework is a developer platform made up of tools, programming languages, and libraries for building many different types of applications.",
"category": "Features", "category": "Features",
"panel": "1", "panel": "1",
"Order": "a010_",
"feature": [ "feature": [
"NetFx4-AdvSrvs", "NetFx4-AdvSrvs",
"NetFx3" "NetFx3"
@@ -17,7 +16,6 @@
"Description": "Hyper-V is a hardware virtualization product developed by Microsoft that allows users to create and manage virtual machines.", "Description": "Hyper-V is a hardware virtualization product developed by Microsoft that allows users to create and manage virtual machines.",
"category": "Features", "category": "Features",
"panel": "1", "panel": "1",
"Order": "a011_",
"feature": [ "feature": [
"Microsoft-Hyper-V-All" "Microsoft-Hyper-V-All"
], ],
@@ -31,7 +29,6 @@
"Description": "Enables legacy programs from previous versions of windows", "Description": "Enables legacy programs from previous versions of windows",
"category": "Features", "category": "Features",
"panel": "1", "panel": "1",
"Order": "a012_",
"feature": [ "feature": [
"WindowsMediaPlayer", "WindowsMediaPlayer",
"MediaPlayback", "MediaPlayback",
@@ -46,7 +43,6 @@
"Description": "Windows Subsystem for Linux is an optional feature of Windows that allows Linux programs to run natively on Windows without the need for a separate virtual machine or dual booting.", "Description": "Windows Subsystem for Linux is an optional feature of Windows that allows Linux programs to run natively on Windows without the need for a separate virtual machine or dual booting.",
"category": "Features", "category": "Features",
"panel": "1", "panel": "1",
"Order": "a020_",
"feature": [ "feature": [
"VirtualMachinePlatform", "VirtualMachinePlatform",
"Microsoft-Windows-Subsystem-Linux" "Microsoft-Windows-Subsystem-Linux"
@@ -59,7 +55,6 @@
"Description": "Network File System (NFS) is a mechanism for storing files on a network.", "Description": "Network File System (NFS) is a mechanism for storing files on a network.",
"category": "Features", "category": "Features",
"panel": "1", "panel": "1",
"Order": "a014_",
"feature": [ "feature": [
"ServicesForNFS-ClientOnly", "ServicesForNFS-ClientOnly",
"ClientForNFS-Infrastructure", "ClientForNFS-Infrastructure",
@@ -79,7 +74,6 @@
"Description": "Enables daily registry backup, previously disabled by Microsoft in Windows 10 1803.", "Description": "Enables daily registry backup, previously disabled by Microsoft in Windows 10 1803.",
"category": "Features", "category": "Features",
"panel": "1", "panel": "1",
"Order": "a017_",
"feature": [], "feature": [],
"InvokeScript": [ "InvokeScript": [
" "
@@ -97,7 +91,6 @@
"Description": "Enables Advanced Boot Options screen that lets you start Windows in advanced troubleshooting modes.", "Description": "Enables Advanced Boot Options screen that lets you start Windows in advanced troubleshooting modes.",
"category": "Features", "category": "Features",
"panel": "1", "panel": "1",
"Order": "a018_",
"feature": [], "feature": [],
"InvokeScript": [ "InvokeScript": [
"bcdedit /set bootmenupolicy legacy" "bcdedit /set bootmenupolicy legacy"
@@ -109,7 +102,6 @@
"Description": "Disables Advanced Boot Options screen that lets you start Windows in advanced troubleshooting modes.", "Description": "Disables Advanced Boot Options screen that lets you start Windows in advanced troubleshooting modes.",
"category": "Features", "category": "Features",
"panel": "1", "panel": "1",
"Order": "a019_",
"feature": [], "feature": [],
"InvokeScript": [ "InvokeScript": [
"bcdedit /set bootmenupolicy standard" "bcdedit /set bootmenupolicy standard"
@@ -121,7 +113,6 @@
"Description": "Windows Sandbox is a lightweight virtual machine that provides a temporary desktop environment to safely run applications and programs in isolation.", "Description": "Windows Sandbox is a lightweight virtual machine that provides a temporary desktop environment to safely run applications and programs in isolation.",
"category": "Features", "category": "Features",
"panel": "1", "panel": "1",
"Order": "a021_",
"feature": [ "feature": [
"Containers-DisposableClientVM" "Containers-DisposableClientVM"
], ],
@@ -131,54 +122,54 @@
"Content": "Install Features", "Content": "Install Features",
"category": "Features", "category": "Features",
"panel": "1", "panel": "1",
"Order": "a060_",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"function": "Invoke-WPFFeatureInstall",
"link": "https://winutil.christitus.com/dev/features/features/install" "link": "https://winutil.christitus.com/dev/features/features/install"
}, },
"WPFPanelAutologin": { "WPFPanelAutologin": {
"Content": "Set Up Autologin", "Content": "Set Up Autologin",
"category": "Fixes", "category": "Fixes",
"Order": "a040_",
"panel": "1", "panel": "1",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"function": "Invoke-WPFPanelAutologin",
"link": "https://winutil.christitus.com/dev/features/fixes/autologin" "link": "https://winutil.christitus.com/dev/features/fixes/autologin"
}, },
"WPFFixesUpdate": { "WPFFixesUpdate": {
"Content": "Reset Windows Update", "Content": "Reset Windows Update",
"category": "Fixes", "category": "Fixes",
"panel": "1", "panel": "1",
"Order": "a041_",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"function": "Invoke-WPFFixesUpdate",
"link": "https://winutil.christitus.com/dev/features/fixes/update" "link": "https://winutil.christitus.com/dev/features/fixes/update"
}, },
"WPFFixesNetwork": { "WPFFixesNetwork": {
"Content": "Reset Network", "Content": "Reset Network",
"category": "Fixes", "category": "Fixes",
"Order": "a042_",
"panel": "1", "panel": "1",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"function": "Invoke-WPFFixesNetwork",
"link": "https://winutil.christitus.com/dev/features/fixes/network" "link": "https://winutil.christitus.com/dev/features/fixes/network"
}, },
"WPFPanelDISM": { "WPFPanelDISM": {
"Content": "System Corruption Scan", "Content": "System Corruption Scan",
"category": "Fixes", "category": "Fixes",
"panel": "1", "panel": "1",
"Order": "a043_",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"function": "Invoke-WPFSystemRepair",
"link": "https://winutil.christitus.com/dev/features/fixes/dism" "link": "https://winutil.christitus.com/dev/features/fixes/dism"
}, },
"WPFFixesWinget": { "WPFFixesWinget": {
"Content": "WinGet Reinstall", "Content": "WinGet Reinstall",
"category": "Fixes", "category": "Fixes",
"panel": "1", "panel": "1",
"Order": "a044_",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"function": "Invoke-WPFFixesWinget",
"link": "https://winutil.christitus.com/dev/features/fixes/winget" "link": "https://winutil.christitus.com/dev/features/fixes/winget"
}, },
"WPFPanelControl": { "WPFPanelControl": {
@@ -187,6 +178,9 @@
"panel": "2", "panel": "2",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"InvokeScript": [
"control"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/control" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/control"
}, },
"WPFPanelComputer": { "WPFPanelComputer": {
@@ -195,6 +189,9 @@
"panel": "2", "panel": "2",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"InvokeScript": [
"compmgmt.msc"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/computer" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/computer"
}, },
"WPFPanelNetwork": { "WPFPanelNetwork": {
@@ -203,6 +200,9 @@
"panel": "2", "panel": "2",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"InvokeScript": [
"ncpa.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/network" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/network"
}, },
"WPFPanelPower": { "WPFPanelPower": {
@@ -211,6 +211,9 @@
"panel": "2", "panel": "2",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"InvokeScript": [
"powercfg.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/power" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/power"
}, },
"WPFPanelPrinter": { "WPFPanelPrinter": {
@@ -219,6 +222,9 @@
"panel": "2", "panel": "2",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"InvokeScript": [
"Start-Process 'shell:::{A8A91A66-3A7D-4424-8D24-04E180695C7A}'"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/printer" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/printer"
}, },
"WPFPanelRegion": { "WPFPanelRegion": {
@@ -227,6 +233,9 @@
"panel": "2", "panel": "2",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"InvokeScript": [
"intl.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/region" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/region"
}, },
"WPFPanelRestore": { "WPFPanelRestore": {
@@ -235,6 +244,9 @@
"panel": "2", "panel": "2",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"InvokeScript": [
"rstrui.exe"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/restore" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/restore"
}, },
"WPFPanelSound": { "WPFPanelSound": {
@@ -243,6 +255,9 @@
"panel": "2", "panel": "2",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"InvokeScript": [
"mmsys.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/sound" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/sound"
}, },
"WPFPanelSystem": { "WPFPanelSystem": {
@@ -251,6 +266,9 @@
"panel": "2", "panel": "2",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"InvokeScript": [
"sysdm.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/system" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/system"
}, },
"WPFPanelTimedate": { "WPFPanelTimedate": {
@@ -259,33 +277,36 @@
"panel": "2", "panel": "2",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"InvokeScript": [
"timedate.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/timedate" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/timedate"
}, },
"WPFWinUtilInstallPSProfile": { "WPFWinUtilInstallPSProfile": {
"Content": "Install CTT PowerShell Profile", "Content": "Install CTT PowerShell Profile",
"category": "Powershell Profile Powershell 7+ Only", "category": "Powershell Profile Powershell 7+ Only",
"panel": "2", "panel": "2",
"Order": "a083_",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"function": "Invoke-WinUtilInstallPSProfile",
"link": "https://winutil.christitus.com/dev/features/powershell-profile-powershell-7--only/installpsprofile" "link": "https://winutil.christitus.com/dev/features/powershell-profile-powershell-7--only/installpsprofile"
}, },
"WPFWinUtilUninstallPSProfile": { "WPFWinUtilUninstallPSProfile": {
"Content": "Uninstall CTT PowerShell Profile", "Content": "Uninstall CTT PowerShell Profile",
"category": "Powershell Profile Powershell 7+ Only", "category": "Powershell Profile Powershell 7+ Only",
"panel": "2", "panel": "2",
"Order": "a084_",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"function": "Invoke-WinUtilUninstallPSProfile",
"link": "https://winutil.christitus.com/dev/features/powershell-profile-powershell-7--only/uninstallpsprofile" "link": "https://winutil.christitus.com/dev/features/powershell-profile-powershell-7--only/uninstallpsprofile"
}, },
"WPFWinUtilSSHServer": { "WPFWinUtilSSHServer": {
"Content": "Enable OpenSSH Server", "Content": "Enable OpenSSH Server",
"category": "Remote Access", "category": "Remote Access",
"panel": "2", "panel": "2",
"Order": "a084_",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"function": "Invoke-WPFSSHServer",
"link": "https://winutil.christitus.com/dev/features/remote-access/sshserver" "link": "https://winutil.christitus.com/dev/features/remote-access/sshserver"
} }
} }

View File

@@ -65,10 +65,12 @@
"ButtonTweaksBackgroundColor": "#F7F7F7", "ButtonTweaksBackgroundColor": "#F7F7F7",
"ButtonConfigBackgroundColor": "#F7F7F7", "ButtonConfigBackgroundColor": "#F7F7F7",
"ButtonUpdatesBackgroundColor": "#F7F7F7", "ButtonUpdatesBackgroundColor": "#F7F7F7",
"ButtonWin11ISOBackgroundColor": "#F7F7F7",
"ButtonInstallForegroundColor": "#232629", "ButtonInstallForegroundColor": "#232629",
"ButtonTweaksForegroundColor": "#232629", "ButtonTweaksForegroundColor": "#232629",
"ButtonConfigForegroundColor": "#232629", "ButtonConfigForegroundColor": "#232629",
"ButtonUpdatesForegroundColor": "#232629", "ButtonUpdatesForegroundColor": "#232629",
"ButtonWin11ISOForegroundColor": "#232629",
"ButtonBackgroundColor": "#F5F5F5", "ButtonBackgroundColor": "#F5F5F5",
"ButtonBackgroundPressedColor": "#1A1A1A", "ButtonBackgroundPressedColor": "#1A1A1A",
"ButtonBackgroundMouseoverColor": "#C2C2C2", "ButtonBackgroundMouseoverColor": "#C2C2C2",
@@ -105,10 +107,12 @@
"ButtonTweaksBackgroundColor": "#333333", "ButtonTweaksBackgroundColor": "#333333",
"ButtonConfigBackgroundColor": "#444444", "ButtonConfigBackgroundColor": "#444444",
"ButtonUpdatesBackgroundColor": "#555555", "ButtonUpdatesBackgroundColor": "#555555",
"ButtonWin11ISOBackgroundColor": "#666666",
"ButtonInstallForegroundColor": "#F7F7F7", "ButtonInstallForegroundColor": "#F7F7F7",
"ButtonTweaksForegroundColor": "#F7F7F7", "ButtonTweaksForegroundColor": "#F7F7F7",
"ButtonConfigForegroundColor": "#F7F7F7", "ButtonConfigForegroundColor": "#F7F7F7",
"ButtonUpdatesForegroundColor": "#F7F7F7", "ButtonUpdatesForegroundColor": "#F7F7F7",
"ButtonWin11ISOForegroundColor": "#F7F7F7",
"ButtonBackgroundColor": "#1E3747", "ButtonBackgroundColor": "#1E3747",
"ButtonBackgroundPressedColor": "#F7F7F7", "ButtonBackgroundPressedColor": "#F7F7F7",
"ButtonBackgroundMouseoverColor": "#3B4252", "ButtonBackgroundMouseoverColor": "#3B4252",

View File

@@ -1907,7 +1907,7 @@
}, },
"WPFTweaksRazerBlock": { "WPFTweaksRazerBlock": {
"Content": "Block Razer Software Installs", "Content": "Block Razer Software Installs",
"Description": "Blocks ALL Razer Software installations. The hardware works fine without any software. WARNING: this will also block all Windows third-party driver installations.", "Description": "Blocks ALL Razer Software installations. The hardware works fine without any software.",
"category": "z__Advanced Tweaks - CAUTION", "category": "z__Advanced Tweaks - CAUTION",
"panel": "1", "panel": "1",
"registry": [ "registry": [
@@ -2246,7 +2246,7 @@
}, },
"WPFToggleStartMenuRecommendations": { "WPFToggleStartMenuRecommendations": {
"Content": "Recommendations in Start Menu", "Content": "Recommendations in Start Menu",
"Description": "If disabled then you will not see recommendations in the Start Menu.", "Description": "If disabled then you will not see recommendations in the Start Menu. WARNING: This will also disable Windows Spotlight on your Lock Screen as a side effect.",
"category": "Customize Preferences", "category": "Customize Preferences",
"panel": "2", "panel": "2",
"Type": "Toggle", "Type": "Toggle",

View File

@@ -27,6 +27,14 @@ If you are still having issues, try using a **VPN**, or changing your **DNS prov
| Cloudflare | `1.1.1.1` | `1.0.0.1` | | Cloudflare | `1.1.1.1` | `1.0.0.1` |
| Google | `8.8.8.8` | `8.8.4.4` | | Google | `8.8.8.8` | `8.8.4.4` |
### Script Won't Run
If your PowerShell session is running in **Constrained Language Mode**, some scripts and commands may fail to execute. To check the current language mode, run:
```powershell
$ExecutionContext.SessionState.LanguageMode
```
If it returns `ConstrainedLanguage`, you may need to switch to `FullLanguage` mode or run the script in a session with administrative privileges. Be aware that some security policies may enforce Constrained Language Mode, especially in corporate or managed environments.
### Script blocked by Execution Policy ### Script blocked by Execution Policy
1. Ensure you are running PowerShell as admin: Press `Windows Key`+`X` and select _PowerShell (Admin)_ in Windows 10, or `Windows Terminal (Admin)` in Windows 11. 1. Ensure you are running PowerShell as admin: Press `Windows Key`+`X` and select _PowerShell (Admin)_ in Windows 10, or `Windows Terminal (Admin)` in Windows 11.

View File

@@ -42,3 +42,11 @@ toc: false
### Features ### Features
{{< autolinks section="dev/features/features" >}} {{< autolinks section="dev/features/features" >}}
### Remote Access
{{< autolinks section="dev/features/remote-access" >}}
### Powershell Profile Powershell 7+ Only
{{< autolinks section="dev/features/powershell-profile-powershell-7--only" >}}

View File

@@ -0,0 +1,7 @@
---
title: "Powershell Profile Powershell 7+ Only"
weight: 5
toc: false
---
{{< autolinks section="dev/features/powershell-profile-powershell-7--only" >}}

View File

@@ -0,0 +1,7 @@
---
title: "Remote Access"
weight: 4
toc: false
---
{{< autolinks section="dev/features/remote-access" >}}

View File

@@ -15,3 +15,11 @@ toc: false
### Features ### Features
{{< autolinks section="dev/features/features" >}} {{< autolinks section="dev/features/features" >}}
### Remote Access
{{< autolinks section="dev/features/remote-access" >}}
### Powershell Profile Powershell 7+ Only
{{< autolinks section="dev/features/powershell-profile-powershell-7--only" >}}

View File

@@ -3,7 +3,7 @@ title: "Bing Search in Start Menu"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2186} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2185}
"WPFToggleBingSearch": { "WPFToggleBingSearch": {
"Content": "Bing Search in Start Menu", "Content": "Bing Search in Start Menu",
"Description": "If enable then includes web search results from Bing in your Start Menu search.", "Description": "If enable then includes web search results from Bing in your Start Menu search.",

View File

@@ -3,7 +3,7 @@ title: "Dark Theme for Windows"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2144} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2143}
"WPFToggleDarkMode": { "WPFToggleDarkMode": {
"Content": "Dark Theme for Windows", "Content": "Dark Theme for Windows",
"Description": "Enable/Disable Dark Mode.", "Description": "Enable/Disable Dark Mode.",

View File

@@ -3,7 +3,7 @@ title: "Detailed BSoD"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2537} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2541}
"WPFToggleDetailedBSoD": { "WPFToggleDetailedBSoD": {
"Content": "Detailed BSoD", "Content": "Detailed BSoD",
"Description": "If Enabled then you will see a detailed Blue Screen of Death (BSOD) with more information.", "Description": "If Enabled then you will see a detailed Blue Screen of Death (BSOD) with more information.",

View File

@@ -3,7 +3,7 @@ title: "Cross-Device Resume"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2667} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2671}
"WPFToggleDisableCrossDeviceResume": { "WPFToggleDisableCrossDeviceResume": {
"Content": "Cross-Device Resume", "Content": "Cross-Device Resume",
"Description": "This tweak controls the Resume function in Windows 11 24H2 and later, which allows you to resume an activity from a mobile device and vice-versa.", "Description": "This tweak controls the Resume function in Windows 11 24H2 and later, which allows you to resume an activity from a mobile device and vice-versa.",

View File

@@ -3,7 +3,7 @@ title: "Show Hidden Files"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2422} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2421}
"WPFToggleHiddenFiles": { "WPFToggleHiddenFiles": {
"Content": "Show Hidden Files", "Content": "Show Hidden Files",
"Description": "If Enabled then Hidden Files will be shown.", "Description": "If Enabled then Hidden Files will be shown.",

View File

@@ -3,7 +3,7 @@ title: "Remove Settings Home Page"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2292} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2291}
"WPFToggleHideSettingsHome": { "WPFToggleHideSettingsHome": {
"Content": "Remove Settings Home Page", "Content": "Remove Settings Home Page",
"Description": "Removes the Home page in the Windows Settings app.", "Description": "Removes the Home page in the Windows Settings app.",

View File

@@ -3,7 +3,7 @@ title: "Mouse Acceleration"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2310} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2309}
"WPFToggleMouseAcceleration": { "WPFToggleMouseAcceleration": {
"Content": "Mouse Acceleration", "Content": "Mouse Acceleration",
"Description": "If Enabled then Cursor movement is affected by the speed of your physical mouse movements.", "Description": "If Enabled then Cursor movement is affected by the speed of your physical mouse movements.",

View File

@@ -3,7 +3,7 @@ title: "Disable Multiplane Overlay"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2404} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2403}
"WPFToggleMultiplaneOverlay": { "WPFToggleMultiplaneOverlay": {
"Content": "Disable Multiplane Overlay", "Content": "Disable Multiplane Overlay",
"Description": "Disable the Multiplane Overlay which can sometimes cause issues with Graphics Cards.", "Description": "Disable the Multiplane Overlay which can sometimes cause issues with Graphics Cards.",

View File

@@ -3,7 +3,7 @@ title: "New Outlook"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2362} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2361}
"WPFToggleNewOutlook": { "WPFToggleNewOutlook": {
"Content": "New Outlook", "Content": "New Outlook",
"Description": "If disabled it removes the toggle for new Outlook, disables the new Outlook migration and makes sure the Outlook Application actually uses the old Outlook.", "Description": "If disabled it removes the toggle for new Outlook, disables the new Outlook migration and makes sure the Outlook Application actually uses the old Outlook.",

View File

@@ -3,7 +3,7 @@ title: "NumLock on Startup"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2204} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2203}
"WPFToggleNumLock": { "WPFToggleNumLock": {
"Content": "NumLock on Startup", "Content": "NumLock on Startup",
"Description": "Toggle the Num Lock key state when your computer starts.", "Description": "Toggle the Num Lock key state when your computer starts.",

View File

@@ -3,7 +3,7 @@ title: "S3 Sleep"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2563} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2567}
"WPFToggleS3Sleep": { "WPFToggleS3Sleep": {
"Content": "S3 Sleep", "Content": "S3 Sleep",
"Description": "Toggles between Modern Standby and S3 sleep.", "Description": "Toggles between Modern Standby and S3 sleep.",

View File

@@ -3,7 +3,7 @@ title: "Show File Extensions"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2450} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2449}
"WPFToggleShowExt": { "WPFToggleShowExt": {
"Content": "Show File Extensions", "Content": "Show File Extensions",
"Description": "If enabled then File extensions (e.g., .txt, .jpg) are visible.", "Description": "If enabled then File extensions (e.g., .txt, .jpg) are visible.",

View File

@@ -3,10 +3,10 @@ title: "Recommendations in Start Menu"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2248} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2247}
"WPFToggleStartMenuRecommendations": { "WPFToggleStartMenuRecommendations": {
"Content": "Recommendations in Start Menu", "Content": "Recommendations in Start Menu",
"Description": "If disabled then you will not see recommendations in the Start Menu.", "Description": "If disabled then you will not see recommendations in the Start Menu. WARNING: This will also disable Windows Spotlight on your Lock Screen as a side effect.",
"category": "Customize Preferences", "category": "Customize Preferences",
"panel": "2", "panel": "2",
"Type": "Toggle", "Type": "Toggle",

View File

@@ -3,7 +3,7 @@ title: "Sticky Keys"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2344} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2343}
"WPFToggleStickyKeys": { "WPFToggleStickyKeys": {
"Content": "Sticky Keys", "Content": "Sticky Keys",
"Description": "If Enabled then Sticky Keys is activated - Sticky keys is an accessibility feature of some graphical user interfaces which assists users who have physical disabilities or help users reduce repetitive strain injury.", "Description": "If Enabled then Sticky Keys is activated - Sticky keys is an accessibility feature of some graphical user interfaces which assists users who have physical disabilities or help users reduce repetitive strain injury.",

View File

@@ -3,7 +3,7 @@ title: "Task View Button in Taskbar"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2496} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2495}
"WPFToggleTaskView": { "WPFToggleTaskView": {
"Content": "Task View Button in Taskbar", "Content": "Task View Button in Taskbar",
"Description": "If Enabled then Task View Button in Taskbar will be shown.", "Description": "If Enabled then Task View Button in Taskbar will be shown.",

View File

@@ -3,7 +3,7 @@ title: "Center Taskbar Items"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2514} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2513}
"WPFToggleTaskbarAlignment": { "WPFToggleTaskbarAlignment": {
"Content": "Center Taskbar Items", "Content": "Center Taskbar Items",
"Description": "[Windows 11] If Enabled then the Taskbar Items will be shown on the Center, otherwise the Taskbar Items will be shown on the Left.", "Description": "[Windows 11] If Enabled then the Taskbar Items will be shown on the Center, otherwise the Taskbar Items will be shown on the Left.",
@@ -25,6 +25,11 @@ description: ""
Invoke-WinUtilExplorerUpdate -action \"restart\" Invoke-WinUtilExplorerUpdate -action \"restart\"
" "
], ],
"UndoScript": [
"
Invoke-WinUtilExplorerUpdate -action \"restart\"
"
],
``` ```
## Registry Changes ## Registry Changes

View File

@@ -3,7 +3,7 @@ title: "Search Button in Taskbar"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2478} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2477}
"WPFToggleTaskbarSearch": { "WPFToggleTaskbarSearch": {
"Content": "Search Button in Taskbar", "Content": "Search Button in Taskbar",
"Description": "If Enabled Search Button will be on the taskbar.", "Description": "If Enabled Search Button will be on the taskbar.",

View File

@@ -3,7 +3,7 @@ title: "Verbose Messages During Logon"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2230} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2229}
"WPFToggleVerboseLogon": { "WPFToggleVerboseLogon": {
"Content": "Verbose Messages During Logon", "Content": "Verbose Messages During Logon",
"Description": "Show detailed messages during the login process for troubleshooting and diagnostics.", "Description": "Show detailed messages during the login process for troubleshooting and diagnostics.",

View File

@@ -3,7 +3,7 @@ title: "Delete Temporary Files"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2039} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2038}
"WPFTweaksDeleteTempFiles": { "WPFTweaksDeleteTempFiles": {
"Content": "Delete Temporary Files", "Content": "Delete Temporary Files",
"Description": "Erases TEMP Folders", "Description": "Erases TEMP Folders",

View File

@@ -3,7 +3,7 @@ title: "Disable Explorer Automatic Folder Discovery"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2612} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2616}
"WPFTweaksDisableExplorerAutoDiscovery": { "WPFTweaksDisableExplorerAutoDiscovery": {
"Content": "Disable Explorer Automatic Folder Discovery", "Content": "Disable Explorer Automatic Folder Discovery",
"Description": "Windows Explorer automatically tries to guess the type of the folder based on its contents, slowing down the browsing experience.", "Description": "Windows Explorer automatically tries to guess the type of the folder based on its contents, slowing down the browsing experience.",

View File

@@ -3,7 +3,7 @@ title: "Run Disk Cleanup"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2026} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2025}
"WPFTweaksDiskCleanup": { "WPFTweaksDiskCleanup": {
"Content": "Run Disk Cleanup", "Content": "Run Disk Cleanup",
"Description": "Runs Disk Cleanup on Drive C: and removes old Windows Updates.", "Description": "Runs Disk Cleanup on Drive C: and removes old Windows Updates.",

View File

@@ -3,7 +3,7 @@ title: "Disable Windows Platform Binary Table (WPBT)"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1893} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1892}
"WPFTweaksWPBT": { "WPFTweaksWPBT": {
"Content": "Disable Windows Platform Binary Table (WPBT)", "Content": "Disable Windows Platform Binary Table (WPBT)",
"Description": "If enabled then allows your computer vendor to execute a program each time it boots. It enables computer vendors to force install anti-theft software, software drivers, or a software program conveniently. This could also be a security risk.", "Description": "If enabled then allows your computer vendor to execute a program each time it boots. It enables computer vendors to force install anti-theft software, software drivers, or a software program conveniently. This could also be a security risk.",

View File

@@ -3,7 +3,7 @@ title: "Adobe Network Block"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1974} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1973}
"WPFTweaksBlockAdobeNet": { "WPFTweaksBlockAdobeNet": {
"Content": "Adobe Network Block", "Content": "Adobe Network Block",
"Description": "Reduce user interruptions by selectively blocking connections to Adobe's activation and telemetry servers. Credit: Ruddernation-Designs", "Description": "Reduce user interruptions by selectively blocking connections to Adobe's activation and telemetry servers. Credit: Ruddernation-Designs",

View File

@@ -3,7 +3,7 @@ title: "Disable Background Apps"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2112} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2111}
"WPFTweaksDisableBGapps": { "WPFTweaksDisableBGapps": {
"Content": "Disable Background Apps", "Content": "Disable Background Apps",
"Description": "Disables all Microsoft Store apps from running in the background, which has to be done individually since Win11", "Description": "Disables all Microsoft Store apps from running in the background, which has to be done individually since Win11",

View File

@@ -3,7 +3,7 @@ title: "Disable Fullscreen Optimizations"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2128} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2127}
"WPFTweaksDisableFSO": { "WPFTweaksDisableFSO": {
"Content": "Disable Fullscreen Optimizations", "Content": "Disable Fullscreen Optimizations",
"Description": "Disables FSO in all applications. NOTE: This will disable Color Management in Exclusive Fullscreen", "Description": "Disables FSO in all applications. NOTE: This will disable Color Management in Exclusive Fullscreen",

View File

@@ -3,7 +3,7 @@ title: "Disable IPv6"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2090} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2089}
"WPFTweaksDisableIPv6": { "WPFTweaksDisableIPv6": {
"Content": "Disable IPv6", "Content": "Disable IPv6",
"Description": "Disables IPv6.", "Description": "Disables IPv6.",

View File

@@ -3,7 +3,7 @@ title: "Disable Notification Tray/Calendar"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1951} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1950}
"WPFTweaksDisableNotifications": { "WPFTweaksDisableNotifications": {
"Content": "Disable Notification Tray/Calendar", "Content": "Disable Notification Tray/Calendar",
"Description": "Disables all Notifications INCLUDING Calendar", "Description": "Disables all Notifications INCLUDING Calendar",

View File

@@ -3,7 +3,7 @@ title: "Prefer IPv4 over IPv6"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2052} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2051}
"WPFTweaksIPv46": { "WPFTweaksIPv46": {
"Content": "Prefer IPv4 over IPv6", "Content": "Prefer IPv4 over IPv6",
"Description": "To set the IPv4 preference can have latency and security benefits on private networks where IPv6 is not configured.", "Description": "To set the IPv4 preference can have latency and security benefits on private networks where IPv6 is not configured.",

View File

@@ -3,7 +3,7 @@ title: "Block Razer Software Installs"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1909} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1908}
"WPFTweaksRazerBlock": { "WPFTweaksRazerBlock": {
"Content": "Block Razer Software Installs", "Content": "Block Razer Software Installs",
"Description": "Blocks ALL Razer Software installations. The hardware works fine without any software. WARNING: this will also block all Windows third-party driver installations.", "Description": "Blocks ALL Razer Software installations. The hardware works fine without any software. WARNING: this will also block all Windows third-party driver installations.",

View File

@@ -78,7 +78,6 @@ description: ""
$Sid = (Get-LocalUser $Env:UserName).Sid.Value $Sid = (Get-LocalUser $Env:UserName).Sid.Value
New-Item \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Appx\\AppxAllUserStore\\EndOfLife\\$Sid\\$Appx\" -Force New-Item \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Appx\\AppxAllUserStore\\EndOfLife\\$Sid\\$Appx\" -Force
Remove-AppxPackage $Appx Remove-AppxPackage $Appx
}
" "
], ],
"UndoScript": [ "UndoScript": [

View File

@@ -3,7 +3,7 @@ title: "Set Classic Right-Click Menu "
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2004} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2003}
"WPFTweaksRightClickMenu": { "WPFTweaksRightClickMenu": {
"Content": "Set Classic Right-Click Menu ", "Content": "Set Classic Right-Click Menu ",
"Description": "Great Windows 11 tweak to bring back good context menus when right clicking things in explorer.", "Description": "Great Windows 11 tweak to bring back good context menus when right clicking things in explorer.",

View File

@@ -3,7 +3,7 @@ title: "Disable Teredo"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2068} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2067}
"WPFTweaksTeredo": { "WPFTweaksTeredo": {
"Content": "Disable Teredo", "Content": "Disable Teredo",
"Description": "Teredo network tunneling is a ipv6 feature that can cause additional latency, but may cause problems with some games", "Description": "Teredo network tunneling is a ipv6 feature that can cause additional latency, but may cause problems with some games",

View File

@@ -3,7 +3,7 @@ title: "DNS"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=2588} ```json {filename="config/tweaks.json",linenos=inline,linenostart=2592}
"WPFchangedns": { "WPFchangedns": {
"Content": "DNS", "Content": "DNS",
"category": "z__Advanced Tweaks - CAUTION", "category": "z__Advanced Tweaks - CAUTION",

View File

@@ -15,9 +15,9 @@ function Get-WinUtilSelectedPackages
) )
if ($PackageList.count -eq 1) { if ($PackageList.count -eq 1) {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }
} else { } else {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
} }
$packages = [System.Collections.Hashtable]::new() $packages = [System.Collections.Hashtable]::new()

View File

@@ -4,9 +4,9 @@ function Hide-WPFInstallAppBusy {
Hides the busy overlay in the install app area of the WPF form. Hides the busy overlay in the install app area of the WPF form.
This is used to indicate that an install or uninstall has finished. This is used to indicate that an install or uninstall has finished.
#> #>
$sync.form.Dispatcher.Invoke([action]{ Invoke-WPFUIThread -ScriptBlock {
$sync.InstallAppAreaOverlay.Visibility = [Windows.Visibility]::Collapsed $sync.InstallAppAreaOverlay.Visibility = [Windows.Visibility]::Collapsed
$sync.InstallAppAreaBorder.IsEnabled = $true $sync.InstallAppAreaBorder.IsEnabled = $true
$sync.InstallAppAreaScrollViewer.Effect.Radius = 0 $sync.InstallAppAreaScrollViewer.Effect.Radius = 0
}) }
} }

View File

@@ -113,7 +113,7 @@ function Install-WinUtilProgramChoco {
[int]$totalPrograms [int]$totalPrograms
) )
$progressState = if ($currentIndex -eq $totalPrograms) { "Normal" } else { "Error" } $progressState = if ($currentIndex -eq $totalPrograms) { "Normal" } else { "Error" }
$sync.form.Dispatcher.Invoke([action] { Set-WinUtilTaskbaritem -state $progressState -value ($currentIndex / $totalPrograms) }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state $progressState -value ($currentIndex / $totalPrograms) }
} }
function Install-ChocoPackage { function Install-ChocoPackage {
@@ -234,7 +234,7 @@ function Install-WinUtilProgramChoco {
for ($currentIndex = 0; $currentIndex -lt $totalPrograms; $currentIndex++) { for ($currentIndex = 0; $currentIndex -lt $totalPrograms; $currentIndex++) {
$Program = $Programs[$currentIndex] $Program = $Programs[$currentIndex]
Set-WinUtilProgressBar -label "$Action $($Program)" -percent ($currentIndex / $totalPrograms * 100) Set-WinUtilProgressBar -label "$Action $($Program)" -percent ($currentIndex / $totalPrograms * 100)
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($currentIndex / $totalPrograms)}) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($currentIndex / $totalPrograms)}
switch ($Action) { switch ($Action) {
"Install" { "Install" {

View File

@@ -117,7 +117,7 @@ Function Install-WinUtilProgramWinget {
$Program = $Programs[$i] $Program = $Programs[$i]
$result = $false $result = $false
Set-WinUtilProgressBar -label "$Action $($Program)" -percent ($i / $count * 100) Set-WinUtilProgressBar -label "$Action $($Program)" -percent ($i / $count * 100)
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($i / $count)}) Invoke-WPFUIThread -ScriptBlock{ Set-WinUtilTaskbaritem -value ($i / $count)}
$result = switch ($Action) { $result = switch ($Action) {
"Install" {Invoke-Install -Program $Program} "Install" {Invoke-Install -Program $Program}

View File

@@ -8,7 +8,7 @@ function Invoke-WinUtilExplorerUpdate {
) )
if ($action -eq "refresh") { if ($action -eq "refresh") {
Invoke-WPFRunspace -DebugPreference $DebugPreference -ScriptBlock { Invoke-WPFRunspace -ScriptBlock {
# Define the Win32 type only if it doesn't exist # Define the Win32 type only if it doesn't exist
if (-not ([System.Management.Automation.PSTypeName]'Win32').Type) { if (-not ([System.Management.Automation.PSTypeName]'Win32').Type) {
Add-Type -TypeDefinition @" Add-Type -TypeDefinition @"

View File

@@ -10,46 +10,40 @@ function Invoke-WinUtilFeatureInstall {
$CheckBox $CheckBox
) )
$x = 0 if($sync.configs.feature.$CheckBox.feature) {
Foreach( $feature in $sync.configs.feature.$CheckBox.feature ) {
try {
Write-Host "Installing $feature"
Enable-WindowsOptionalFeature -Online -FeatureName $feature -All -NoRestart
} catch {
if ($CheckBox.Exception.Message -like "*requires elevation*") {
Write-Warning "Unable to Install $feature due to permissions. Are you running as admin?"
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" }
} else {
$CheckBox | ForEach-Object { Write-Warning "Unable to Install $feature due to unhandled exception"
if($sync.configs.feature.$psitem.feature) { Write-Warning $CheckBox.Exception.StackTrace
Foreach( $feature in $sync.configs.feature.$psitem.feature ) {
try {
Write-Host "Installing $feature"
Enable-WindowsOptionalFeature -Online -FeatureName $feature -All -NoRestart
} catch {
if ($psitem.Exception.Message -like "*requires elevation*") {
Write-Warning "Unable to Install $feature due to permissions. Are you running as admin?"
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" })
} else {
Write-Warning "Unable to Install $feature due to unhandled exception"
Write-Warning $psitem.Exception.StackTrace
}
} }
} }
} }
if($sync.configs.feature.$psitem.InvokeScript) { }
Foreach( $script in $sync.configs.feature.$psitem.InvokeScript ) { if($sync.configs.feature.$CheckBox.InvokeScript) {
try { Foreach( $script in $sync.configs.feature.$CheckBox.InvokeScript ) {
$Scriptblock = [scriptblock]::Create($script) try {
$Scriptblock = [scriptblock]::Create($script)
Write-Host "Running Script for $psitem" Write-Host "Running Script for $CheckBox"
Invoke-Command $scriptblock -ErrorAction stop Invoke-Command $scriptblock -ErrorAction stop
} catch { } catch {
if ($psitem.Exception.Message -like "*requires elevation*") { if ($CheckBox.Exception.Message -like "*requires elevation*") {
Write-Warning "Unable to Install $feature due to permissions. Are you running as admin?" Write-Warning "Unable to Install $feature due to permissions. Are you running as admin?"
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" }
} else { } else {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" }
Write-Warning "Unable to Install $feature due to unhandled exception" Write-Warning "Unable to Install $feature due to unhandled exception"
Write-Warning $psitem.Exception.StackTrace Write-Warning $CheckBox.Exception.StackTrace
}
} }
} }
} }
$X++
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($x/$CheckBox.Count) })
} }
} }

View File

@@ -1,23 +0,0 @@
function Invoke-WinUtilGPU {
$gpuInfo = Get-CimInstance Win32_VideoController
# GPUs to blacklist from using Demanding Theming
$lowPowerGPUs = (
"*NVIDIA GeForce*M*",
"*NVIDIA GeForce*Laptop*",
"*NVIDIA GeForce*GT*",
"*AMD Radeon(TM)*",
"*Intel(R) HD Graphics*",
"*UHD*"
)
foreach ($gpu in $gpuInfo) {
foreach ($gpuPattern in $lowPowerGPUs) {
if ($gpu.Name -like $gpuPattern) {
return $false
}
}
}
return $true
}

View File

@@ -0,0 +1,572 @@
function Write-Win11ISOLog {
<#
.SYNOPSIS
Appends a timestamped message to the Win11ISO status log TextBox.
.PARAMETER Message
The message to append.
#>
param([string]$Message)
$timestamp = (Get-Date).ToString("HH:mm:ss")
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$current = $sync["WPFWin11ISOStatusLog"].Text
if ($current -eq "Ready. Please select a Windows 11 ISO to begin.") {
$sync["WPFWin11ISOStatusLog"].Text = "[$timestamp] $Message"
} else {
$sync["WPFWin11ISOStatusLog"].Text += "`n[$timestamp] $Message"
}
$sync["WPFWin11ISOStatusLog"].ScrollToEnd()
})
}
function Invoke-WinUtilISOBrowse {
<#
.SYNOPSIS
Opens an OpenFileDialog so the user can choose a Windows 11 ISO file.
Populates WPFWin11ISOPath and reveals the Mount & Verify section (Step 2).
#>
Add-Type -AssemblyName System.Windows.Forms
$dlg = [System.Windows.Forms.OpenFileDialog]::new()
$dlg.Title = "Select Windows 11 ISO"
$dlg.Filter = "ISO files (*.iso)|*.iso|All files (*.*)|*.*"
$dlg.InitialDirectory = [System.Environment]::GetFolderPath("Desktop")
if ($dlg.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) { return }
$isoPath = $dlg.FileName
# ── Basic size sanity-check (a Win11 ISO is typically > 4 GB) ──
$fileSizeGB = [math]::Round((Get-Item $isoPath).Length / 1GB, 2)
$sync["WPFWin11ISOPath"].Text = $isoPath
$sync["WPFWin11ISOFileInfo"].Text = "File size: $fileSizeGB GB"
$sync["WPFWin11ISOFileInfo"].Visibility = "Visible"
# Reveal Step 2
$sync["WPFWin11ISOMountSection"].Visibility = "Visible"
# Collapse all later steps whenever a new ISO is chosen
$sync["WPFWin11ISOVerifyResultPanel"].Visibility = "Collapsed"
$sync["WPFWin11ISOModifySection"].Visibility = "Collapsed"
$sync["WPFWin11ISOOutputSection"].Visibility = "Collapsed"
Write-Win11ISOLog "ISO selected: $isoPath ($fileSizeGB GB)"
}
function Invoke-WinUtilISOMountAndVerify {
<#
.SYNOPSIS
Mounts the selected ISO, verifies it is a valid Windows 11 image,
and populates the edition list. Reveals Step 3 on success.
#>
$isoPath = $sync["WPFWin11ISOPath"].Text
if ([string]::IsNullOrWhiteSpace($isoPath) -or $isoPath -eq "No ISO selected...") {
[System.Windows.MessageBox]::Show(
"Please select an ISO file first.",
"No ISO Selected", "OK", "Warning")
return
}
Write-Win11ISOLog "Mounting ISO: $isoPath"
Set-WinUtilProgressBar -Label "Mounting ISO..." -Percent 10
try {
# Mount the ISO
$diskImage = Mount-DiskImage -ImagePath $isoPath -PassThru -ErrorAction Stop
$driveLetter = ($diskImage | Get-Volume).DriveLetter + ":"
Write-Win11ISOLog "Mounted at drive $driveLetter"
Set-WinUtilProgressBar -Label "Verifying ISO contents..." -Percent 30
# ── Verify install.wim / install.esd presence ──
$wimPath = Join-Path $driveLetter "sources\install.wim"
$esdPath = Join-Path $driveLetter "sources\install.esd"
if (-not (Test-Path $wimPath) -and -not (Test-Path $esdPath)) {
Dismount-DiskImage -ImagePath $isoPath | Out-Null
Write-Win11ISOLog "ERROR: install.wim/install.esd not found — not a valid Windows ISO."
[System.Windows.MessageBox]::Show(
"This does not appear to be a valid Windows ISO.`n`ninstall.wim / install.esd was not found.",
"Invalid ISO", "OK", "Error")
Set-WinUtilProgressBar -Label "" -Percent 0
return
}
$activeWim = if (Test-Path $wimPath) { $wimPath } else { $esdPath }
# ── Read edition / architecture info ──
Set-WinUtilProgressBar -Label "Reading image metadata..." -Percent 55
$editions = Get-WindowsImage -ImagePath $activeWim | Select-Object -ExpandProperty ImageName
# ── Verify at least one Win11 edition is present ──
$isWin11 = $editions | Where-Object { $_ -match "Windows 11" }
if (-not $isWin11) {
Dismount-DiskImage -ImagePath $isoPath | Out-Null
Write-Win11ISOLog "ERROR: No 'Windows 11' edition found in the image."
[System.Windows.MessageBox]::Show(
"No Windows 11 edition was found in this ISO.`n`nOnly official Windows 11 ISOs are supported.",
"Not a Windows 11 ISO", "OK", "Error")
Set-WinUtilProgressBar -Label "" -Percent 0
return
}
# ── Populate UI ──
$sync["WPFWin11ISOMountDriveLetter"].Text = "Mounted at: $driveLetter | Image file: $(Split-Path $activeWim -Leaf)"
$sync["WPFWin11ISOEditionList"].Text = ($editions -join "`n")
$sync["WPFWin11ISOVerifyResultPanel"].Visibility = "Visible"
# Store for later steps
$sync["Win11ISODriveLetter"] = $driveLetter
$sync["Win11ISOWimPath"] = $activeWim
$sync["Win11ISOImagePath"] = $isoPath
# Reveal Step 3
$sync["WPFWin11ISOModifySection"].Visibility = "Visible"
Set-WinUtilProgressBar -Label "ISO verified ✔" -Percent 100
Write-Win11ISOLog "ISO verified OK. Editions found: $($editions.Count)"
}
catch {
Write-Win11ISOLog "ERROR during mount/verify: $_"
[System.Windows.MessageBox]::Show(
"An error occurred while mounting or verifying the ISO:`n`n$_",
"Error", "OK", "Error")
}
finally {
Start-Sleep -Milliseconds 800
Set-WinUtilProgressBar -Label "" -Percent 0
}
}
function Invoke-WinUtilISOModify {
<#
.SYNOPSIS
Extracts ISO contents to a temp working directory, modifies install.wim,
then repackages the image. Reveals Step 4 (output options) on success.
.NOTES
This function runs inside a PowerShell runspace so the UI stays responsive.
Placeholder modification logic is provided; extend as needed.
#>
$isoPath = $sync["Win11ISOImagePath"]
$driveLetter= $sync["Win11ISODriveLetter"]
$wimPath = $sync["Win11ISOWimPath"]
if (-not $isoPath) {
[System.Windows.MessageBox]::Show(
"No verified ISO found. Please complete Steps 1 and 2 first.",
"Not Ready", "OK", "Warning")
return
}
# Disable the modify button to prevent double-click
$sync["WPFWin11ISOModifyButton"].IsEnabled = $false
$workDir = Join-Path $env:TEMP "WinUtil_Win11ISO_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
# ── Run modification in a background runspace ──
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
$runspace.ApartmentState = "STA"
$runspace.ThreadOptions = "ReuseThread"
$runspace.Open()
$runspace.SessionStateProxy.SetVariable("sync", $sync)
$runspace.SessionStateProxy.SetVariable("isoPath", $isoPath)
$runspace.SessionStateProxy.SetVariable("driveLetter", $driveLetter)
$runspace.SessionStateProxy.SetVariable("wimPath", $wimPath)
$runspace.SessionStateProxy.SetVariable("workDir", $workDir)
# Serialize functions so they are available inside the runspace
$isoScriptFuncDef = "function Invoke-WinUtilISOScript {`n" + `
${function:Invoke-WinUtilISOScript}.ToString() + "`n}"
$runspace.SessionStateProxy.SetVariable("isoScriptFuncDef", $isoScriptFuncDef)
$win11ISOLogFuncDef = "function Write-Win11ISOLog {`n" + `
${function:Write-Win11ISOLog}.ToString() + "`n}"
$runspace.SessionStateProxy.SetVariable("win11ISOLogFuncDef", $win11ISOLogFuncDef)
$refreshUSBFuncDef = "function Invoke-WinUtilISORefreshUSBDrives {`n" + `
${function:Invoke-WinUtilISORefreshUSBDrives}.ToString() + "`n}"
$runspace.SessionStateProxy.SetVariable("refreshUSBFuncDef", $refreshUSBFuncDef)
$script = [Management.Automation.PowerShell]::Create()
$script.Runspace = $runspace
$script.AddScript({
# Import helper functions into this runspace
. ([scriptblock]::Create($isoScriptFuncDef))
. ([scriptblock]::Create($win11ISOLogFuncDef))
. ([scriptblock]::Create($refreshUSBFuncDef))
function Log($msg) {
$ts = (Get-Date).ToString("HH:mm:ss")
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync["WPFWin11ISOStatusLog"].Text += "`n[$ts] $msg"
$sync["WPFWin11ISOStatusLog"].ScrollToEnd()
})
}
function SetProgress($label, $pct) {
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync.progressBarTextBlock.Text = $label
$sync.progressBarTextBlock.ToolTip = $label
$sync.ProgressBar.Value = [Math]::Max($pct, 5)
})
}
try {
# ── 1. Create working directory structure ──
Log "Creating working directory: $workDir"
$isoContents = Join-Path $workDir "iso_contents"
$mountDir = Join-Path $workDir "wim_mount"
New-Item -ItemType Directory -Path $isoContents, $mountDir -Force | Out-Null
SetProgress "Copying ISO contents..." 10
# ── 2. Copy all ISO contents to the working directory ──
Log "Copying ISO contents from $driveLetter to $isoContents..."
$robocopyArgs = @($driveLetter, $isoContents, "/E", "/NFL", "/NDL", "/NJH", "/NJS")
& robocopy @robocopyArgs | Out-Null
Log "ISO contents copied."
SetProgress "Mounting install.wim..." 25
# ── 3. Copy install.wim to working dir (it may be read-only on the DVD) ──
$localWim = Join-Path $isoContents "sources\install.wim"
if (-not (Test-Path $localWim)) {
# ESD path
$localWim = Join-Path $isoContents "sources\install.esd"
}
# Ensure the file is writable
Set-ItemProperty -Path $localWim -Name IsReadOnly -Value $false
# ── 4. Mount the first index of install.wim ──
Log "Mounting install.wim (Index 1) at $mountDir..."
Mount-WindowsImage -ImagePath $localWim -Index 1 -Path $mountDir -ErrorAction Stop | Out-Null
SetProgress "Modifying install.wim..." 45
# ── Apply all WinUtil modifications via Invoke-WinUtilISOScript ──
Log "Applying WinUtil modifications to install.wim..."
Invoke-WinUtilISOScript -ScratchDir $mountDir -Log { param($m) Log $m }
# ── 5. Save and dismount the WIM ──
SetProgress "Saving modified install.wim..." 65
Log "Dismounting and saving install.wim..."
Dismount-WindowsImage -Path $mountDir -Save -ErrorAction Stop | Out-Null
Log "install.wim saved."
SetProgress "Dismounting source ISO..." 80
# ── 6. Dismount the original ISO ──
Log "Dismounting original ISO..."
Dismount-DiskImage -ImagePath $isoPath | Out-Null
# Store work directory for output steps
$sync["Win11ISOWorkDir"] = $workDir
$sync["Win11ISOContentsDir"] = $isoContents
SetProgress "Modification complete ✔" 100
Log "install.wim modification complete. Select an output option in Step 4."
# ── Reveal Step 4 on the UI thread ──
$sync["WPFWin11ISOOutputSection"].Dispatcher.Invoke([action]{
$sync["WPFWin11ISOOutputSection"].Visibility = "Visible"
Invoke-WinUtilISORefreshUSBDrives
})
}
catch {
Log "ERROR during modification: $_"
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
[System.Windows.MessageBox]::Show(
"An error occurred during install.wim modification:`n`n$_",
"Modification Error", "OK", "Error")
})
}
finally {
Start-Sleep -Milliseconds 800
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync.progressBarTextBlock.Text = ""
$sync.progressBarTextBlock.ToolTip = ""
$sync.ProgressBar.Value = 0
$sync["WPFWin11ISOModifyButton"].IsEnabled = $true
})
}
}) | Out-Null
$script.BeginInvoke() | Out-Null
}
function Invoke-WinUtilISOExport {
<#
.SYNOPSIS
Saves the modified ISO contents as a new bootable ISO file.
Uses oscdimg.exe (part of the Windows ADK) if present; falls back
to a reminder message if not installed.
#>
$contentsDir = $sync["Win11ISOContentsDir"]
if (-not $contentsDir -or -not (Test-Path $contentsDir)) {
[System.Windows.MessageBox]::Show(
"No modified ISO content found. Please complete Steps 13 first.",
"Not Ready", "OK", "Warning")
return
}
Add-Type -AssemblyName System.Windows.Forms
$dlg = [System.Windows.Forms.SaveFileDialog]::new()
$dlg.Title = "Save Modified Windows 11 ISO"
$dlg.Filter = "ISO files (*.iso)|*.iso"
$dlg.FileName = "Win11_Modified_$(Get-Date -Format 'yyyyMMdd').iso"
$dlg.InitialDirectory = [System.Environment]::GetFolderPath("Desktop")
if ($dlg.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) { return }
$outputISO = $dlg.FileName
Write-Win11ISOLog "Exporting to ISO: $outputISO"
Set-WinUtilProgressBar -Label "Building ISO..." -Percent 10
# Locate oscdimg.exe (Windows ADK)
$oscdimg = Get-ChildItem "C:\Program Files (x86)\Windows Kits" -Recurse -Filter "oscdimg.exe" -ErrorAction SilentlyContinue |
Select-Object -First 1 -ExpandProperty FullName
if (-not $oscdimg) {
Set-WinUtilProgressBar -Label "" -Percent 0
Write-Win11ISOLog "oscdimg.exe not found. Install Windows ADK to enable ISO export."
[System.Windows.MessageBox]::Show(
"oscdimg.exe was not found.`n`nTo export an ISO you need the Windows Assessment and Deployment Kit (ADK).`n`nDownload it from: https://learn.microsoft.com/windows-hardware/get-started/adk-install",
"Windows ADK Required", "OK", "Warning")
return
}
# Build boot parameters (BIOS + UEFI dual-boot)
$bootData = "2#p0,e,b`"$contentsDir\boot\etfsboot.com`"#pEF,e,b`"$contentsDir\efi\microsoft\boot\efisys.bin`""
$oscdimgArgs = @(
"-m", # ignore source path max size
"-o", # optimise storage
"-u2", # UDF 2.01
"-udfver102",
"-bootdata:$bootData",
"-l`"CTOS_MODIFIED`"",
"`"$contentsDir`"",
"`"$outputISO`""
)
try {
Write-Win11ISOLog "Running oscdimg..."
$proc = Start-Process -FilePath $oscdimg -ArgumentList $oscdimgArgs -Wait -PassThru -NoNewWindow
if ($proc.ExitCode -eq 0) {
Set-WinUtilProgressBar -Label "ISO exported ✔" -Percent 100
Write-Win11ISOLog "ISO exported successfully: $outputISO"
[System.Windows.MessageBox]::Show(
"ISO exported successfully!`n`n$outputISO",
"Export Complete", "OK", "Info")
} else {
Write-Win11ISOLog "oscdimg exited with code $($proc.ExitCode)."
[System.Windows.MessageBox]::Show(
"oscdimg exited with code $($proc.ExitCode).`nCheck the status log for details.",
"Export Error", "OK", "Error")
}
}
catch {
Write-Win11ISOLog "ERROR during ISO export: $_"
[System.Windows.MessageBox]::Show("ISO export failed:`n`n$_","Error","OK","Error")
}
finally {
Start-Sleep -Milliseconds 800
Set-WinUtilProgressBar -Label "" -Percent 0
}
}
function Invoke-WinUtilISORefreshUSBDrives {
<#
.SYNOPSIS
Populates the USB drive ComboBox with all currently attached removable drives.
#>
$combo = $sync["WPFWin11ISOUSBDriveComboBox"]
$combo.Items.Clear()
$removable = Get-Disk | Where-Object { $_.BusType -eq "USB" } | Sort-Object Number
if ($removable.Count -eq 0) {
$combo.Items.Add("No USB drives detected")
$combo.SelectedIndex = 0
Write-Win11ISOLog "No USB drives detected."
return
}
foreach ($disk in $removable) {
$sizeGB = [math]::Round($disk.Size / 1GB, 1)
$label = "Disk $($disk.Number): $($disk.FriendlyName) [$sizeGB GB] — $($disk.PartitionStyle)"
$combo.Items.Add($label)
}
$combo.SelectedIndex = 0
Write-Win11ISOLog "Found $($removable.Count) USB drive(s)."
# Store disk objects for later use
$sync["Win11ISOUSBDisks"] = $removable
}
function Invoke-WinUtilISOWriteUSB {
<#
.SYNOPSIS
Erases the selected USB drive and writes the modified Windows 11 ISO
content as a bootable installation drive (using DISM / robocopy approach).
#>
$contentsDir = $sync["Win11ISOContentsDir"]
$usbDisks = $sync["Win11ISOUSBDisks"]
if (-not $contentsDir -or -not (Test-Path $contentsDir)) {
[System.Windows.MessageBox]::Show(
"No modified ISO content found. Please complete Steps 13 first.",
"Not Ready", "OK", "Warning")
return
}
$selectedIndex = $sync["WPFWin11ISOUSBDriveComboBox"].SelectedIndex
if ($selectedIndex -lt 0 -or -not $usbDisks -or $selectedIndex -ge $usbDisks.Count) {
[System.Windows.MessageBox]::Show(
"Please select a USB drive from the dropdown.",
"No Drive Selected", "OK", "Warning")
return
}
$targetDisk = $usbDisks[$selectedIndex]
$diskNum = $targetDisk.Number
$sizeGB = [math]::Round($targetDisk.Size / 1GB, 1)
$confirm = [System.Windows.MessageBox]::Show(
"ALL data on Disk $diskNum ($($targetDisk.FriendlyName), $sizeGB GB) will be PERMANENTLY ERASED.`n`nAre you sure you want to continue?",
"Confirm USB Erase", "YesNo", "Warning")
if ($confirm -ne "Yes") {
Write-Win11ISOLog "USB write cancelled by user."
return
}
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $false
Write-Win11ISOLog "Starting USB write to Disk $diskNum..."
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
$runspace.ApartmentState = "STA"
$runspace.ThreadOptions = "ReuseThread"
$runspace.Open()
$runspace.SessionStateProxy.SetVariable("sync", $sync)
$runspace.SessionStateProxy.SetVariable("diskNum", $diskNum)
$runspace.SessionStateProxy.SetVariable("contentsDir", $contentsDir)
$script = [Management.Automation.PowerShell]::Create()
$script.Runspace = $runspace
$script.AddScript({
function Log($msg) {
$ts = (Get-Date).ToString("HH:mm:ss")
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync["WPFWin11ISOStatusLog"].Text += "`n[$ts] $msg"
$sync["WPFWin11ISOStatusLog"].ScrollToEnd()
})
}
function SetProgress($label, $pct) {
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync.progressBarTextBlock.Text = $label
$sync.progressBarTextBlock.ToolTip = $label
$sync.ProgressBar.Value = [Math]::Max($pct, 5)
})
}
try {
SetProgress "Formatting USB drive..." 10
# ── Diskpart script: clean, GPT, create ESP + data partitions ──
$dpScript = @"
select disk $diskNum
clean
convert gpt
create partition efi size=512
format quick fs=fat32 label="SYSTEM"
assign
create partition primary
format quick fs=fat32 label="WINPE"
assign
exit
"@
$dpFile = Join-Path $env:TEMP "winutil_diskpart_$(Get-Random).txt"
$dpScript | Set-Content -Path $dpFile -Encoding ASCII
Log "Running diskpart on Disk $diskNum..."
diskpart /s $dpFile | Out-Null
Remove-Item $dpFile -Force
SetProgress "Identifying USB partitions..." 30
Start-Sleep -Seconds 3 # let Windows assign drive letters
# Find newly assigned drive letter for the data partition
$usbVol = Get-Partition -DiskNumber $diskNum |
Where-Object { $_.Type -eq "Basic" } |
Get-Volume |
Where-Object { $_.FileSystemLabel -eq "WINPE" } |
Select-Object -First 1
if (-not $usbVol) {
throw "Could not locate the formatted USB data partition. Drive letter may not have been assigned automatically."
}
$usbDrive = "$($usbVol.DriveLetter):"
Log "USB data partition: $usbDrive"
SetProgress "Copying Windows 11 files to USB..." 45
# ── Copy files (split large install.wim if > 4 GB for FAT32) ──
$installWim = Join-Path $contentsDir "sources\install.wim"
if (Test-Path $installWim) {
$wimSizeMB = [math]::Round((Get-Item $installWim).Length / 1MB)
if ($wimSizeMB -gt 3800) {
# FAT32 limit split with DISM
Log "install.wim is $wimSizeMB MB splitting for FAT32 compatibility..."
$splitDest = Join-Path $usbDrive "sources\install.swm"
New-Item -ItemType Directory -Path (Split-Path $splitDest) -Force | Out-Null
Split-WindowsImage -ImagePath $installWim `
-SplitImagePath $splitDest `
-FileSize 3800 -CheckIntegrity | Out-Null
Log "install.wim split complete."
# Copy everything else (exclude install.wim)
$robocopyArgs = @($contentsDir, $usbDrive, "/E", "/XF", "install.wim", "/NFL", "/NDL", "/NJH", "/NJS")
& robocopy @robocopyArgs | Out-Null
} else {
& robocopy $contentsDir $usbDrive /E /NFL /NDL /NJH /NJS | Out-Null
}
} else {
& robocopy $contentsDir $usbDrive /E /NFL /NDL /NJH /NJS | Out-Null
}
SetProgress "Finalising USB drive..." 90
Log "Files copied to USB."
SetProgress "USB write complete ✔" 100
Log "USB drive is ready for use."
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
[System.Windows.MessageBox]::Show(
"USB drive created successfully!`n`nYou can now boot from this drive to install Windows 11.",
"USB Ready", "OK", "Info")
})
}
catch {
Log "ERROR during USB write: $_"
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
[System.Windows.MessageBox]::Show(
"USB write failed:`n`n$_",
"USB Write Error", "OK", "Error")
})
}
finally {
Start-Sleep -Milliseconds 800
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync.progressBarTextBlock.Text = ""
$sync.progressBarTextBlock.ToolTip = ""
$sync.ProgressBar.Value = 0
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $true
})
}
}) | Out-Null
$script.BeginInvoke() | Out-Null
}

View File

@@ -0,0 +1,270 @@
function Invoke-WinUtilISOScript {
<#
.SYNOPSIS
Applies the standard WinUtil modifications to a mounted Windows 11 install.wim image.
.DESCRIPTION
Removes bloatware AppX packages, Edge, OneDrive, applies privacy/telemetry
registry tweaks, disables sponsored-app delivery, bypasses hardware checks,
copies autounattend.xml for local-account OOBE, and deletes unwanted
scheduled-task definition files — all against an already-mounted WIM image.
Mounting and dismounting the WIM is the responsibility of the caller
(e.g. Invoke-WinUtilISOModify).
.PARAMETER ScratchDir
Full path to the directory where the Windows image is currently mounted
(the "scratchdir"). Example: C:\Temp\WinUtil_Win11ISO_20260222\wim_mount
.PARAMETER Log
Optional ScriptBlock used for progress/status logging.
Receives a single [string] message argument.
Defaults to Write-Output when not supplied.
.EXAMPLE
Invoke-WinUtilISOScript -ScratchDir "C:\Temp\wim_mount"
Invoke-WinUtilISOScript -ScratchDir $mountDir -Log { param($m) Write-Host $m }
.NOTES
Author : Chris Titus @christitustech
GitHub : https://github.com/ChrisTitusTech
Version : 26.02.22
#>
param (
[Parameter(Mandatory)][string]$ScratchDir,
[scriptblock]$Log = { param($m) Write-Output $m }
)
# ── Resolve admin group name (for takeown / icacls) ──────────────────────
$adminSID = New-Object System.Security.Principal.SecurityIdentifier('S-1-5-32-544')
$adminGroup = $adminSID.Translate([System.Security.Principal.NTAccount])
# ── Local helpers ─────────────────────────────────────────────────────────
function _ISOScript-SetReg {
param ([string]$path, [string]$name, [string]$type, [string]$value)
try {
& reg add $path /v $name /t $type /d $value /f | Out-Null
& $Log "Set registry value: $path\$name"
} catch {
& $Log "Error setting registry value: $_"
}
}
function _ISOScript-DelReg {
param ([string]$path)
try {
& reg delete $path /f | Out-Null
& $Log "Removed registry key: $path"
} catch {
& $Log "Error removing registry key: $_"
}
}
# ═════════════════════════════════════════════════════════════════════════
# 1. Remove provisioned AppX packages
# ═════════════════════════════════════════════════════════════════════════
& $Log "Removing provisioned AppX packages..."
$packages = & dism /English "/image:$ScratchDir" /Get-ProvisionedAppxPackages |
ForEach-Object {
if ($_ -match 'PackageName : (.*)') { $matches[1] }
}
$packagePrefixes = @(
'AppUp.IntelManagementandSecurityStatus',
'Clipchamp.Clipchamp',
'DolbyLaboratories.DolbyAccess',
'DolbyLaboratories.DolbyDigitalPlusDecoderOEM',
'Microsoft.BingNews',
'Microsoft.BingSearch',
'Microsoft.BingWeather',
'Microsoft.Copilot',
'Microsoft.Windows.CrossDevice',
'Microsoft.GamingApp',
'Microsoft.GetHelp',
'Microsoft.Getstarted',
'Microsoft.Microsoft3DViewer',
'Microsoft.MicrosoftOfficeHub',
'Microsoft.MicrosoftSolitaireCollection',
'Microsoft.MicrosoftStickyNotes',
'Microsoft.MixedReality.Portal',
'Microsoft.MSPaint',
'Microsoft.Office.OneNote',
'Microsoft.OfficePushNotificationUtility',
'Microsoft.OutlookForWindows',
'Microsoft.Paint',
'Microsoft.People',
'Microsoft.PowerAutomateDesktop',
'Microsoft.SkypeApp',
'Microsoft.StartExperiencesApp',
'Microsoft.Todos',
'Microsoft.Wallet',
'Microsoft.Windows.DevHome',
'Microsoft.Windows.Copilot',
'Microsoft.Windows.Teams',
'Microsoft.WindowsAlarms',
'Microsoft.WindowsCamera',
'microsoft.windowscommunicationsapps',
'Microsoft.WindowsFeedbackHub',
'Microsoft.WindowsMaps',
'Microsoft.WindowsSoundRecorder',
'Microsoft.WindowsTerminal',
'Microsoft.ZuneMusic',
'Microsoft.ZuneVideo',
'MicrosoftCorporationII.MicrosoftFamily',
'MicrosoftCorporationII.QuickAssist',
'MSTeams',
'MicrosoftTeams',
'Microsoft.549981C3F5F10'
)
$packagesToRemove = $packages | Where-Object {
$pkg = $_
$packagePrefixes | Where-Object { $pkg -like "*$_*" }
}
foreach ($package in $packagesToRemove) {
& dism /English "/image:$ScratchDir" /Remove-ProvisionedAppxPackage "/PackageName:$package"
}
# ═════════════════════════════════════════════════════════════════════════
# 2. Remove Edge
# ═════════════════════════════════════════════════════════════════════════
& $Log "Removing Edge..."
Remove-Item -Path "$ScratchDir\Program Files (x86)\Microsoft\Edge" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -Path "$ScratchDir\Program Files (x86)\Microsoft\EdgeUpdate" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -Path "$ScratchDir\Program Files (x86)\Microsoft\EdgeCore" -Recurse -Force -ErrorAction SilentlyContinue
& takeown /f "$ScratchDir\Windows\System32\Microsoft-Edge-Webview" /r | Out-Null
& icacls "$ScratchDir\Windows\System32\Microsoft-Edge-Webview" /grant "$($adminGroup.Value):(F)" /T /C | Out-Null
Remove-Item -Path "$ScratchDir\Windows\System32\Microsoft-Edge-Webview" -Recurse -Force -ErrorAction SilentlyContinue
# ═════════════════════════════════════════════════════════════════════════
# 3. Remove OneDrive
# ═════════════════════════════════════════════════════════════════════════
& $Log "Removing OneDrive..."
& takeown /f "$ScratchDir\Windows\System32\OneDriveSetup.exe" | Out-Null
& icacls "$ScratchDir\Windows\System32\OneDriveSetup.exe" /grant "$($adminGroup.Value):(F)" /T /C | Out-Null
Remove-Item -Path "$ScratchDir\Windows\System32\OneDriveSetup.exe" -Force -ErrorAction SilentlyContinue
# ═════════════════════════════════════════════════════════════════════════
# 4. Registry tweaks
# ═════════════════════════════════════════════════════════════════════════
& $Log "Loading offline registry hives..."
reg load HKLM\zCOMPONENTS "$ScratchDir\Windows\System32\config\COMPONENTS" | Out-Null
reg load HKLM\zDEFAULT "$ScratchDir\Windows\System32\config\default" | Out-Null
reg load HKLM\zNTUSER "$ScratchDir\Users\Default\ntuser.dat" | Out-Null
reg load HKLM\zSOFTWARE "$ScratchDir\Windows\System32\config\SOFTWARE" | Out-Null
reg load HKLM\zSYSTEM "$ScratchDir\Windows\System32\config\SYSTEM" | Out-Null
& $Log "Bypassing system requirements..."
_ISOScript-SetReg 'HKLM\zDEFAULT\Control Panel\UnsupportedHardwareNotificationCache' 'SV1' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zDEFAULT\Control Panel\UnsupportedHardwareNotificationCache' 'SV2' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Control Panel\UnsupportedHardwareNotificationCache' 'SV1' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Control Panel\UnsupportedHardwareNotificationCache' 'SV2' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zSYSTEM\Setup\LabConfig' 'BypassCPUCheck' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zSYSTEM\Setup\LabConfig' 'BypassRAMCheck' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zSYSTEM\Setup\LabConfig' 'BypassSecureBootCheck' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zSYSTEM\Setup\LabConfig' 'BypassStorageCheck' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zSYSTEM\Setup\LabConfig' 'BypassTPMCheck' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zSYSTEM\Setup\MoSetup' 'AllowUpgradesWithUnsupportedTPMOrCPU' 'REG_DWORD' '1'
& $Log "Disabling sponsored apps..."
_ISOScript-SetReg 'HKLM\zNTUSER\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'OemPreInstalledAppsEnabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'PreInstalledAppsEnabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'SilentInstalledAppsEnabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\CloudContent' 'DisableWindowsConsumerFeatures' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'ContentDeliveryAllowed' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zSOFTWARE\Microsoft\PolicyManager\current\device\Start' 'ConfigureStartPins' 'REG_SZ' '{"pinnedList": [{}]}'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'FeatureManagementEnabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'PreInstalledAppsEverEnabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'SoftLandingEnabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'SubscribedContentEnabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'SubscribedContent-310093Enabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'SubscribedContent-338388Enabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'SubscribedContent-338389Enabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'SubscribedContent-338393Enabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'SubscribedContent-353694Enabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'SubscribedContent-353696Enabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'SystemPaneSuggestionsEnabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\PushToInstall' 'DisablePushToInstall' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\MRT' 'DontOfferThroughWUAU' 'REG_DWORD' '1'
_ISOScript-DelReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager\Subscriptions'
_ISOScript-DelReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager\SuggestedApps'
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\CloudContent' 'DisableConsumerAccountStateContent' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\CloudContent' 'DisableCloudOptimizedContent' 'REG_DWORD' '1'
& $Log "Enabling local accounts on OOBE..."
_ISOScript-SetReg 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\OOBE' 'BypassNRO' 'REG_DWORD' '1'
$sysprepDest = "$ScratchDir\Windows\System32\Sysprep\autounattend.xml"
Set-Content -Path $sysprepDest -Value $WinUtilAutounattendXml -Encoding UTF8 -Force
& $Log "Written autounattend.xml to Sysprep directory."
& $Log "Disabling reserved storage..."
_ISOScript-SetReg 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\ReserveManager' 'ShippedWithReserves' 'REG_DWORD' '0'
& $Log "Disabling BitLocker device encryption..."
_ISOScript-SetReg 'HKLM\zSYSTEM\ControlSet001\Control\BitLocker' 'PreventDeviceEncryption' 'REG_DWORD' '1'
& $Log "Disabling Chat icon..."
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Windows Chat' 'ChatIcon' 'REG_DWORD' '3'
_ISOScript-SetReg 'HKLM\zNTUSER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced' 'TaskbarMn' 'REG_DWORD' '0'
& $Log "Removing Edge registry entries..."
_ISOScript-DelReg 'HKLM\zSOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge'
_ISOScript-DelReg 'HKLM\zSOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge Update'
& $Log "Disabling OneDrive folder backup..."
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\OneDrive' 'DisableFileSyncNGSC' 'REG_DWORD' '1'
& $Log "Disabling telemetry..."
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\AdvertisingInfo' 'Enabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\Privacy' 'TailoredExperiencesWithDiagnosticDataEnabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Speech_OneCore\Settings\OnlineSpeechPrivacy' 'HasAccepted' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Input\TIPC' 'Enabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\InputPersonalization' 'RestrictImplicitInkCollection' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\InputPersonalization' 'RestrictImplicitTextCollection' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\InputPersonalization\TrainedDataStore' 'HarvestContacts' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zNTUSER\Software\Microsoft\Personalization\Settings' 'AcceptedPrivacyPolicy' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\DataCollection' 'AllowTelemetry' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zSYSTEM\ControlSet001\Services\dmwappushservice' 'Start' 'REG_DWORD' '4'
& $Log "Preventing installation of DevHome and Outlook..."
_ISOScript-SetReg 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate' 'workCompleted' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\OutlookUpdate' 'workCompleted' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\DevHomeUpdate' 'workCompleted' 'REG_DWORD' '1'
_ISOScript-DelReg 'HKLM\zSOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate'
_ISOScript-DelReg 'HKLM\zSOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\DevHomeUpdate'
& $Log "Disabling Copilot..."
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\WindowsCopilot' 'TurnOffWindowsCopilot' 'REG_DWORD' '1'
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\Edge' 'HubsSidebarEnabled' 'REG_DWORD' '0'
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Explorer' 'DisableSearchBoxSuggestions' 'REG_DWORD' '1'
& $Log "Preventing installation of Teams..."
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\Teams' 'DisableInstallation' 'REG_DWORD' '1'
& $Log "Preventing installation of new Outlook..."
_ISOScript-SetReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Windows Mail' 'PreventRun' 'REG_DWORD' '1'
& $Log "Unloading offline registry hives..."
reg unload HKLM\zCOMPONENTS | Out-Null
reg unload HKLM\zDEFAULT | Out-Null
reg unload HKLM\zNTUSER | Out-Null
reg unload HKLM\zSOFTWARE | Out-Null
reg unload HKLM\zSYSTEM | Out-Null
# ═════════════════════════════════════════════════════════════════════════
# 5. Delete scheduled task definition files
# ═════════════════════════════════════════════════════════════════════════
& $Log "Deleting scheduled task definition files..."
$tasksPath = "$ScratchDir\Windows\System32\Tasks"
Remove-Item "$tasksPath\Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser" -Force -ErrorAction SilentlyContinue
Remove-Item "$tasksPath\Microsoft\Windows\Customer Experience Improvement Program" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item "$tasksPath\Microsoft\Windows\Application Experience\ProgramDataUpdater" -Force -ErrorAction SilentlyContinue
Remove-Item "$tasksPath\Microsoft\Windows\Chkdsk\Proxy" -Force -ErrorAction SilentlyContinue
Remove-Item "$tasksPath\Microsoft\Windows\Windows Error Reporting\QueueReporting" -Force -ErrorAction SilentlyContinue
& $Log "Scheduled task files deleted."
}

View File

@@ -14,11 +14,15 @@ function Set-WinUtilProgressbar{
[int]$Percent [int]$Percent
) )
$sync.form.Dispatcher.Invoke([action]{$sync.progressBarTextBlock.Text = $label}) if($PARAM_NOUI) {
$sync.form.Dispatcher.Invoke([action]{$sync.progressBarTextBlock.ToolTip = $label}) return;
}
Invoke-WPFUIThread -ScriptBlock {$sync.progressBarTextBlock.Text = $label}
Invoke-WPFUIThread -ScriptBlock {$sync.progressBarTextBlock.ToolTip = $label}
if ($percent -lt 5 ) { if ($percent -lt 5 ) {
$percent = 5 # Ensure the progress bar is not empty, as it looks weird $percent = 5 # Ensure the progress bar is not empty, as it looks weird
} }
$sync.form.Dispatcher.Invoke([action]{ $sync.ProgressBar.Value = $percent}) Invoke-WPFUIThread -ScriptBlock { $sync.ProgressBar.Value = $percent}
} }

View File

@@ -10,12 +10,12 @@ function Show-WPFInstallAppBusy {
param ( param (
$text = "Installing apps..." $text = "Installing apps..."
) )
$sync.form.Dispatcher.Invoke([action]{ Invoke-WPFUIThread -ScriptBlock {
$sync.InstallAppAreaOverlay.Visibility = [Windows.Visibility]::Visible $sync.InstallAppAreaOverlay.Visibility = [Windows.Visibility]::Visible
$sync.InstallAppAreaOverlay.Width = $($sync.InstallAppAreaScrollViewer.ActualWidth * 0.4) $sync.InstallAppAreaOverlay.Width = $($sync.InstallAppAreaScrollViewer.ActualWidth * 0.4)
$sync.InstallAppAreaOverlay.Height = $($sync.InstallAppAreaScrollViewer.ActualWidth * 0.4) $sync.InstallAppAreaOverlay.Height = $($sync.InstallAppAreaScrollViewer.ActualWidth * 0.4)
$sync.InstallAppAreaOverlayText.Text = $text $sync.InstallAppAreaOverlayText.Text = $text
$sync.InstallAppAreaBorder.IsEnabled = $false $sync.InstallAppAreaBorder.IsEnabled = $false
$sync.InstallAppAreaScrollViewer.Effect.Radius = 5 $sync.InstallAppAreaScrollViewer.Effect.Radius = 5
})
} }
}

View File

@@ -18,6 +18,31 @@ function Invoke-WPFButton {
Set-WinUtilProgressBar -label "" -percent 0 Set-WinUtilProgressBar -label "" -percent 0
} }
# Check if button is defined in feature config with function or InvokeScript
if ($sync.configs.feature.$Button) {
$buttonConfig = $sync.configs.feature.$Button
# If button has a function defined, call it
if ($buttonConfig.function) {
$functionName = $buttonConfig.function
if (Get-Command $functionName -ErrorAction SilentlyContinue) {
& $functionName
return
}
}
# If button has InvokeScript defined, execute the scripts
if ($buttonConfig.InvokeScript -and $buttonConfig.InvokeScript.Count -gt 0) {
foreach ($script in $buttonConfig.InvokeScript) {
if (-not [string]::IsNullOrWhiteSpace($script)) {
Invoke-Expression $script
}
}
return
}
}
# Fallback to hard-coded switch for buttons not in feature.json
Switch -Wildcard ($Button) { Switch -Wildcard ($Button) {
"WPFTab?BT" {Invoke-WPFTab $Button} "WPFTab?BT" {Invoke-WPFTab $Button}
"WPFInstall" {Invoke-WPFInstall} "WPFInstall" {Invoke-WPFInstall}
@@ -34,34 +59,14 @@ function Invoke-WPFButton {
"WPFAddUltPerf" {Invoke-WPFUltimatePerformance -State "Enable"} "WPFAddUltPerf" {Invoke-WPFUltimatePerformance -State "Enable"}
"WPFRemoveUltPerf" {Invoke-WPFUltimatePerformance -State "Disable"} "WPFRemoveUltPerf" {Invoke-WPFUltimatePerformance -State "Disable"}
"WPFundoall" {Invoke-WPFundoall} "WPFundoall" {Invoke-WPFundoall}
"WPFFeatureInstall" {Invoke-WPFFeatureInstall}
"WPFPanelDISM" {Invoke-WPFSystemRepair}
"WPFPanelAutologin" {Invoke-WPFPanelAutologin}
"WPFPanelComputer" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelControl" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelNetwork" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelPower" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelPrinter" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelRegion" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelRestore" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelSound" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelSystem" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelTimedate" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelUser" {Invoke-WPFControlPanel -Panel $button}
"WPFUpdatesdefault" {Invoke-WPFUpdatesdefault} "WPFUpdatesdefault" {Invoke-WPFUpdatesdefault}
"WPFFixesUpdate" {Invoke-WPFFixesUpdate}
"WPFFixesWinget" {Invoke-WPFFixesWinget}
"WPFRunAdobeCCCleanerTool" {Invoke-WPFRunAdobeCCCleanerTool} "WPFRunAdobeCCCleanerTool" {Invoke-WPFRunAdobeCCCleanerTool}
"WPFFixesNetwork" {Invoke-WPFFixesNetwork}
"WPFUpdatesdisable" {Invoke-WPFUpdatesdisable} "WPFUpdatesdisable" {Invoke-WPFUpdatesdisable}
"WPFUpdatessecurity" {Invoke-WPFUpdatessecurity} "WPFUpdatessecurity" {Invoke-WPFUpdatessecurity}
"WPFWinUtilShortcut" {Invoke-WPFShortcut -ShortcutToAdd "WinUtil" -RunAsAdmin $true} "WPFWinUtilShortcut" {Invoke-WPFShortcut -ShortcutToAdd "WinUtil" -RunAsAdmin $true}
"WPFGetInstalled" {Invoke-WPFGetInstalled -CheckBox "winget"} "WPFGetInstalled" {Invoke-WPFGetInstalled -CheckBox "winget"}
"WPFGetInstalledTweaks" {Invoke-WPFGetInstalled -CheckBox "tweaks"} "WPFGetInstalledTweaks" {Invoke-WPFGetInstalled -CheckBox "tweaks"}
"WPFCloseButton" {Invoke-WPFCloseButton} "WPFCloseButton" {$sync.Form.Close(); Write-Host "Bye bye!"}
"WPFWinUtilInstallPSProfile" {Invoke-WinUtilInstallPSProfile}
"WPFWinUtilUninstallPSProfile" {Invoke-WinUtilUninstallPSProfile}
"WPFWinUtilSSHServer" {Invoke-WPFSSHServer}
"WPFselectedAppsButton" {$sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen} "WPFselectedAppsButton" {$sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen}
"WPFToggleFOSSHighlight" { "WPFToggleFOSSHighlight" {
if ($sync.WPFToggleFOSSHighlight.IsChecked) { if ($sync.WPFToggleFOSSHighlight.IsChecked) {

View File

@@ -1,12 +0,0 @@
function Invoke-WPFCloseButton {
<#
.SYNOPSIS
Close application
.PARAMETER Button
#>
$sync["Form"].Close()
Write-Host "Bye bye!"
}

View File

@@ -1,26 +0,0 @@
function Invoke-WPFControlPanel {
<#
.SYNOPSIS
Opens the requested legacy panel
.PARAMETER Panel
The panel to open
#>
param($Panel)
switch ($Panel) {
"WPFPanelControl" {control}
"WPFPanelComputer" {compmgmt.msc}
"WPFPanelNetwork" {ncpa.cpl}
"WPFPanelPower" {powercfg.cpl}
"WPFPanelPrinter" {Start-Process "shell:::{A8A91A66-3A7D-4424-8D24-04E180695C7A}"}
"WPFPanelRegion" {intl.cpl}
"WPFPanelRestore" {rstrui.exe}
"WPFPanelSound" {mmsys.cpl}
"WPFPanelSystem" {sysdm.cpl}
"WPFPanelTimedate" {timedate.cpl}
"WPFPanelUser" {control userpasswords2}
}
}

View File

@@ -12,21 +12,25 @@ function Invoke-WPFFeatureInstall {
return return
} }
$Features = $sync.selectedFeatures $handle = Invoke-WPFRunspace -ScriptBlock {
$Features = $sync.selectedFeatures
Invoke-WPFRunspace -ArgumentList $Features -DebugPreference $DebugPreference -ScriptBlock {
param($Features, $DebugPreference)
$sync.ProcessRunning = $true $sync.ProcessRunning = $true
if ($Features.count -eq 1) { if ($Features.count -eq 1) {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }
} else { } else {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
} }
Invoke-WinUtilFeatureInstall $Features $x = 0
$Features | ForEach-Object {
Invoke-WinUtilFeatureInstall $Feature
$X++
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($x/$CheckBox.Count) }
}
$sync.ProcessRunning = $false $sync.ProcessRunning = $false
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
Write-Host "===================================" Write-Host "==================================="
Write-Host "--- Features are Installed ---" Write-Host "--- Features are Installed ---"

View File

@@ -20,13 +20,13 @@ function Invoke-WPFGetInstalled {
} }
$managerPreference = $sync["ManagerPreference"] $managerPreference = $sync["ManagerPreference"]
Invoke-WPFRunspace -ParameterList @(("managerPreference", $managerPreference),("checkbox", $checkbox)) -DebugPreference $DebugPreference -ScriptBlock { Invoke-WPFRunspace -ParameterList @(("managerPreference", $managerPreference),("checkbox", $checkbox)) -ScriptBlock {
param ( param (
[string]$checkbox, [string]$checkbox,
[PackageManagers]$managerPreference [PackageManagers]$managerPreference
) )
$sync.ProcessRunning = $true $sync.ProcessRunning = $true
$sync.form.Dispatcher.Invoke([action] { Set-WinUtilTaskbaritem -state "Indeterminate" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Indeterminate" }
if ($checkbox -eq "winget") { if ($checkbox -eq "winget") {
Write-Host "Getting Installed Programs..." Write-Host "Getting Installed Programs..."
@@ -48,6 +48,6 @@ function Invoke-WPFGetInstalled {
Write-Host "Done..." Write-Host "Done..."
$sync.ProcessRunning = $false $sync.ProcessRunning = $false
$sync.form.Dispatcher.Invoke([action] { Set-WinUtilTaskbaritem -state "None" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" }
} }
} }

View File

@@ -71,8 +71,10 @@ function Invoke-WPFImpex {
# $flattenedJson = $jsonFile.PSObject.Properties.Where({ $_.Name -ne "Install" }).ForEach({ $_.Value }) # $flattenedJson = $jsonFile.PSObject.Properties.Where({ $_.Name -ne "Install" }).ForEach({ $_.Value })
$flattenedJson = $jsonFile $flattenedJson = $jsonFile
Update-WinUtilSelections -flatJson $flattenedJson Update-WinUtilSelections -flatJson $flattenedJson
# TODO test with toggles
Reset-WPFCheckBoxes -doToggles $true if (!$PARAM_NOUI) {
Reset-WPFCheckBoxes -doToggles $true
}
} }
} catch { } catch {
Write-Error "An error occurred while importing: $_" Write-Error "An error occurred while importing: $_"

View File

@@ -1,13 +1,12 @@
function Invoke-WPFInstall { function Invoke-WPFInstall {
param (
[Parameter(Mandatory=$false)]
[PSObject[]]$PackagesToInstall = $($sync.selectedApps | Foreach-Object { $sync.configs.applicationsHashtable.$_ })
)
<# <#
.SYNOPSIS .SYNOPSIS
Installs the selected programs using winget, if one or more of the selected programs are already installed on the system, winget will try and perform an upgrade if there's a newer version to install. Installs the selected programs using winget, if one or more of the selected programs are already installed on the system, winget will try and perform an upgrade if there's a newer version to install.
#> #>
$PackagesToInstall = $sync.selectedApps | Foreach-Object { $sync.configs.applicationsHashtable.$_ }
if($sync.ProcessRunning) { if($sync.ProcessRunning) {
$msg = "[Invoke-WPFInstall] An Install process is currently running." $msg = "[Invoke-WPFInstall] An Install process is currently running."
[System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) [System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning)
@@ -22,8 +21,8 @@ function Invoke-WPFInstall {
$ManagerPreference = $sync["ManagerPreference"] $ManagerPreference = $sync["ManagerPreference"]
Invoke-WPFRunspace -ParameterList @(("PackagesToInstall", $PackagesToInstall),("ManagerPreference", $ManagerPreference)) -DebugPreference $DebugPreference -ScriptBlock { $handle = Invoke-WPFRunspace -ParameterList @(("PackagesToInstall", $PackagesToInstall),("ManagerPreference", $ManagerPreference)) -ScriptBlock {
param($PackagesToInstall, $ManagerPreference, $DebugPreference) param($PackagesToInstall, $ManagerPreference)
$packagesSorted = Get-WinUtilSelectedPackages -PackageList $PackagesToInstall -Preference $ManagerPreference $packagesSorted = Get-WinUtilSelectedPackages -PackageList $PackagesToInstall -Preference $ManagerPreference
@@ -45,12 +44,12 @@ function Invoke-WPFInstall {
Write-Host "===========================================" Write-Host "==========================================="
Write-Host "-- Installs have finished ---" Write-Host "-- Installs have finished ---"
Write-Host "===========================================" Write-Host "==========================================="
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
} catch { } catch {
Write-Host "===========================================" Write-Host "==========================================="
Write-Host "Error: $_" Write-Host "Error: $_"
Write-Host "===========================================" Write-Host "==========================================="
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
} }
$sync.ProcessRunning = $False $sync.ProcessRunning = $False
} }

View File

@@ -27,8 +27,7 @@ function Invoke-WPFRunspace {
Param ( Param (
$ScriptBlock, $ScriptBlock,
$ArgumentList, $ArgumentList,
$ParameterList, $ParameterList
$DebugPreference
) )
# Create a PowerShell instance # Create a PowerShell instance
@@ -41,7 +40,7 @@ function Invoke-WPFRunspace {
foreach ($parameter in $ParameterList) { foreach ($parameter in $ParameterList) {
$script:powershell.AddParameter($parameter[0], $parameter[1]) $script:powershell.AddParameter($parameter[0], $parameter[1])
} }
$script:powershell.AddArgument($DebugPreference) # Pass DebugPreference to the script block
$script:powershell.RunspacePool = $sync.runspace $script:powershell.RunspacePool = $sync.runspace
# Execute the RunspacePool # Execute the RunspacePool

View File

@@ -6,7 +6,7 @@ function Invoke-WPFSSHServer {
#> #>
Invoke-WPFRunspace -DebugPreference $DebugPreference -ScriptBlock { Invoke-WPFRunspace -ScriptBlock {
Invoke-WinUtilSSHServer Invoke-WinUtilSSHServer

View File

@@ -2,13 +2,15 @@ function Invoke-WPFSystemRepair {
<# <#
.SYNOPSIS .SYNOPSIS
Checks for system corruption using SFC, and DISM Checks for system corruption using SFC, and DISM
Checks for disk failure using Chkdsk
.DESCRIPTION .DESCRIPTION
1. SFC - Fixes system file corruption, and fixes DISM if it was corrupted 1. Chkdsk - Checks for disk errors, which can cause system file corruption and notifies of early disk failure
2. DISM - Fixes system image corruption, and fixes SFC's system image if it was corrupted 2. SFC - scans protected system files for corruption and fixes them
3. Chkdsk - Checks for disk errors, which can cause system file corruption and notifies of early disk failure 3. DISM - Repair a corrupted Windows operating system image
#> #>
Start-Process cmd.exe -ArgumentList "/c chkdsk.exe /scan /perf" -NoNewWindow -Wait
Start-Process cmd.exe -ArgumentList "/c chkdsk /scan /perf" -NoNewWindow -Wait
Start-Process cmd.exe -ArgumentList "/c sfc /scannow" -NoNewWindow -Wait Start-Process cmd.exe -ArgumentList "/c sfc /scannow" -NoNewWindow -Wait
Start-Process cmd.exe -ArgumentList "/c dism /online /cleanup-image /restorehealth" -NoNewWindow -Wait Start-Process cmd.exe -ArgumentList "/c dism /online /cleanup-image /restorehealth" -NoNewWindow -Wait

View File

@@ -0,0 +1,21 @@
function Invoke-WPFUIThread {
<#
.SYNOPSIS
Creates and runs a task on Winutil's WPF Forms thread.
.PARAMETER ScriptBlock
The scriptblock to invoke in the thread
#>
[CmdletBinding()]
Param (
$ScriptBlock
)
if ($PARAM_NOUI) {
return;
}
$sync.form.Dispatcher.Invoke([action]$ScriptBlock)
}

View File

@@ -32,8 +32,8 @@ function Invoke-WPFUnInstall {
$ManagerPreference = $sync["ManagerPreference"] $ManagerPreference = $sync["ManagerPreference"]
Invoke-WPFRunspace -ParameterList @(("PackagesToUninstall", $PackagesToUninstall),("ManagerPreference", $ManagerPreference)) -DebugPreference $DebugPreference -ScriptBlock { Invoke-WPFRunspace -ParameterList @(("PackagesToUninstall", $PackagesToUninstall),("ManagerPreference", $ManagerPreference)) -ScriptBlock {
param($PackagesToUninstall, $ManagerPreference, $DebugPreference) param($PackagesToUninstall, $ManagerPreference)
$packagesSorted = Get-WinUtilSelectedPackages -PackageList $PackagesToUninstall -Preference $ManagerPreference $packagesSorted = Get-WinUtilSelectedPackages -PackageList $PackagesToUninstall -Preference $ManagerPreference
$packagesWinget = $packagesSorted[[PackageManagers]::Winget] $packagesWinget = $packagesSorted[[PackageManagers]::Winget]
@@ -54,12 +54,12 @@ function Invoke-WPFUnInstall {
Write-Host "===========================================" Write-Host "==========================================="
Write-Host "-- Uninstalls have finished ---" Write-Host "-- Uninstalls have finished ---"
Write-Host "===========================================" Write-Host "==========================================="
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
} catch { } catch {
Write-Host "===========================================" Write-Host "==========================================="
Write-Host "Error: $_" Write-Host "Error: $_"
Write-Host "===========================================" Write-Host "==========================================="
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
} }
$sync.ProcessRunning = $False $sync.ProcessRunning = $False

View File

@@ -25,30 +25,27 @@ function Invoke-WPFtweaksbutton {
Write-Debug "Number of tweaks to process: $($Tweaks.Count)" Write-Debug "Number of tweaks to process: $($Tweaks.Count)"
# The leading "," in the ParameterList is necessary because we only provide one argument and powershell cannot be convinced that we want a nested loop with only one argument otherwise # The leading "," in the ParameterList is necessary because we only provide one argument and powershell cannot be convinced that we want a nested loop with only one argument otherwise
Invoke-WPFRunspace -ParameterList @(,("tweaks",$tweaks)) -DebugPreference $DebugPreference -ScriptBlock { $handle = Invoke-WPFRunspace -ParameterList @(,("tweaks",$tweaks)) -ScriptBlock {
param( param($tweaks)
$tweaks,
$DebugPreference
)
Write-Debug "Inside Number of tweaks to process: $($Tweaks.Count)" Write-Debug "Inside Number of tweaks to process: $($Tweaks.Count)"
$sync.ProcessRunning = $true $sync.ProcessRunning = $true
if ($Tweaks.count -eq 1) { if ($Tweaks.count -eq 1) {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }
} else { } else {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }) Invoke-WPFUIThread -ScriptBlock{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
} }
# Execute other selected tweaks # Execute other selected tweaks
for ($i = 0; $i -lt $Tweaks.Count; $i++) { for ($i = 0; $i -lt $Tweaks.Count; $i++) {
Set-WinUtilProgressBar -Label "Applying $($tweaks[$i])" -Percent ($i / $tweaks.Count * 100) Set-WinUtilProgressBar -Label "Applying $($tweaks[$i])" -Percent ($i / $tweaks.Count * 100)
Invoke-WinUtilTweaks $tweaks[$i] Invoke-WinUtilTweaks $tweaks[$i]
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($i/$Tweaks.Count) }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($i/$Tweaks.Count) }
} }
Set-WinUtilProgressBar -Label "Tweaks finished" -Percent 100 Set-WinUtilProgressBar -Label "Tweaks finished" -Percent 100
$sync.ProcessRunning = $false $sync.ProcessRunning = $false
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
Write-Host "=================================" Write-Host "================================="
Write-Host "-- Tweaks are Finished ---" Write-Host "-- Tweaks are Finished ---"
Write-Host "=================================" Write-Host "================================="

View File

@@ -20,26 +20,26 @@ function Invoke-WPFundoall {
return return
} }
Invoke-WPFRunspace -ArgumentList $tweaks -DebugPreference $DebugPreference -ScriptBlock { Invoke-WPFRunspace -ArgumentList $tweaks -ScriptBlock {
param($tweaks, $DebugPreference) param($tweaks)
$sync.ProcessRunning = $true $sync.ProcessRunning = $true
if ($tweaks.count -eq 1) { if ($tweaks.count -eq 1) {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }
} else { } else {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
} }
for ($i = 0; $i -lt $tweaks.Count; $i++) { for ($i = 0; $i -lt $tweaks.Count; $i++) {
Set-WinUtilProgressBar -Label "Undoing $($tweaks[$i])" -Percent ($i / $tweaks.Count * 100) Set-WinUtilProgressBar -Label "Undoing $($tweaks[$i])" -Percent ($i / $tweaks.Count * 100)
Invoke-WinUtiltweaks $tweaks[$i] -undo $true Invoke-WinUtiltweaks $tweaks[$i] -undo $true
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($i/$tweaks.Count) }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($i/$tweaks.Count) }
} }
Set-WinUtilProgressBar -Label "Undo Tweaks Finished" -Percent 100 Set-WinUtilProgressBar -Label "Undo Tweaks Finished" -Percent 100
$sync.ProcessRunning = $false $sync.ProcessRunning = $false
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }) Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
Write-Host "==================================" Write-Host "=================================="
Write-Host "--- Undo Tweaks are Finished ---" Write-Host "--- Undo Tweaks are Finished ---"
Write-Host "==================================" Write-Host "=================================="

View File

@@ -0,0 +1,48 @@
function Invoke-WinUtilAutoRun {
<#
.SYNOPSIS
Runs Install, Tweaks, and Features with optional UI invocation.
#>
function BusyWait {
Start-Sleep -Seconds 5
while ($sync.ProcessRunning) {
Start-Sleep -Seconds 5
}
}
BusyWait
Write-Host "Applying tweaks..."
Invoke-WPFtweaksbutton
BusyWait
Write-Host "Applying toggles..."
$handle = Invoke-WPFRunspace -ScriptBlock {
$Toggles = $sync.selectedToggles
Write-Debug "Inside Number of toggles to process: $($Toggles.Count)"
$sync.ProcessRunning = $true
for ($i = 0; $i -lt $Tweaks.Count; $i++) {
Invoke-WinUtilTweaks $Toggles[$i]
}
$sync.ProcessRunning = $false
Write-Host "================================="
Write-Host "-- Toggles are Finished ---"
Write-Host "================================="
}
BusyWait
Write-Host "Applying features..."
Invoke-WPFFeatureInstall
BusyWait
Write-Host "Installing applications..."
Invoke-WPFInstall
BusyWait
Write-Host "Done."
}

View File

@@ -13,10 +13,14 @@ $maxthreads = [int]$env:NUMBER_OF_PROCESSORS
# Create a new session state for parsing variables into our runspace # Create a new session state for parsing variables into our runspace
$hashVars = New-object System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'sync',$sync,$Null $hashVars = New-object System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'sync',$sync,$Null
$debugVar = New-object System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'DebugPreference',$DebugPreference,$Null
$uiVar = New-object System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'PARAM_NOUI',$PARAM_NOUI,$Null
$InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
# Add the variable to the session state # Add the variable to the session state
$InitialSessionState.Variables.Add($hashVars) $InitialSessionState.Variables.Add($hashVars)
$InitialSessionState.Variables.Add($debugVar)
$InitialSessionState.Variables.Add($uiVar)
# Get every private function and add them to the session state # Get every private function and add them to the session state
$functions = Get-ChildItem function:\ | Where-Object { $_.Name -imatch 'winutil|WPF' } $functions = Get-ChildItem function:\ | Where-Object { $_.Name -imatch 'winutil|WPF' }
@@ -55,6 +59,42 @@ class GenericException : Exception {
GenericException($Message) : base($Message) {} GenericException($Message) : base($Message) {}
} }
# Load the configuration files
$sync.configs.applicationsHashtable = @{}
$sync.configs.applications.PSObject.Properties | ForEach-Object {
$sync.configs.applicationsHashtable[$_.Name] = $_.Value
}
Set-PackageManagerPreference
if ($PARAM_NOUI) {
Show-CTTLogo
if ($PARAM_CONFIG -and -not [string]::IsNullOrWhiteSpace($PARAM_CONFIG)) {
Write-Host "Running config file tasks..."
Invoke-WPFImpex -type "import" -Config $PARAM_CONFIG
if ($PARAM_RUN) {
Invoke-WinUtilAutoRun
}
else {
Write-Host "Did you forget to add '--Run'?";
}
$sync.runspace.Dispose()
$sync.runspace.Close()
[System.GC]::Collect()
Stop-Transcript
exit 1
}
else {
Write-Host "Cannot automatically run without a config file provided."
$sync.runspace.Dispose()
$sync.runspace.Close()
[System.GC]::Collect()
Stop-Transcript
exit 1
}
}
$inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^<Win.*', '<Window' $inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework') [void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
@@ -115,12 +155,7 @@ $sync.Form.Add_Loaded({
}) })
Invoke-WinutilThemeChange -init $true Invoke-WinutilThemeChange -init $true
# Load the configuration files
$sync.configs.applicationsHashtable = @{}
$sync.configs.applications.PSObject.Properties | ForEach-Object {
$sync.configs.applicationsHashtable[$_.Name] = $_.Value
}
# Now call the function with the final merged config # Now call the function with the final merged config
Invoke-WPFUIElements -configVariable $sync.configs.appnavigation -targetGridName "appscategory" -columncount 1 Invoke-WPFUIElements -configVariable $sync.configs.appnavigation -targetGridName "appscategory" -columncount 1
@@ -144,7 +179,6 @@ $xaml.SelectNodes("//*[@Name]") | ForEach-Object {$sync["$("$($psitem.Name)")"]
#Persist Package Manager preference across winutil restarts #Persist Package Manager preference across winutil restarts
$sync.ChocoRadioButton.Add_Checked({Set-PackageManagerPreference Choco}) $sync.ChocoRadioButton.Add_Checked({Set-PackageManagerPreference Choco})
$sync.WingetRadioButton.Add_Checked({Set-PackageManagerPreference Winget}) $sync.WingetRadioButton.Add_Checked({Set-PackageManagerPreference Winget})
Set-PackageManagerPreference
switch ($sync["ManagerPreference"]) { switch ($sync["ManagerPreference"]) {
"Choco" {$sync.ChocoRadioButton.IsChecked = $true; break} "Choco" {$sync.ChocoRadioButton.IsChecked = $true; break}
@@ -247,6 +281,7 @@ $commonKeyEvents = {
"T" { Invoke-WPFButton "WPFTab2BT"; $keyEventArgs.Handled = $true } # Navigate to Tweaks tab "T" { Invoke-WPFButton "WPFTab2BT"; $keyEventArgs.Handled = $true } # Navigate to Tweaks tab
"C" { Invoke-WPFButton "WPFTab3BT"; $keyEventArgs.Handled = $true } # Navigate to Config tab "C" { Invoke-WPFButton "WPFTab3BT"; $keyEventArgs.Handled = $true } # Navigate to Config tab
"U" { Invoke-WPFButton "WPFTab4BT"; $keyEventArgs.Handled = $true } # Navigate to Updates tab "U" { Invoke-WPFButton "WPFTab4BT"; $keyEventArgs.Handled = $true } # Navigate to Updates tab
"W" { Invoke-WPFButton "WPFTab5BT"; $keyEventArgs.Handled = $true } # Navigate to Win11ISO tab
} }
} }
# Handle Ctrl key combinations for specific actions # Handle Ctrl key combinations for specific actions
@@ -341,45 +376,11 @@ $sync["Form"].Add_ContentRendered({
$sync["Form"].Focus() $sync["Form"].Focus()
# maybe this is not the best place to load and execute config file? if ($PARAM_CONFIG -and -not [string]::IsNullOrWhiteSpace($PARAM_CONFIG)) {
# maybe community can help? Write-Host "Running config file tasks..."
if ($PARAM_CONFIG -and -not [string]::IsNullOrWhiteSpace($PARAM_CONFIG)) {
Invoke-WPFImpex -type "import" -Config $PARAM_CONFIG Invoke-WPFImpex -type "import" -Config $PARAM_CONFIG
if ($PARAM_RUN) { if ($PARAM_RUN) {
# Wait for any existing process to complete before starting Invoke-WinUtilAutoRun
while ($sync.ProcessRunning) {
Start-Sleep -Seconds 5
}
Start-Sleep -Seconds 5
Write-Host "Applying tweaks..."
if (-not $sync.ProcessRunning) {
Invoke-WPFtweaksbutton
while ($sync.ProcessRunning) {
Start-Sleep -Seconds 5
}
}
Start-Sleep -Seconds 5
Write-Host "Installing features..."
if (-not $sync.ProcessRunning) {
Invoke-WPFFeatureInstall
while ($sync.ProcessRunning) {
Start-Sleep -Seconds 5
}
}
Start-Sleep -Seconds 5
Write-Host "Installing applications..."
if (-not $sync.ProcessRunning) {
Invoke-WPFInstall
while ($sync.ProcessRunning) {
Start-Sleep -Seconds 1
}
}
Start-Sleep -Seconds 5
Write-Host "Done."
} }
} }
@@ -531,5 +532,44 @@ $sync["FontScalingApplyButton"].Add_Click({
Invoke-WPFPopup -Action "Hide" -Popups @("FontScaling") Invoke-WPFPopup -Action "Hide" -Popups @("FontScaling")
}) })
# ── Win11ISO Tab button handlers ──────────────────────────────────────────────
$sync["WPFWin11ISOBrowseButton"].Add_Click({
Write-Debug "WPFWin11ISOBrowseButton clicked"
Invoke-WinUtilISOBrowse
})
$sync["WPFWin11ISODownloadLink"].Add_Click({
Write-Debug "WPFWin11ISODownloadLink clicked"
Start-Process "https://www.microsoft.com/software-download/windows11"
})
$sync["WPFWin11ISOMountButton"].Add_Click({
Write-Debug "WPFWin11ISOMountButton clicked"
Invoke-WinUtilISOMountAndVerify
})
$sync["WPFWin11ISOModifyButton"].Add_Click({
Write-Debug "WPFWin11ISOModifyButton clicked"
Invoke-WinUtilISOModify
})
$sync["WPFWin11ISOExportButton"].Add_Click({
Write-Debug "WPFWin11ISOExportButton clicked"
Invoke-WinUtilISOExport
})
$sync["WPFWin11ISORefreshUSBButton"].Add_Click({
Write-Debug "WPFWin11ISORefreshUSBButton clicked"
Invoke-WinUtilISORefreshUSBDrives
})
$sync["WPFWin11ISOWriteUSBButton"].Add_Click({
Write-Debug "WPFWin11ISOWriteUSBButton clicked"
Invoke-WinUtilISOWriteUSB
})
# ──────────────────────────────────────────────────────────────────────────────
$sync["Form"].ShowDialog() | out-null $sync["Form"].ShowDialog() | out-null
Stop-Transcript Stop-Transcript

View File

@@ -7,16 +7,11 @@
#> #>
param ( param (
[switch]$Debug,
[string]$Config, [string]$Config,
[switch]$Run [switch]$Run,
[switch]$Noui
) )
# Set DebugPreference based on the -Debug switch
if ($Debug) {
$DebugPreference = "Continue"
}
if ($Config) { if ($Config) {
$PARAM_CONFIG = $Config $PARAM_CONFIG = $Config
} }
@@ -24,10 +19,14 @@ if ($Config) {
$PARAM_RUN = $false $PARAM_RUN = $false
# Handle the -Run switch # Handle the -Run switch
if ($Run) { if ($Run) {
Write-Host "Running config file tasks..."
$PARAM_RUN = $true $PARAM_RUN = $true
} }
$PARAM_NOUI = $false
if ($Noui) {
$PARAM_NOUI = $true
}
# Load DLLs # Load DLLs
Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Windows.Forms

239
tools/autounattend.xml Normal file
View File

@@ -0,0 +1,239 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Windows 11 Unattended Installation Answer File
================================================
UNIVERSAL — no modification required before use.
What this file does automatically:
• Installs "Windows 11 Pro" from any standard Microsoft ISO
• Bypasses the Microsoft-account OOBE requirement (local account)
• Skips the EULA, wireless, and privacy nag screens
• Leaves timezone, language, region, and user account to the user
at the two short OOBE screens that remain
What the user is prompted for during first-run (OOBE):
1. Region / Language / Keyboard (one screen)
2. Who will use this PC? (local account name + password)
Timezone is set to UTC and can be adjusted after login.
Computer name is auto-generated; rename at any time.
Tested against: Windows 11 Home / Pro / Home Single Language (amd64)
Pass order: windowsPE → specialize → oobeSystem
-->
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<!-- ═══════════════════════════════════════════════════════════════════
PASS 1 — windowsPE
Runs inside the installer environment before the OS is laid down.
Handles disk layout and image selection only.
Locale is intentionally omitted so the installer inherits the
language of whichever ISO is being used.
═══════════════════════════════════════════════════════════════════════ -->
<settings pass="windowsPE">
<!-- Setup / image selection -->
<component name="Microsoft-Windows-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- Disable dynamic updates during setup (keeps installation offline/fast) -->
<DynamicUpdate>
<Enable>false</Enable>
</DynamicUpdate>
<ImageInstall>
<OSImage>
<!-- CompactOS saves ~1.5 GB but is slower on spinning drives -->
<Compact>false</Compact>
<WillShowUI>OnError</WillShowUI>
<InstallFrom>
<!--
Select the edition by NAME rather than by index number.
Index numbers vary between ISO builds; the name is stable.
Change "Windows 11 Pro" to "Windows 11 Home" etc. if your
ISO only contains that edition. To choose interactively,
delete this entire <InstallFrom> block.
-->
<MetaData wcm:action="add">
<Key>/IMAGE/NAME</Key>
<Value>Windows 11 Pro</Value>
</MetaData>
</InstallFrom>
<!-- InstallTo is omitted — the Windows installer will prompt
the user to select the destination disk and partition. -->
</OSImage>
</ImageInstall>
<UserData>
<AcceptEula>true</AcceptEula>
<ProductKey>
<!--
Leave <Key> absent to use an existing digital licence or
to be prompted for one after setup.
Generic setup keys (allow setup to proceed; do NOT activate):
Home : YTMG3-N6DKC-DKB77-7M9GH-8HVX7
Home Single Language : 7HNRX-D7KGG-3K4RQ-4WPJ4-YTDFH
Pro : VK7JG-NPHTM-C97JM-9MPGT-3V66T
Education : YNMGQ-8RYV3-4PGQ3-C8XTP-7CFBY
Enterprise : XGVPP-NMH47-7TTHJ-W3FW7-8HV2C
-->
<WillShowUI>OnError</WillShowUI>
</ProductKey>
</UserData>
</component>
</settings>
<!-- ═══════════════════════════════════════════════════════════════════
PASS 2 — specialize
First boot into the installed OS.
Machine-level settings that do not vary by user or region.
═══════════════════════════════════════════════════════════════════════ -->
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- Auto-generate a unique computer name. Rename anytime:
Settings System About Rename this PC -->
<ComputerName>*</ComputerName>
<!--
UTC is the only timezone that is correct everywhere on Earth
without knowing where the machine will be used.
Windows will auto-adjust to local time once the user sets their
region, or they can change it in Settings Time & Language.
-->
<TimeZone>UTC</TimeZone>
<!-- Suppress the Teams/Chat auto-install prompt during setup -->
<ConfigureChatAutoInstall>false</ConfigureChatAutoInstall>
</component>
<!-- Reduce telemetry to the minimum permitted by the licence.
0 = Security (Enterprise/Education only; treated as 1 on other SKUs)
1 = Basic / Required diagnostic data ← effective minimum for Home/Pro -->
<component name="Microsoft-Windows-SQMAPI"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CEIPEnabled>0</CEIPEnabled>
</component>
</settings>
<!-- ═══════════════════════════════════════════════════════════════════
PASS 3 — oobeSystem
Out-of-Box Experience (first-run wizard).
Screens shown to the user (everything else is suppressed):
① Region / Language / Keyboard layout — user picks their locale
② Create a local account — user picks name + password
Screens suppressed automatically:
• EULA
• "Sign in with Microsoft" / online account
• Wi-Fi selection (can be done after login)
• "Let Microsoft and apps use your location", Cortana, etc.
Locale settings are intentionally omitted here so that Windows
applies whatever the user selects on screen ①.
═══════════════════════════════════════════════════════════════════════ -->
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<OOBE>
<!-- Suppress the licence agreement — already accepted in windowsPE -->
<HideEULAPage>true</HideEULAPage>
<!-- KEEP false — this is the screen where the user creates -->
<!-- their local account (name + password). Setting it true -->
<!-- would skip account creation entirely, leaving only the -->
<!-- built-in Administrator account. -->
<HideLocalAccountScreen>false</HideLocalAccountScreen>
<!-- Suppress "Sign in with Microsoft" screens.
The user goes straight to local account creation. -->
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<!-- Skip Wi-Fi setup — can be connected after first login -->
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<!-- Suppress the privacy / recommended-settings nag screen -->
<ProtectYourPC>3</ProtectYourPC>
</OOBE>
<!--
UserAccounts and AutoLogon are intentionally absent.
The user creates their own account on the OOBE screen above.
This is the safest and most universally applicable approach:
no hardcoded credentials ship inside the answer file.
If you want to pre-create an account instead, add:
<UserAccounts>
<LocalAccounts>
<LocalAccount wcm:action="add">
<Name>YourUsername</Name>
<Password>
<Value>YourPassword</Value>
<PlainText>true</PlainText>
</Password>
<DisplayName>Your Full Name</DisplayName>
<Group>Administrators</Group>
</LocalAccount>
</LocalAccounts>
</UserAccounts>
<AutoLogon>
<Enabled>true</Enabled>
<LogonCount>1</LogonCount>
<Username>YourUsername</Username>
<Password>
<Value>YourPassword</Value>
<PlainText>true</PlainText>
</Password>
</AutoLogon>
And set HideLocalAccountScreen to true above.
-->
<!--
Optional: run a script on first logon.
Uncomment and adjust the path/command as needed.
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<Order>1</Order>
<CommandLine>powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://christitus.com/win | iex"</CommandLine>
<Description>Launch WinUtil post-install</Description>
<RequiresUserInput>false</RequiresUserInput>
</SynchronousCommand>
</FirstLogonCommands>
-->
</component>
</settings>
</unattend>

View File

@@ -1,9 +1,7 @@
<# <#
.DESCRIPTION .DESCRIPTION
Generates Hugo-compatible markdown files for the development documentation Generates Hugo markdown docs from config/tweaks.json and config/feature.json.
based on config/tweaks.json and config/feature.json. Run by the GitHub Actions docs workflow before Hugo build.
Each JSON entry gets its own .md file with the raw JSON snippet or PowerShell function embedded.
Called by the GitHub Actions docs workflow before Hugo build.
#> #>
function Update-Progress { function Update-Progress {
@@ -18,11 +16,7 @@ function Update-Progress {
} }
function Get-RawJsonBlock { function Get-RawJsonBlock {
<# # Returns the raw JSON text and 1-based start line for an item, excluding the "link" property.
.SYNOPSIS
Extracts the raw JSON text for a specific item from a JSON file's lines.
Returns the line number and raw text, excluding the "link" property and closing brace.
#>
param ( param (
[Parameter(Mandatory)] [Parameter(Mandatory)]
[string]$ItemName, [string]$ItemName,
@@ -32,13 +26,12 @@ function Get-RawJsonBlock {
) )
$escapedName = [regex]::Escape($ItemName) $escapedName = [regex]::Escape($ItemName)
$startIndex = -1 $startIndex = -1
$startIndent = "" $startIndent = ""
# Find the line containing "ItemName": {
for ($i = 0; $i -lt $JsonLines.Count; $i++) { for ($i = 0; $i -lt $JsonLines.Count; $i++) {
if ($JsonLines[$i] -match "^(\s*)`"$escapedName`"\s*:\s*\{") { if ($JsonLines[$i] -match "^(\s*)`"$escapedName`"\s*:\s*\{") {
$startIndex = $i $startIndex = $i
$startIndent = $matches[1] $startIndent = $matches[1]
break break
} }
@@ -49,9 +42,8 @@ function Get-RawJsonBlock {
return $null return $null
} }
# Find the closing } at the same indentation level
$escapedIndent = [regex]::Escape($startIndent) $escapedIndent = [regex]::Escape($startIndent)
$endIndex = -1 $endIndex = -1
for ($i = ($startIndex + 1); $i -lt $JsonLines.Count; $i++) { for ($i = ($startIndex + 1); $i -lt $JsonLines.Count; $i++) {
if ($JsonLines[$i] -match "^$escapedIndent\}") { if ($JsonLines[$i] -match "^$escapedIndent\}") {
$endIndex = $i $endIndex = $i
@@ -64,7 +56,7 @@ function Get-RawJsonBlock {
return $null return $null
} }
# Walk backwards from closing brace to exclude "link" property and empty lines # Strip trailing "link" property and blank lines before returning
$lastContentIndex = $endIndex - 1 $lastContentIndex = $endIndex - 1
while ($lastContentIndex -gt $startIndex) { while ($lastContentIndex -gt $startIndex) {
$trimmed = $JsonLines[$lastContentIndex].Trim() $trimmed = $JsonLines[$lastContentIndex].Trim()
@@ -75,28 +67,21 @@ function Get-RawJsonBlock {
} }
} }
$rawLines = $JsonLines[$startIndex..$lastContentIndex]
$rawText = $rawLines -join "`r`n"
return @{ return @{
LineNumber = $startIndex + 1 # 1-based LineNumber = $startIndex + 1
RawText = $rawText RawText = ($JsonLines[$startIndex..$lastContentIndex] -join "`r`n")
} }
} }
function Get-ButtonFunctionMapping { function Get-ButtonFunctionMapping {
<# # Parses Invoke-WPFButton.ps1 and returns a hashtable of button name -> function name.
.SYNOPSIS
Parses Invoke-WPFButton.ps1 to build a hashtable mapping button names to function names.
#>
param ( param (
[Parameter(Mandatory)] [Parameter(Mandatory)]
[string]$ButtonFilePath [string]$ButtonFilePath
) )
$mapping = @{} $mapping = @{}
$lines = Get-Content -Path $ButtonFilePath foreach ($line in (Get-Content -Path $ButtonFilePath)) {
foreach ($line in $lines) {
if ($line -match '^\s*"(\w+)"\s*\{(Invoke-\w+)') { if ($line -match '^\s*"(\w+)"\s*\{(Invoke-\w+)') {
$mapping[$matches[1]] = $matches[2] $mapping[$matches[1]] = $matches[2]
} }
@@ -105,11 +90,8 @@ function Get-ButtonFunctionMapping {
} }
function Add-LinkAttributeToJson { function Add-LinkAttributeToJson {
<# # Updates only the "link" property for each entry in a JSON config file.
.SYNOPSIS # Reads via ConvertFrom-Json for metadata, then edits lines directly to avoid reformatting.
Updates the "link" property on each top-level entry in a JSON config file
to point to the corresponding documentation page URL.
#>
param ( param (
[Parameter(Mandatory)] [Parameter(Mandatory)]
[string]$JsonFilePath, [string]$JsonFilePath,
@@ -119,43 +101,85 @@ function Add-LinkAttributeToJson {
[string]$ItemNameToCut [string]$ItemNameToCut
) )
$jsonText = Get-Content -Path $JsonFilePath -Raw $jsonData = Get-Content -Path $JsonFilePath -Raw | ConvertFrom-Json
$jsonData = $jsonText | ConvertFrom-Json $lines = [System.Collections.Generic.List[string]](Get-Content -Path $JsonFilePath)
foreach ($item in $jsonData.PSObject.Properties) { foreach ($item in $jsonData.PSObject.Properties) {
$itemName = $item.Name $itemName = $item.Name
$itemDetails = $item.Value $category = $item.Value.category -replace '[^a-zA-Z0-9]', '-'
$category = $itemDetails.category -replace '[^a-zA-Z0-9]', '-'
$displayName = $itemName -replace $ItemNameToCut, '' $displayName = $itemName -replace $ItemNameToCut, ''
$docLink = "$UrlPrefix/$($category.ToLower())/$($displayName.ToLower())" $newLink = "$UrlPrefix/$($category.ToLower())/$($displayName.ToLower())"
$escapedName = [regex]::Escape($itemName)
$itemDetails | Add-Member -NotePropertyName "link" -NotePropertyValue $docLink -Force # Find item start line
$startIdx = -1
for ($i = 0; $i -lt $lines.Count; $i++) {
if ($lines[$i] -match "^\s*`"$escapedName`"\s*:\s*\{") {
$startIdx = $i
break
}
}
if ($startIdx -eq -1) { continue }
# Derive indentation: propIndent is one level deeper than the item start.
# Used to target only top-level properties and skip nested object braces.
$null = $lines[$startIdx] -match '^(\s*)'
$propIndent = $matches[1] + ' '
$propIndentLen = $propIndent.Length
$escapedPropIndent = [regex]::Escape($propIndent)
# Scan forward: update existing "link" or find the closing brace to insert one.
# Closing brace is matched by indent <= propIndentLen to handle inconsistent formatting.
$linkUpdated = $false
$closeBraceIdx = -1
for ($j = $startIdx + 1; $j -lt $lines.Count; $j++) {
if ($lines[$j] -match "^$escapedPropIndent`"link`"\s*:") {
$lines[$j] = $lines[$j] -replace '"link"\s*:\s*"[^"]*"', "`"link`": `"$newLink`""
$linkUpdated = $true
break
}
if ($lines[$j] -match '^\s*\}') {
$null = $lines[$j] -match '^(\s*)'
if ($matches[1].Length -le $propIndentLen) {
$closeBraceIdx = $j
break
}
}
}
if (-not $linkUpdated -and $closeBraceIdx -ne -1) {
# Insert "link" before the closing brace
$prevPropIdx = $closeBraceIdx - 1
while ($prevPropIdx -gt $startIdx -and $lines[$prevPropIdx].Trim() -eq '') { $prevPropIdx-- }
if ($lines[$prevPropIdx] -notmatch ',\s*$') {
$lines[$prevPropIdx] = $lines[$prevPropIdx].TrimEnd() + ','
}
$lines.Insert($closeBraceIdx, "$propIndent`"link`": `"$newLink`"")
}
} }
$jsonText = ($jsonData | ConvertTo-Json -Depth 100).replace('\n', "`n").replace('\r', "`r") Set-Content -Path $JsonFilePath -Value $lines -Encoding utf8
Set-Content -Path $JsonFilePath -Value $jsonText -Encoding utf8
} }
# ============================================================================== # ==============================================================================
# Main Script # Main
# ============================================================================== # ==============================================================================
# Use PSScriptRoot if available (running as a script file), otherwise assume CWD is tools/
$scriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { (Get-Location).Path } $scriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { (Get-Location).Path }
$repoRoot = Resolve-Path "$scriptDir/.." $repoRoot = Resolve-Path "$scriptDir/.."
# Paths $tweaksJsonPath = "$repoRoot/config/tweaks.json"
$tweaksJsonPath = "$repoRoot/config/tweaks.json" $featuresJsonPath = "$repoRoot/config/feature.json"
$featuresJsonPath = "$repoRoot/config/feature.json" $tweaksOutputDir = "$repoRoot/docs/content/dev/tweaks"
$tweaksOutputDir = "$repoRoot/docs/content/dev/tweaks" $featuresOutputDir = "$repoRoot/docs/content/dev/features"
$featuresOutputDir = "$repoRoot/docs/content/dev/features"
$publicFunctionsDir = "$repoRoot/functions/public" $publicFunctionsDir = "$repoRoot/functions/public"
$privateFunctionsDir = "$repoRoot/functions/private" $privateFunctionsDir = "$repoRoot/functions/private"
$itemnametocut = 'WPF(WinUtil|Toggle|Features?|Tweaks?|Panel|Fix(es)?)?' $itemnametocut = 'WPF(WinUtil|Toggle|Features?|Tweaks?|Panel|Fix(es)?)?'
$baseUrl = "https://winutil.christitus.com" $baseUrl = "https://winutil.christitus.com"
# Categories that should have generated documentation # Categories with generated docs
$documentedCategories = @( $documentedCategories = @(
"Essential Tweaks", "Essential Tweaks",
"z__Advanced Tweaks - CAUTION", "z__Advanced Tweaks - CAUTION",
@@ -163,49 +187,44 @@ $documentedCategories = @(
"Performance Plans", "Performance Plans",
"Features", "Features",
"Fixes", "Fixes",
"Legacy Windows Panels" "Legacy Windows Panels",
"Powershell Profile Powershell 7+ Only",
"Remote Access"
) )
# --- Load data --- # Categories where Button entries embed a PS function instead of raw JSON
$functionEmbedCategories = @(
"Fixes",
"Powershell Profile Powershell 7+ Only",
"Remote Access"
)
Update-Progress "Loading JSON files" 10 Update-Progress "Loading JSON files" 10
$tweaks = Get-Content -Path $tweaksJsonPath -Raw | ConvertFrom-Json $tweaks = Get-Content -Path $tweaksJsonPath -Raw | ConvertFrom-Json
$features = Get-Content -Path $featuresJsonPath -Raw | ConvertFrom-Json $features = Get-Content -Path $featuresJsonPath -Raw | ConvertFrom-Json
# --- Load function files (content + relative path) ---
Update-Progress "Loading function files" 20 Update-Progress "Loading function files" 20
$functionFiles = @{} $functionFiles = @{}
Get-ChildItem -Path $publicFunctionsDir -Filter *.ps1 | ForEach-Object { Get-ChildItem -Path $publicFunctionsDir -Filter *.ps1 | ForEach-Object {
$functionFiles[$_.BaseName] = @{ $functionFiles[$_.BaseName] = @{ Content = (Get-Content -Path $_.FullName -Raw).TrimEnd(); RelativePath = "functions/public/$($_.Name)" }
Content = (Get-Content -Path $_.FullName -Raw).TrimEnd()
RelativePath = "functions/public/$($_.Name)"
}
} }
Get-ChildItem -Path $privateFunctionsDir -Filter *.ps1 | ForEach-Object { Get-ChildItem -Path $privateFunctionsDir -Filter *.ps1 | ForEach-Object {
$functionFiles[$_.BaseName] = @{ $functionFiles[$_.BaseName] = @{ Content = (Get-Content -Path $_.FullName -Raw).TrimEnd(); RelativePath = "functions/private/$($_.Name)" }
Content = (Get-Content -Path $_.FullName -Raw).TrimEnd()
RelativePath = "functions/private/$($_.Name)"
}
} }
# --- Build button-to-function mapping ---
Update-Progress "Building button-to-function mapping" 30 Update-Progress "Building button-to-function mapping" 30
$buttonFunctionMap = Get-ButtonFunctionMapping -ButtonFilePath "$publicFunctionsDir/Invoke-WPFButton.ps1" $buttonFunctionMap = Get-ButtonFunctionMapping -ButtonFilePath "$publicFunctionsDir/Invoke-WPFButton.ps1"
# --- Update link attributes in JSON files ---
Update-Progress "Updating documentation links in JSON" 40 Update-Progress "Updating documentation links in JSON" 40
Add-LinkAttributeToJson -JsonFilePath $tweaksJsonPath -UrlPrefix "$baseUrl/dev/tweaks" -ItemNameToCut $itemnametocut Add-LinkAttributeToJson -JsonFilePath $tweaksJsonPath -UrlPrefix "$baseUrl/dev/tweaks" -ItemNameToCut $itemnametocut
Add-LinkAttributeToJson -JsonFilePath $featuresJsonPath -UrlPrefix "$baseUrl/dev/features" -ItemNameToCut $itemnametocut Add-LinkAttributeToJson -JsonFilePath $featuresJsonPath -UrlPrefix "$baseUrl/dev/features" -ItemNameToCut $itemnametocut
# Reload JSON lines after link update (so line numbers are accurate) # Reload lines after link update so line numbers in docs are accurate
$tweaksLines = Get-Content -Path $tweaksJsonPath $tweaksLines = Get-Content -Path $tweaksJsonPath
$featuresLines = Get-Content -Path $featuresJsonPath $featuresLines = Get-Content -Path $featuresJsonPath
# ============================================================================== # ==============================================================================
# Clean up old generated .md files (keep _index.md) # Clean up old generated .md files (preserve _index.md)
# ============================================================================== # ==============================================================================
Update-Progress "Cleaning up old generated docs" 45 Update-Progress "Cleaning up old generated docs" 45
@@ -221,9 +240,9 @@ foreach ($dir in @($tweaksOutputDir, $featuresOutputDir)) {
Update-Progress "Generating tweak documentation" 50 Update-Progress "Generating tweak documentation" 50
$tweakNames = $tweaks.PSObject.Properties.Name $tweakNames = $tweaks.PSObject.Properties.Name
$totalTweaks = $tweakNames.Count $totalTweaks = $tweakNames.Count
$tweakCount = 0 $tweakCount = 0
foreach ($itemName in $tweakNames) { foreach ($itemName in $tweakNames) {
$item = $tweaks.$itemName $item = $tweaks.$itemName
@@ -236,25 +255,20 @@ foreach ($itemName in $tweakNames) {
$categoryDir = "$tweaksOutputDir/$category" $categoryDir = "$tweaksOutputDir/$category"
$filename = "$categoryDir/$displayName.md" $filename = "$categoryDir/$displayName.md"
if (-Not (Test-Path -Path $categoryDir)) { if (-Not (Test-Path -Path $categoryDir)) { New-Item -ItemType Directory -Path $categoryDir | Out-Null }
New-Item -ItemType Directory -Path $categoryDir | Out-Null
}
# Hugo frontmatter $title = $item.Content -replace '"', '\"'
$title = $item.Content -replace '"', '\"'
$content = "---`r`ntitle: `"$title`"`r`ndescription: `"`"`r`n---`r`n`r`n" $content = "---`r`ntitle: `"$title`"`r`ndescription: `"`"`r`n---`r`n`r`n"
if ($item.Type -eq "Button") { if ($item.Type -eq "Button") {
# Button-type tweak: embed the mapped PowerShell function
$funcName = $buttonFunctionMap[$itemName] $funcName = $buttonFunctionMap[$itemName]
if ($funcName -and $functionFiles.ContainsKey($funcName)) { if ($funcName -and $functionFiles.ContainsKey($funcName)) {
$func = $functionFiles[$funcName] $func = $functionFiles[$funcName]
$content += "``````powershell {filename=`"$($func.RelativePath)`",linenos=inline,linenostart=1}`r`n" $content += "``````powershell {filename=`"$($func.RelativePath)`",linenos=inline,linenostart=1}`r`n"
$content += $func.Content + "`r`n" $content += $func.Content + "`r`n"
$content += "```````r`n" $content += "```````r`n"
} }
} else { } else {
# Standard tweak: embed raw JSON block
$jsonBlock = Get-RawJsonBlock -ItemName $itemName -JsonLines $tweaksLines $jsonBlock = Get-RawJsonBlock -ItemName $itemName -JsonLines $tweaksLines
if ($jsonBlock) { if ($jsonBlock) {
$content += "``````json {filename=`"config/tweaks.json`",linenos=inline,linenostart=$($jsonBlock.LineNumber)}`r`n" $content += "``````json {filename=`"config/tweaks.json`",linenos=inline,linenostart=$($jsonBlock.LineNumber)}`r`n"
@@ -262,27 +276,16 @@ foreach ($itemName in $tweakNames) {
$content += "```````r`n" $content += "```````r`n"
} }
# Registry Changes section
if ($item.registry) { if ($item.registry) {
$content += "`r`n## Registry Changes`r`n`r`n" $content += "`r`n## Registry Changes`r`n`r`n"
$content += "Applications and System Components store and retrieve configuration data to modify windows settings, so we can use the registry to change many settings in one place.`r`n`r`n" $content += "Applications and System Components store and retrieve configuration data to modify windows settings, so we can use the registry to change many settings in one place.`r`n`r`n"
$content += "You can find information about the registry on [Wikipedia](https://www.wikiwand.com/en/Windows_Registry) and [Microsoft's Website](https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry).`r`n" $content += "You can find information about the registry on [Wikipedia](https://www.wikiwand.com/en/Windows_Registry) and [Microsoft's Website](https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry).`r`n"
} }
# Service function reference
if ($item.service -and $functionFiles.ContainsKey("Set-WinUtilService")) {
$svcFunc = $functionFiles["Set-WinUtilService"]
$content += "#Function`r`n"
$content += "``````powershell {filename=`"$($svcFunc.RelativePath)`",linenos=inline,linenostart=1}`r`n"
$content += $svcFunc.Content + "`r`n"
$content += "```````r`n"
}
} }
Set-Content -Path $filename -Value $content -Encoding utf8 -NoNewline Set-Content -Path $filename -Value $content -Encoding utf8 -NoNewline
$percent = 50 + [int](($tweakCount / $totalTweaks) * 20) $percent = [Math]::Min(70, 50 + [int](($tweakCount / $totalTweaks) * 20))
if ($percent -gt 70) { $percent = 70 }
Update-Progress "Generating tweak documentation ($tweakCount/$totalTweaks)" $percent Update-Progress "Generating tweak documentation ($tweakCount/$totalTweaks)" $percent
} }
@@ -292,17 +295,15 @@ foreach ($itemName in $tweakNames) {
Update-Progress "Generating feature documentation" 70 Update-Progress "Generating feature documentation" 70
$featureNames = $features.PSObject.Properties.Name $featureNames = $features.PSObject.Properties.Name
$totalFeatures = $featureNames.Count $totalFeatures = $featureNames.Count
$featureCount = 0 $featureCount = 0
foreach ($itemName in $featureNames) { foreach ($itemName in $featureNames) {
$item = $features.$itemName $item = $features.$itemName
$featureCount++ $featureCount++
if ($item.category -notin $documentedCategories) { continue } if ($item.category -notin $documentedCategories) { continue }
# Skip pure UI buttons that don't need docs
if ($itemName -eq "WPFFeatureInstall") { continue } if ($itemName -eq "WPFFeatureInstall") { continue }
$category = $item.category -replace '[^a-zA-Z0-9]', '-' $category = $item.category -replace '[^a-zA-Z0-9]', '-'
@@ -310,24 +311,20 @@ foreach ($itemName in $featureNames) {
$categoryDir = "$featuresOutputDir/$category" $categoryDir = "$featuresOutputDir/$category"
$filename = "$categoryDir/$displayName.md" $filename = "$categoryDir/$displayName.md"
if (-Not (Test-Path -Path $categoryDir)) { if (-Not (Test-Path -Path $categoryDir)) { New-Item -ItemType Directory -Path $categoryDir | Out-Null }
New-Item -ItemType Directory -Path $categoryDir | Out-Null
}
$title = $item.Content -replace '"', '\"' $title = $item.Content -replace '"', '\"'
$content = "---`r`ntitle: `"$title`"`r`ndescription: `"`"`r`n---`r`n`r`n" $content = "---`r`ntitle: `"$title`"`r`ndescription: `"`"`r`n---`r`n`r`n"
if ($item.category -eq "Fixes" -or $item.category -eq "Legacy Windows Panels") { if ($item.category -in $functionEmbedCategories) {
# Embed the PowerShell function file $funcName = if ($item.function) { $item.function } else { $buttonFunctionMap[$itemName] }
$funcName = $buttonFunctionMap[$itemName]
if ($funcName -and $functionFiles.ContainsKey($funcName)) { if ($funcName -and $functionFiles.ContainsKey($funcName)) {
$func = $functionFiles[$funcName] $func = $functionFiles[$funcName]
$content += "``````powershell {filename=`"$($func.RelativePath)`",linenos=inline,linenostart=1}`r`n" $content += "``````powershell {filename=`"$($func.RelativePath)`",linenos=inline,linenostart=1}`r`n"
$content += $func.Content + "`r`n" $content += $func.Content + "`r`n"
$content += "```````r`n" $content += "```````r`n"
} }
} else { } else {
# Features category: embed raw JSON block
$jsonBlock = Get-RawJsonBlock -ItemName $itemName -JsonLines $featuresLines $jsonBlock = Get-RawJsonBlock -ItemName $itemName -JsonLines $featuresLines
if ($jsonBlock) { if ($jsonBlock) {
$content += "``````json {filename=`"config/feature.json`",linenos=inline,linenostart=$($jsonBlock.LineNumber)}`r`n" $content += "``````json {filename=`"config/feature.json`",linenos=inline,linenostart=$($jsonBlock.LineNumber)}`r`n"
@@ -338,8 +335,7 @@ foreach ($itemName in $featureNames) {
Set-Content -Path $filename -Value $content -Encoding utf8 -NoNewline Set-Content -Path $filename -Value $content -Encoding utf8 -NoNewline
$percent = 70 + [int](($featureCount / $totalFeatures) * 20) $percent = [Math]::Min(90, 70 + [int](($featureCount / $totalFeatures) * 20))
if ($percent -gt 90) { $percent = 90 }
Update-Progress "Generating feature documentation ($featureCount/$totalFeatures)" $percent Update-Progress "Generating feature documentation ($featureCount/$totalFeatures)" $percent
} }

View File

@@ -970,6 +970,14 @@
</TextBlock> </TextBlock>
</ToggleButton.Content> </ToggleButton.Content>
</ToggleButton> </ToggleButton>
<ToggleButton Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="{DynamicResource TabButtonWidth}"
Background="{DynamicResource ButtonWin11ISOBackgroundColor}" Foreground="{DynamicResource ButtonWin11ISOForegroundColor}" FontWeight="Bold" Name="WPFTab5BT">
<ToggleButton.Content>
<TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonWin11ISOForegroundColor}">
<Underline>W</Underline>in11ISO
</TextBlock>
</ToggleButton.Content>
</ToggleButton>
</StackPanel> </StackPanel>
<!-- Search Bar and Action Buttons --> <!-- Search Bar and Action Buttons -->
@@ -1203,7 +1211,7 @@
<Button Name="WPFstandard" Content=" Standard " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/> <Button Name="WPFstandard" Content=" Standard " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
<Button Name="WPFminimal" Content=" Minimal " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/> <Button Name="WPFminimal" Content=" Minimal " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
<Button Name="WPFClearTweaksSelection" Content=" Clear " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/> <Button Name="WPFClearTweaksSelection" Content=" Clear " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
<Button Name="WPFGetInstalledTweaks" Content=" Show Installed Apps " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/> <Button Name="WPFGetInstalledTweaks" Content=" Get Installed Tweaks " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
@@ -1331,6 +1339,300 @@
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</TabItem> </TabItem>
<TabItem Header="Win11ISO" Visibility="Collapsed" Name="WPFTab5">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Margin="{DynamicResource TabContentMargin}">
<Grid Background="Transparent" Name="Win11ISOPanel">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- Step 1: Select ISO -->
<RowDefinition Height="Auto"/> <!-- Step 2: Mount & Verify -->
<RowDefinition Height="Auto"/> <!-- Step 3: Modify install.wim -->
<RowDefinition Height="Auto"/> <!-- Step 4: Output Options -->
<RowDefinition Height="Auto"/> <!-- Log / Status -->
</Grid.RowDefinitions>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- STEP 1 : Select Windows 11 ISO -->
<!-- ═══════════════════════════════════════════════════════════ -->
<Border Grid.Row="0" Style="{StaticResource BorderStyle}">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Left: File Selector -->
<StackPanel Grid.Column="0" Margin="5,5,15,5">
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
Foreground="{DynamicResource MainForegroundColor}" Margin="0,0,0,8">
Step 1 — Select Windows 11 ISO
</TextBlock>
<TextBlock FontSize="{DynamicResource FontSize}" Foreground="{DynamicResource MainForegroundColor}"
TextWrapping="Wrap" Margin="0,0,0,12">
Browse to your locally saved Windows 11 ISO file. Only official ISOs
downloaded from Microsoft are supported.
</TextBlock>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0"
Name="WPFWin11ISOPath"
IsReadOnly="True"
VerticalAlignment="Center"
Padding="6,4"
Margin="0,0,6,0"
Text="No ISO selected..."
Foreground="{DynamicResource MainForegroundColor}"
Background="{DynamicResource MainBackgroundColor}"/>
<Button Grid.Column="1"
Name="WPFWin11ISOBrowseButton"
Content="Browse…"
Width="Auto" Padding="12,0"
Height="{DynamicResource ButtonHeight}"/>
</Grid>
<TextBlock Name="WPFWin11ISOFileInfo"
FontSize="{DynamicResource FontSize}"
Foreground="{DynamicResource MainForegroundColor}"
Margin="0,8,0,0"
TextWrapping="Wrap"
Visibility="Collapsed"/>
</StackPanel>
<!-- Right: Download guidance -->
<Border Grid.Column="1"
Background="{DynamicResource MainBackgroundColor}"
BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1" CornerRadius="5"
Margin="5" Padding="15">
<StackPanel>
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
Foreground="OrangeRed" Margin="0,0,0,10">
⚠ You must use an official Microsoft ISO
</TextBlock>
<TextBlock FontSize="{DynamicResource FontSize}"
Foreground="{DynamicResource MainForegroundColor}"
TextWrapping="Wrap" Margin="0,0,0,8">
Download the Windows 11 ISO directly from Microsoft.com.
Third-party, pre-modified, or unofficial images are not supported
and may produce broken results.
</TextBlock>
<TextBlock FontSize="{DynamicResource FontSize}"
Foreground="{DynamicResource MainForegroundColor}"
TextWrapping="Wrap" Margin="0,0,0,6">
On the Microsoft download page, choose:
</TextBlock>
<TextBlock FontSize="{DynamicResource FontSize}"
Foreground="{DynamicResource MainForegroundColor}"
TextWrapping="Wrap" Margin="12,0,0,12">
• Edition : Windows 11
<LineBreak/>• Language : your preferred language
<LineBreak/>• Architecture : 64-bit (x64)
</TextBlock>
<Button Name="WPFWin11ISODownloadLink"
Content="Open Microsoft Download Page"
HorizontalAlignment="Left"
Width="Auto" Padding="12,0"
Height="{DynamicResource ButtonHeight}"/>
</StackPanel>
</Border>
</Grid>
</Border>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- STEP 2 : Mount & Verify ISO -->
<!-- ═══════════════════════════════════════════════════════════ -->
<Border Grid.Row="1"
Name="WPFWin11ISOMountSection"
Style="{StaticResource BorderStyle}"
Visibility="Collapsed">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="0,0,20,0" VerticalAlignment="Top">
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
Foreground="{DynamicResource MainForegroundColor}" Margin="0,0,0,8">
Step 2 — Mount &amp; Verify ISO
</TextBlock>
<TextBlock FontSize="{DynamicResource FontSize}"
Foreground="{DynamicResource MainForegroundColor}"
TextWrapping="Wrap" Margin="0,0,0,12" MaxWidth="320">
Mount the ISO and confirm it contains a valid Windows 11
install.wim before any modifications are made.
</TextBlock>
<Button Name="WPFWin11ISOMountButton"
Content="Mount &amp; Verify ISO"
HorizontalAlignment="Left"
Width="Auto" Padding="12,0"
Height="{DynamicResource ButtonHeight}"/>
</StackPanel>
<!-- Verification results panel -->
<Border Grid.Column="1"
Name="WPFWin11ISOVerifyResultPanel"
Background="{DynamicResource MainBackgroundColor}"
BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1" CornerRadius="5"
Padding="12" Margin="0,0,0,0"
Visibility="Collapsed">
<StackPanel>
<TextBlock Name="WPFWin11ISOMountDriveLetter"
FontSize="{DynamicResource FontSize}"
Foreground="{DynamicResource MainForegroundColor}"
Margin="0,0,0,4"/>
<TextBlock Name="WPFWin11ISOArchLabel"
FontSize="{DynamicResource FontSize}"
Foreground="{DynamicResource MainForegroundColor}"
Margin="0,0,0,4"/>
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
Foreground="{DynamicResource MainForegroundColor}"
Margin="0,6,0,4">
Available Editions:
</TextBlock>
<TextBlock Name="WPFWin11ISOEditionList"
FontSize="{DynamicResource FontSize}"
Foreground="{DynamicResource MainForegroundColor}"
TextWrapping="Wrap"/>
</StackPanel>
</Border>
</Grid>
</Border>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- STEP 3 : Modify install.wim -->
<!-- ═══════════════════════════════════════════════════════════ -->
<Border Grid.Row="2"
Name="WPFWin11ISOModifySection"
Style="{StaticResource BorderStyle}"
Visibility="Collapsed">
<StackPanel Margin="5">
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
Foreground="{DynamicResource MainForegroundColor}" Margin="0,0,0,8">
Step 3 — Modify install.wim
</TextBlock>
<TextBlock FontSize="{DynamicResource FontSize}"
Foreground="{DynamicResource MainForegroundColor}"
TextWrapping="Wrap" Margin="0,0,0,12">
The ISO contents will be extracted to a temporary working directory,
install.wim will be modified (components removed, tweaks applied),
and the result will be repackaged. This process may take several minutes
depending on your hardware.
</TextBlock>
<Button Name="WPFWin11ISOModifyButton"
Content="Run install.wim Modification"
HorizontalAlignment="Left"
Width="Auto" Padding="12,0"
Height="{DynamicResource ButtonHeight}"/>
</StackPanel>
</Border>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- STEP 4 : Output Options -->
<!-- ═══════════════════════════════════════════════════════════ -->
<Border Grid.Row="3"
Name="WPFWin11ISOOutputSection"
Style="{StaticResource BorderStyle}"
Visibility="Collapsed">
<StackPanel Margin="5">
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
Foreground="{DynamicResource MainForegroundColor}" Margin="0,0,0,12">
Step 4 — Output: What would you like to do with the modified ISO?
</TextBlock>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Option 1: Export to ISO -->
<Border Grid.Column="0" Style="{StaticResource BorderStyle}">
<StackPanel>
<Button Name="WPFWin11ISOExportButton"
Content="1 — Export to ISO"
HorizontalAlignment="Stretch"
Width="Auto" Padding="12,0"
Height="{DynamicResource ButtonHeight}"
Margin="0,0,0,10"/>
<TextBlock FontSize="{DynamicResource FontSize}"
Foreground="{DynamicResource MainForegroundColor}"
TextWrapping="Wrap">
Save the modified content as a new bootable ISO file.
You can store it, use it in a virtual machine, or later
burn it to USB with any tool of your choice.
</TextBlock>
</StackPanel>
</Border>
<!-- Option 2: Erase & Write to USB -->
<Border Grid.Column="1" Style="{StaticResource BorderStyle}">
<StackPanel>
<!-- USB drive selector row -->
<Grid Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0"
Name="WPFWin11ISOUSBDriveComboBox"
Foreground="{DynamicResource MainForegroundColor}"
Background="{DynamicResource MainBackgroundColor}"
VerticalAlignment="Center"
Margin="0,0,6,0"/>
<Button Grid.Column="1"
Name="WPFWin11ISORefreshUSBButton"
Content="↻ Refresh"
Width="Auto" Padding="8,0"
Height="{DynamicResource ButtonHeight}"/>
</Grid>
<Button Name="WPFWin11ISOWriteUSBButton"
Content="2 — Erase &amp; Write to USB"
Foreground="OrangeRed"
HorizontalAlignment="Stretch"
Width="Auto" Padding="12,0"
Height="{DynamicResource ButtonHeight}"
Margin="0,0,0,10"/>
<TextBlock FontSize="{DynamicResource FontSize}"
Foreground="{DynamicResource MainForegroundColor}"
TextWrapping="Wrap">
<Run FontWeight="Bold" Foreground="OrangeRed">!! All data on the selected USB drive will be erased !!</Run>
<LineBreak/>
Select a removable USB drive above, then click the button
to write the modified Windows 11 installation directly to it.
</TextBlock>
</StackPanel>
</Border>
</Grid>
</StackPanel>
</Border>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- Status / Log Output -->
<!-- ═══════════════════════════════════════════════════════════ -->
<Border Grid.Row="4" Style="{StaticResource BorderStyle}">
<StackPanel>
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
Foreground="{DynamicResource MainForegroundColor}" Margin="0,0,0,6">
Status Log
</TextBlock>
<TextBox Name="WPFWin11ISOStatusLog"
IsReadOnly="True"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto"
Height="140" Padding="6"
Background="{DynamicResource MainBackgroundColor}"
Foreground="{DynamicResource MainForegroundColor}"
BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1"
Text="Ready. Please select a Windows 11 ISO to begin."/>
</StackPanel>
</Border>
</Grid>
</ScrollViewer>
</TabItem>
</TabControl> </TabControl>
</Grid> </Grid>
</Window> </Window>