mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-10-08 11:39:10 +11:00
Compare commits
12
Commits
48885d7c1e
...
26.10.07
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07ccd8e2e7 | ||
|
|
9c87c02fdb | ||
|
|
9419b2803e | ||
|
|
92d5a08d49 | ||
|
|
a3a7c500d4 | ||
|
|
40666e1d31 | ||
|
|
4d4e219562 | ||
|
|
fc03af421b | ||
|
|
8939ecf4b4 | ||
|
|
735b59b82f | ||
|
|
d0d39d6478 | ||
|
|
6f1266eb86 |
@@ -78,7 +78,7 @@ See https://github.com/ChrisTitusTech/winutil/blob/main/.github/CONTRIBUTING.md
|
||||
|
||||
These are the sponsors that help keep this project alive with monthly contributions.
|
||||
|
||||
<!-- sponsors --><a href="https://github.com/dwelfusius"><img src="https://github.com/dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https://github.com/mews-se.png" width="60px" alt="User avatar: Martin" /></a><a href="https://github.com/jdiegmueller"><img src="https://github.com/jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https://github.com/robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/paulsheets"><img src="https://github.com/paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https://github.com/djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https://github.com/anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https://github.com/FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https://github.com/DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https://github.com/DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https://github.com/KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/seanh1995"><img src="https://github.com/seanh1995.png" width="60px" alt="User avatar: Sean (ANGRYxScotsman)" /></a><a href="https://github.com/F-L-Perez"><img src="https://github.com/F-L-Perez.png" width="60px" alt="User avatar: Fra · ppe" /></a><a href="https://github.com/josencarnacao"><img src="https://github.com/josencarnacao.png" width="60px" alt="User avatar: José Encarnação" /></a><!-- sponsors -->
|
||||
<!-- sponsors --><a href="https://github.com/dwelfusius"><img src="https://github.com/dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https://github.com/mews-se.png" width="60px" alt="User avatar: Martin" /></a><a href="https://github.com/jdiegmueller"><img src="https://github.com/jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https://github.com/robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/paulsheets"><img src="https://github.com/paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https://github.com/djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https://github.com/anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https://github.com/FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https://github.com/DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https://github.com/DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https://github.com/KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/seanh1995"><img src="https://github.com/seanh1995.png" width="60px" alt="User avatar: Sean (ANGRYxScotsman)" /></a><a href="https://github.com/josencarnacao"><img src="https://github.com/josencarnacao.png" width="60px" alt="User avatar: José Encarnação" /></a><!-- sponsors -->
|
||||
|
||||
*<sub>Sponsors with a recurring subscription also get access to the .NET alternative.</sub>
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 78 KiB |
@@ -10,7 +10,8 @@ This page is generated from [`functions/public/Invoke-WPFPanelAutologin.ps1`](ht
|
||||
|
||||
```powershell title="functions/public/Invoke-WPFPanelAutologin.ps1"
|
||||
function Invoke-WPFPanelAutologin {
|
||||
Invoke-WebRequest -Uri https://live.sysinternals.com/Autologon.exe -OutFile "$winutildir\autologin.exe"
|
||||
Start-Process -FilePath "$winutildir\autologin.exe" -ArgumentList /accepteula
|
||||
$autologonPath = Join-Path $sync.winutildir "autologin.exe"
|
||||
Invoke-WebRequest -Uri https://live.sysinternals.com/Autologon.exe -OutFile $autologonPath
|
||||
Start-Process -FilePath $autologonPath -ArgumentList /accepteula
|
||||
}
|
||||
```
|
||||
|
||||
@@ -21,11 +21,54 @@ function Invoke-WPFSystemRepair {
|
||||
3. DISM - Repair a corrupted Windows operating system image
|
||||
#>
|
||||
|
||||
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 dism /online /cleanup-image /restorehealth" -NoNewWindow -Wait
|
||||
# SuccessCodes maps the non-zero exits a step treats as success to what they mean. The codes
|
||||
# are per step because the same number means different things: 1 and 2 are ordinary chkdsk
|
||||
# outcomes, while 1 from sfc is a failure, and 3010 is a repaired image from DISM only.
|
||||
$steps = @(
|
||||
@{
|
||||
Label = "Checking the disk for errors"
|
||||
Arguments = "/c chkdsk /scan /perf"
|
||||
# 3 is left out: the disk could not be checked, or has errors an online scan cannot
|
||||
# fix, and the steps after this one are not worth running on a disk in that state.
|
||||
SuccessCodes = @{
|
||||
1 = "errors were found and fixed"
|
||||
2 = "cleanup was performed, or was skipped because /f was not given"
|
||||
}
|
||||
},
|
||||
@{
|
||||
Label = "Scanning protected system files"
|
||||
Arguments = "/c sfc /scannow"
|
||||
SuccessCodes = @{}
|
||||
},
|
||||
@{
|
||||
Label = "Repairing the Windows image"
|
||||
Arguments = "/c dism /online /cleanup-image /restorehealth"
|
||||
SuccessCodes = @{
|
||||
3010 = "a restart is needed for the repair to take effect"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
Write-Host "==> Finished System Repair"
|
||||
Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"
|
||||
$completed = 0
|
||||
foreach ($step in $steps) {
|
||||
Step-WinUtilJob -Status "$($step.Label) ($($completed + 1)/$($steps.Count))" -Percent ([int](($completed / $steps.Count) * 100))
|
||||
Write-WinUtilLog -Component "SystemRepair" -Message $step.Label
|
||||
# Start-Process does not throw on a nonzero exit, so without this a failed chkdsk, sfc
|
||||
# or dism run would still be reported as a completed repair
|
||||
$process = Start-Process cmd.exe -ArgumentList $step.Arguments -NoNewWindow -Wait -PassThru
|
||||
$exitCode = $process.ExitCode
|
||||
|
||||
if ($exitCode -ne 0) {
|
||||
if ($step.SuccessCodes.ContainsKey($exitCode)) {
|
||||
# Start-WinUtilJob records WarningRecord output in both the session log and the
|
||||
# job result, so accepted nonzero outcomes cannot finish with a green checkmark.
|
||||
Write-Warning "$($step.Label) finished: $($step.SuccessCodes[$exitCode])."
|
||||
} else {
|
||||
throw "$($step.Label) failed with exit code $exitCode."
|
||||
}
|
||||
}
|
||||
|
||||
$completed++
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -25,8 +25,5 @@ function Invoke-WPFFixesNTPPool {
|
||||
Restart-Service w32time
|
||||
w32tm /resync
|
||||
|
||||
Write-Host "================================="
|
||||
Write-Host "-- NTP Configuration Complete ---"
|
||||
Write-Host "================================="
|
||||
}
|
||||
```
|
||||
|
||||
@@ -41,7 +41,7 @@ function Invoke-WPFFixesUpdate {
|
||||
param($Aggressive = $false)
|
||||
|
||||
Write-Progress -Id 0 -Activity "Repairing Windows Update" -PercentComplete 0
|
||||
Set-WinUtilTaskbaritem -state "Indeterminate" -overlay "logo"
|
||||
Step-WinUtilJob -State "Indeterminate"
|
||||
Write-Host "Starting Windows Update Repair..."
|
||||
# Wait for the first progress bar to show, otherwise the second one won't show
|
||||
Start-Sleep -Milliseconds 200
|
||||
@@ -203,24 +203,14 @@ function Invoke-WPFFixesUpdate {
|
||||
try {
|
||||
(New-Object -ComObject Microsoft.Update.AutoUpdate).DetectNow()
|
||||
} catch {
|
||||
Set-WinUtilTaskbaritem -state "Error" -overlay "warning"
|
||||
Write-WinUtilLog -Level "ERROR" -Component "Updates" -Message "Failed to create Windows Update COM object: $_"
|
||||
Write-Warning "Failed to create Windows Update COM object: $_"
|
||||
}
|
||||
Start-Process -NoNewWindow -FilePath "wuauclt" -ArgumentList "/resetauthorization", "/detectnow"
|
||||
Write-Progress -Id 10 -ParentId 0 -Activity "Forcing discovery" -Status "Completed" -PercentComplete 100
|
||||
Write-Progress -Id 0 -Activity "Repairing Windows Update" -Status "Completed" -PercentComplete 100
|
||||
|
||||
Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"
|
||||
|
||||
$ButtonType = [System.Windows.MessageBoxButton]::OK
|
||||
$MessageboxTitle = "Reset Windows Update "
|
||||
$Messageboxbody = ("Stock settings loaded.`n Please reboot your computer")
|
||||
$MessageIcon = [System.Windows.MessageBoxImage]::Information
|
||||
|
||||
[System.Windows.MessageBox]::Show($Messageboxbody, $MessageboxTitle, $ButtonType, $MessageIcon)
|
||||
Write-Host "==============================================="
|
||||
Write-Host "-- Reset All Windows Update Settings to Stock -"
|
||||
Write-Host "==============================================="
|
||||
Show-WinUtilMessage -Message "Stock settings loaded.`n Please reboot your computer" -Title "Reset Windows Update" -Button "OK" -Icon "Information" | Out-Null
|
||||
|
||||
# Remove the progress bars
|
||||
Write-Progress -Id 0 -Activity "Repairing Windows Update" -Completed
|
||||
|
||||
@@ -18,18 +18,8 @@ function Invoke-WPFFixesWinget {
|
||||
.DESCRIPTION
|
||||
BravoNorris for the fantastic idea of a button to reinstall WinGet
|
||||
#>
|
||||
# Install Choco if not already present
|
||||
try {
|
||||
Set-WinUtilTaskbaritem -state "Indeterminate" -overlay "logo"
|
||||
Write-Host "==> Starting WinGet Repair"
|
||||
Install-WinUtilWinget
|
||||
} catch {
|
||||
Write-Error "Failed to install WinGet: $_"
|
||||
Set-WinUtilTaskbaritem -state "Error" -overlay "warning"
|
||||
} finally {
|
||||
Write-Host "==> Finished WinGet Repair"
|
||||
Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"
|
||||
}
|
||||
|
||||
Step-WinUtilJob -Status "Repairing WinGet" -State "Indeterminate"
|
||||
Install-WinUtilWinget -Force
|
||||
}
|
||||
```
|
||||
|
||||
+61
-8
@@ -9,19 +9,72 @@ This page is generated from [`functions/private/Invoke-WinUtilInstallPSProfile.p
|
||||
:::
|
||||
|
||||
```powershell title="functions/private/Invoke-WinUtilInstallPSProfile.ps1"
|
||||
function Get-WinUtilPowerShell7Path {
|
||||
$command = Get-Command pwsh -CommandType Application -ErrorAction SilentlyContinue
|
||||
if ($command) { return $command.Source }
|
||||
|
||||
foreach ($candidate in @(
|
||||
"$env:ProgramFiles\PowerShell\7\pwsh.exe",
|
||||
"$env:LOCALAPPDATA\Microsoft\WindowsApps\pwsh.exe")) {
|
||||
if (Test-Path -LiteralPath $candidate) { return $candidate }
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Invoke-WinUtilInstallPSProfile {
|
||||
if (-not (Get-Command wt)) {
|
||||
Write-Host "Windows Terminal not found. Installing..."
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Installs the CTT PowerShell profile
|
||||
|
||||
.DESCRIPTION
|
||||
The profile targets PowerShell 7, so its setup script has to run under pwsh rather than
|
||||
the runspace this job is on. It runs as a child process with its output captured, so the
|
||||
job log records what happened instead of it scrolling past in a terminal nobody kept.
|
||||
#>
|
||||
|
||||
$pwshPath = Get-WinUtilPowerShell7Path
|
||||
if (-not $pwshPath) {
|
||||
Step-WinUtilJob -Status "Installing PowerShell 7" -State "Indeterminate"
|
||||
Write-WinUtilLog -Component "Feature" -Message "PowerShell 7 not found, installing it first."
|
||||
|
||||
Install-WinUtilWinget
|
||||
winget install Microsoft.WindowsTerminal --source winget --silent
|
||||
Install-WinUtilProgramWinget -Action Install -Programs @("Microsoft.PowerShell") | Out-Null
|
||||
|
||||
# WinGet updates the persisted PATH, not this already-running process. Resolve the
|
||||
# standard install locations as well as the current PATH before deciding it failed.
|
||||
$pwshPath = Get-WinUtilPowerShell7Path
|
||||
if (-not $pwshPath) {
|
||||
throw "PowerShell 7 could not be installed, so the profile cannot be set up."
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Get-Command pwsh)) {
|
||||
Write-Host "PowerShell 7 not found. Installing..."
|
||||
Install-WinUtilWinget
|
||||
winget install Microsoft.PowerShell --source winget --installer-type wix --silent
|
||||
Step-WinUtilJob -Status "Running the profile setup" -State "Indeterminate"
|
||||
|
||||
$setupUrl = "https://github.com/ChrisTitusTech/powershell-profile/raw/main/setup.ps1"
|
||||
# Stop in the child, so a setup failure is a nonzero exit rather than a logged error and a
|
||||
# exit code of zero
|
||||
$output = & $pwshPath -NoProfile -NonInteractive -Command "`$ErrorActionPreference = 'Stop'; irm '$setupUrl' | iex" 2>&1
|
||||
$exitCode = $LASTEXITCODE
|
||||
|
||||
$failures = 0
|
||||
foreach ($line in @($output)) {
|
||||
if ($line -is [System.Management.Automation.ErrorRecord]) {
|
||||
$failures++
|
||||
Write-WinUtilErrorRecord -ErrorRecord $line -Component "Feature" -Context "PowerShell profile setup"
|
||||
} elseif (-not [string]::IsNullOrWhiteSpace($line)) {
|
||||
Write-WinUtilLog -Component "Feature" -Message ([string]$line).Trim()
|
||||
}
|
||||
}
|
||||
|
||||
wt new-tab pwsh -NoExit -Command "irm https://github.com/ChrisTitusTech/powershell-profile/raw/main/setup.ps1 | iex"
|
||||
if ($exitCode -ne 0) {
|
||||
throw "The profile setup script exited with code $exitCode."
|
||||
}
|
||||
|
||||
if ($failures -gt 0) {
|
||||
throw "The profile setup script reported $failures error(s); see the log."
|
||||
}
|
||||
|
||||
Write-WinUtilLog -Component "Feature" -Message "CTT PowerShell profile installed. Open a new PowerShell 7 session to use it."
|
||||
}
|
||||
```
|
||||
|
||||
+31
-5
@@ -10,13 +10,39 @@ This page is generated from [`functions/private/Invoke-WinUtilUninstallPSProfile
|
||||
|
||||
```powershell title="functions/private/Invoke-WinUtilUninstallPSProfile.ps1"
|
||||
function Invoke-WinUtilUninstallPSProfile {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Restores the PowerShell 7 profile the CTT profile replaced
|
||||
|
||||
if (Test-Path ($Profile + ".bak")) {
|
||||
Move-Item -Path ($Profile + ".bak") -Destination $Profile
|
||||
} else {
|
||||
Remove-Item -Path $Profile
|
||||
.DESCRIPTION
|
||||
The profile path has to come from pwsh itself. $PROFILE inside this job is the worker's
|
||||
own Windows PowerShell profile, which is not the file the install wrote.
|
||||
#>
|
||||
|
||||
$pwshPath = Get-WinUtilPowerShell7Path
|
||||
if (-not $pwshPath) {
|
||||
throw "PowerShell 7 is not installed, so there is no CTT profile to remove."
|
||||
}
|
||||
|
||||
Write-Host "Successfully uninstalled CTT PowerShell Profile." -ForegroundColor Green
|
||||
$profilePath = (& $pwshPath -NoProfile -NonInteractive -Command '$PROFILE' | Select-Object -First 1)
|
||||
if ([string]::IsNullOrWhiteSpace($profilePath)) {
|
||||
throw "Could not determine the PowerShell 7 profile path."
|
||||
}
|
||||
$profilePath = $profilePath.Trim()
|
||||
$backupPath = "$profilePath.bak"
|
||||
|
||||
if (Test-Path $backupPath) {
|
||||
Move-Item -Path $backupPath -Destination $profilePath -Force
|
||||
Write-WinUtilLog -Component "Feature" -Message "Restored the profile that was in place before: $profilePath"
|
||||
return
|
||||
}
|
||||
|
||||
if (Test-Path $profilePath) {
|
||||
Remove-Item -Path $profilePath -Force
|
||||
Write-WinUtilLog -Component "Feature" -Message "Removed the CTT PowerShell profile: $profilePath"
|
||||
return
|
||||
}
|
||||
|
||||
Write-WinUtilLog -Level "WARN" -Component "Feature" -Message "No PowerShell 7 profile found at $profilePath, nothing to remove."
|
||||
}
|
||||
```
|
||||
|
||||
@@ -13,17 +13,10 @@ function Invoke-WPFSSHServer {
|
||||
<#
|
||||
|
||||
.SYNOPSIS
|
||||
Invokes the OpenSSH Server install in a runspace
|
||||
Installs and starts the OpenSSH Server
|
||||
|
||||
#>
|
||||
|
||||
Invoke-WPFRunspace -ScriptBlock {
|
||||
|
||||
Invoke-WinUtilSSHServer
|
||||
|
||||
Write-Host "======================================="
|
||||
Write-Host "-- OpenSSH Server installed! ---"
|
||||
Write-Host "======================================="
|
||||
}
|
||||
Invoke-WinUtilSSHServer
|
||||
}
|
||||
```
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
:::
|
||||
|
||||
```json title="config/tweaks.json"
|
||||
"WPFTweaksDisableLockscreen": {
|
||||
"WPFToggleDisableLockscreen": {
|
||||
"Content": "Lock Screen - Disable",
|
||||
"Description": "Skips the lock screen entirely and goes directly to the sign-in screen on boot and wake.",
|
||||
"category": "Customize Preferences",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "Microsoft Outlook New Version"
|
||||
description: "This will ensures the classic Outlook application is used."
|
||||
description: "This will ensure the new Outlook application is used."
|
||||
editUrl: false
|
||||
---
|
||||
|
||||
@@ -11,7 +11,7 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
```json title="config/tweaks.json"
|
||||
"WPFToggleNewOutlook": {
|
||||
"Content": "Microsoft Outlook New Version",
|
||||
"Description": "This will ensures the classic Outlook application is used.",
|
||||
"Description": "This will ensure the new Outlook application is used.",
|
||||
"category": "Customize Preferences",
|
||||
"panel": "2",
|
||||
"Type": "Toggle",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "Activity History - Disable"
|
||||
description: "Erases recent docs, clipboard, and run history."
|
||||
description: "Stops Windows from publishing or uploading user activities while preserving clipboard history."
|
||||
editUrl: false
|
||||
---
|
||||
|
||||
@@ -11,14 +11,14 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
```json title="config/tweaks.json"
|
||||
"WPFTweaksActivity": {
|
||||
"Content": "Activity History - Disable",
|
||||
"Description": "Erases recent docs, clipboard, and run history.",
|
||||
"Description": "Stops Windows from publishing or uploading user activities while preserving clipboard history.",
|
||||
"category": "Essential Tweaks",
|
||||
"panel": "1",
|
||||
"registry": [
|
||||
{
|
||||
"Path": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\System",
|
||||
"Name": "EnableActivityFeed",
|
||||
"Value": "0",
|
||||
"Value": "1",
|
||||
"Type": "DWord",
|
||||
"OriginalValue": "<RemoveEntry>"
|
||||
},
|
||||
|
||||
@@ -16,8 +16,10 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
"panel": "1",
|
||||
"InvokeScript": [
|
||||
"
|
||||
Remove-Item -Path \"$Env:Temp\\*\" -Recurse -Force
|
||||
Remove-Item -Path \"$Env:SystemRoot\\Temp\\*\" -Recurse -Force
|
||||
# A temp folder always holds files something has open, including this run's own, and
|
||||
# the job layer counts a logged error as a failed step
|
||||
Remove-Item -Path \"$Env:Temp\\*\" -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path \"$Env:SystemRoot\\Temp\\*\" -Recurse -Force -ErrorAction SilentlyContinue
|
||||
"
|
||||
],
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
"category": "Essential Tweaks",
|
||||
"panel": "1",
|
||||
"InvokeScript": [
|
||||
"icacls \"$Env:LocalAppData\\Packages\\Microsoft.WindowsStore_8wekyb3d8bbwe\\LocalState\\store.db\" /deny Everyone:F"
|
||||
"icacls \"$Env:LocalAppData\\Packages\\Microsoft.WindowsStore_8wekyb3d8bbwe\\LocalState\\store.db\" /deny *S-1-1-0:F"
|
||||
],
|
||||
"UndoScript": [
|
||||
"icacls \"$Env:LocalAppData\\Packages\\Microsoft.WindowsStore_8wekyb3d8bbwe\\LocalState\\store.db\" /grant Everyone:F"
|
||||
"icacls \"$Env:LocalAppData\\Packages\\Microsoft.WindowsStore_8wekyb3d8bbwe\\LocalState\\store.db\" /grant *S-1-1-0:F"
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "End Task With Right Click - Enable"
|
||||
description: "Enables option to end task when right clicking a program in the taskbar."
|
||||
description: "Enables option to end task when right-clicking a program in the taskbar."
|
||||
editUrl: false
|
||||
---
|
||||
|
||||
@@ -11,7 +11,7 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
```json title="config/tweaks.json"
|
||||
"WPFTweaksEndTaskOnTaskbar": {
|
||||
"Content": "End Task With Right Click - Enable",
|
||||
"Description": "Enables option to end task when right clicking a program in the taskbar.",
|
||||
"Description": "Enables option to end task when right-clicking a program in the taskbar.",
|
||||
"category": "Essential Tweaks",
|
||||
"panel": "1",
|
||||
"registry": [
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: "Logitech Download Assistant Auto-Install - Disable"
|
||||
description: "Blocks the Logi Download Assistant that Windows Update keeps reinstalling with Logitech device drivers. Logitech hardware keeps working without it."
|
||||
editUrl: false
|
||||
---
|
||||
|
||||
:::note
|
||||
This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitusTech/winutil/blob/main/config/tweaks.json). Do not edit this page directly.
|
||||
:::
|
||||
|
||||
```json title="config/tweaks.json"
|
||||
"WPFTweaksLogiBlock": {
|
||||
"Content": "Logitech Download Assistant Auto-Install - Disable",
|
||||
"Description": "Blocks the Logi Download Assistant that Windows Update keeps reinstalling with Logitech device drivers. Logitech hardware keeps working without it.",
|
||||
"category": "z__Advanced Tweaks - CAUTION",
|
||||
"panel": "1",
|
||||
"InvokeScript": [
|
||||
"
|
||||
Stop-Process -Name \"logi_download_assistant\" -Force -ErrorAction SilentlyContinue
|
||||
|
||||
$ProgramFiles64 = if ($Env:ProgramW6432) { $Env:ProgramW6432 } else { $Env:ProgramFiles }
|
||||
$LogiPath = \"$ProgramFiles64\\LogiDownloadAssistant\"
|
||||
|
||||
if (Test-Path $LogiPath) {
|
||||
Remove-Item $LogiPath\\* -Recurse -Force
|
||||
} else {
|
||||
New-Item -Path $LogiPath -ItemType Directory
|
||||
}
|
||||
|
||||
icacls $LogiPath /deny \"*S-1-1-0:(W)\"
|
||||
if ($LASTEXITCODE -ne 0) { throw \"icacls failed to deny write access on $LogiPath (exit code $LASTEXITCODE)\" }
|
||||
"
|
||||
],
|
||||
"UndoScript": [
|
||||
"
|
||||
$ProgramFiles64 = if ($Env:ProgramW6432) { $Env:ProgramW6432 } else { $Env:ProgramFiles }
|
||||
$LogiPath = \"$ProgramFiles64\\LogiDownloadAssistant\"
|
||||
|
||||
if (Test-Path $LogiPath) {
|
||||
icacls $LogiPath /remove:d \"*S-1-1-0\"
|
||||
if ($LASTEXITCODE -ne 0) { throw \"icacls failed to remove the write-deny rule on $LogiPath (exit code $LASTEXITCODE)\" }
|
||||
}
|
||||
"
|
||||
],
|
||||
}
|
||||
```
|
||||
+12
-44
@@ -10,53 +10,21 @@ This page is generated from [`functions/public/Invoke-WPFOOSU.ps1`](https://gith
|
||||
|
||||
```powershell title="functions/public/Invoke-WPFOOSU.ps1"
|
||||
function Invoke-WPFOOSU {
|
||||
if ($sync.ProcessRunning) {
|
||||
Show-WinUtilMessage -Message "Another process is currently running." -Title "WinUtil" -Button "OK" -Icon "Warning"
|
||||
return
|
||||
}
|
||||
Start-WinUtilJob -Name "OOSU" -Description "Downloading O&O ShutUp10++" -Parameters @{
|
||||
DownloadPath = Join-Path $sync.winutildir "ooshutup10.exe"
|
||||
} -ScriptBlock {
|
||||
param($DownloadPath)
|
||||
|
||||
$downloadPath = Join-Path $sync.winutildir "ooshutup10.exe"
|
||||
$sync.ProcessRunning = $true
|
||||
Write-WinUtilLog -Component "OOSU" -Message "Downloading O&O ShutUp10++."
|
||||
|
||||
Invoke-WPFRunspace -ParameterList @(,("downloadPath", $downloadPath)) -ScriptBlock {
|
||||
param($downloadPath)
|
||||
|
||||
$hasUI = $null -ne $sync.Form -and $null -ne $sync.Form.Dispatcher
|
||||
|
||||
try {
|
||||
Write-WinUtilLog -Component "OOSU" -Message "Downloading O&O ShutUp10++."
|
||||
if ($hasUI) {
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Downloading O&O ShutUp10++ (0%)" -Percent 0
|
||||
}
|
||||
|
||||
Save-WinUtilFile -Uri "https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe" -DestinationPath $downloadPath -ProgressCallback {
|
||||
param($percent)
|
||||
|
||||
if ($hasUI) {
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Downloading O&O ShutUp10++ ($percent%)" -Percent $percent
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasUI) {
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Launching O&O ShutUp10++" -Percent 100
|
||||
}
|
||||
Start-Process -FilePath $downloadPath
|
||||
|
||||
Write-WinUtilLog -Component "OOSU" -Message "O&O ShutUp10++ launched."
|
||||
if ($hasUI) {
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "O&O ShutUp10++ launched" -Percent 100
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-WinUtilLog -Level "ERROR" -Component "OOSU" -Message "O&O ShutUp10++ download failed: $($_.Exception.Message)"
|
||||
if ($hasUI) {
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "O&O ShutUp10++ download failed" -Percent 100
|
||||
}
|
||||
Write-Error "Couldn't download O&O ShutUp10. Please make sure you have an active Internet connection."
|
||||
}
|
||||
finally {
|
||||
$sync.ProcessRunning = $false
|
||||
Save-WinUtilFile -Uri "https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe" -DestinationPath $DownloadPath -ProgressCallback {
|
||||
param($percent)
|
||||
Step-WinUtilJob -Status "Downloading O&O ShutUp10++ ($percent%)" -Percent $percent
|
||||
}
|
||||
|
||||
Step-WinUtilJob -Status "Launching O&O ShutUp10++" -Percent 100
|
||||
Start-Process -FilePath $DownloadPath
|
||||
Write-WinUtilLog -Component "OOSU" -Message "O&O ShutUp10++ launched."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
+2
-2
@@ -40,12 +40,12 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
New-Item -Path $RazerPath -ItemType Directory
|
||||
}
|
||||
|
||||
icacls $RazerPath /deny \"Everyone:(W)\"
|
||||
icacls $RazerPath /deny \"*S-1-1-0:(W)\"
|
||||
"
|
||||
],
|
||||
"UndoScript": [
|
||||
"
|
||||
icacls \"$Env:SystemRoot\\Installer\\Razer\" /remove:d Everyone
|
||||
icacls \"$Env:SystemRoot\\Installer\\Razer\" /remove:d *S-1-1-0
|
||||
"
|
||||
],
|
||||
}
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
"InvokeScript": [
|
||||
"
|
||||
# Deny permission to remove OneDrive folder
|
||||
icacls $Env:OneDrive /deny \"Administrators:(D,DC)\"
|
||||
icacls $Env:OneDrive /deny \"*S-1-5-32-544:(D,DC)\"
|
||||
|
||||
Write-Host \"Uninstalling OneDrive...\"
|
||||
Start-Process -FilePath (Join-Path $Env:SystemRoot \"System32\\OneDriveSetup.exe\") -ArgumentList '/uninstall' -Wait
|
||||
@@ -31,7 +31,7 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
Remove-Item \"$Env:ProgramData\\Microsoft OneDrive\" -Recurse -Force
|
||||
|
||||
# Grant back permission to access OneDrive folder
|
||||
icacls $Env:OneDrive /grant \"Administrators:(D,DC)\"
|
||||
icacls $Env:OneDrive /grant \"*S-1-5-32-544:(D,DC)\"
|
||||
|
||||
if (-not (Get-ChildItem -Path $Env:OneDrive)) {
|
||||
Remove-Item -Path $Env:OneDrive -Recurse
|
||||
|
||||
+1
-1
@@ -14,6 +14,6 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
"category": "z__Advanced Tweaks - CAUTION",
|
||||
"panel": "1",
|
||||
"Type": "Combobox",
|
||||
"ComboItems": "Default DHCP Google Cloudflare Cloudflare_Malware Cloudflare_Malware_Adult Open_DNS Quad9 AdGuard_Ads_Trackers AdGuard_Ads_Trackers_Malware_Adult Mullvad Mullvad_Ads_Trackers Mullvad_Ads_Trackers_Malware Mullvad_Ads_Trackers_Malware_Social Mullvad_Ads_Trackers_Malware_Adult_Gambling Mullvad_Ads_Trackers_Malware_Adult_Gambling_Social",
|
||||
"ComboItems": "Default DHCP Fastest Google Cloudflare Cloudflare_Malware Cloudflare_Malware_Adult Open_DNS Quad9 AdGuard_Ads_Trackers AdGuard_Ads_Trackers_Malware_Adult",
|
||||
}
|
||||
```
|
||||
|
||||
@@ -230,7 +230,7 @@ Yes, WinUtil works on Windows Server editions, though some features may not be a
|
||||
|
||||
### Does WinUtil work with Windows LTSC?
|
||||
|
||||
Yes, WinUtil works with Windows 10/11 LTSC editions. Some applications may not be available depending on your configuration.
|
||||
WinUtil currently targets Windows 11. Windows 10 LTSC is not supported, consistent with the Windows 10 support policy above. Some features and applications may vary by Windows 11 edition.
|
||||
|
||||
### Can I use WinUtil in a corporate/enterprise environment?
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Use the quick-selection buttons at the top of the Tweaks tab to speed up setup:
|
||||
* **Minimal**: Selects a smaller, lower-impact set of common tweaks.
|
||||
* **Advanced**: Selects a focused set of safer advanced tweaks. This preset intentionally skips restore point creation and cleanup tasks to avoid a long runtime.
|
||||
* **Clear**: Clears all currently selected tweaks.
|
||||
* **Get Installed Tweaks**: Best-effort detection for tweaks already applied to your system.
|
||||
* **Select Installed Tweaks**: Best-effort detection for tweaks already applied to your system.
|
||||
|
||||
### Run Tweaks
|
||||
* **Open the Tweaks tab**: Navigate to the **Tweaks** tab in the application.
|
||||
@@ -29,6 +29,8 @@ Use the quick-selection buttons at the top of the Tweaks tab to speed up setup:
|
||||
To see what each preset includes, view [preset.json](https://github.com/ChrisTitusTech/winutil/blob/main/config/preset.json).
|
||||
:::
|
||||
|
||||
When a listed service does not exist, WinUtil logs a warning and skips it. The remaining services and tweaks still run.
|
||||
|
||||
:::caution
|
||||
Some tweaks take effect immediately, while others may require Explorer to restart, a sign-out, or a full reboot.
|
||||
:::
|
||||
|
||||
@@ -27,7 +27,7 @@ Changing modes adjusts system-wide Windows Update behavior. After switching mode
|
||||
- **Feature updates**: Delayed by **365 days** to reduce the chance of disruption from major Windows changes.
|
||||
- **Quality updates**: Delayed by **4 days** to allow time for early issues to surface while still keeping the system protected.
|
||||
- **Drivers**: Excluded from Windows quality updates.
|
||||
- **Restarts**: Scheduled updates do not automatically restart Windows while a user is signed in. A restart explicitly scheduled by a user still takes precedence.
|
||||
- **Installation**: Updates download automatically and notify you when they are ready to install. This setting does not control restarts after installation.
|
||||
- **Availability**: Update deferral policies apply to Windows Pro, Enterprise, and Education editions.
|
||||
- **Why use it**: This mode offers the best balance between security and stability, which is why it is the recommended option for most PCs.
|
||||
|
||||
|
||||
@@ -144,6 +144,7 @@ When you install Windows 11 from your modified ISO:
|
||||
| Driver injection seems stuck | WIM servicing can pause at a mount or commit. Allow 10–20 minutes depending on hardware, and check the live log before closing WinUtil |
|
||||
| "Access Denied" error | Make sure WinUtil is running as Administrator |
|
||||
| Driver injection warning | Review the live log for the named package. Other compatible packages continue; if none succeed, the ISO is created with the original `install.wim` |
|
||||
| DISM error with an explanation in parentheses, e.g. "exit code 112 (Disk is full)" | WinUtil now explains common DISM failures in plain language. Free up disk space for a "Disk is full" error, or run WinUtil as Administrator for an "Access denied" error. If the code has no explanation, search the number on Microsoft's [DISM error code reference](https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes) |
|
||||
| "Setup has failed to validate the product key" | Recreate the ISO with the latest WinUtil. The creator now removes stale `PID.txt`, writes `sources\ei.cfg`, and pins the selected image in `autounattend.xml` so setup does not use an embedded OEM key for a different edition |
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@ function Get-WinUtilTweaksStateReport {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Groups every config/tweaks.json entry's live applied state by category, reusing the same
|
||||
detection Invoke-WPFGetInstalled uses to check the "Get Installed Tweaks" checkboxes.
|
||||
detection Invoke-WPFGetInstalled uses to check the "Select Installed Tweaks" checkboxes.
|
||||
#>
|
||||
|
||||
$categoryFieldNames = [ordered]@{
|
||||
|
||||
@@ -356,7 +356,8 @@ function Invoke-WinUtilISOModify {
|
||||
Invoke-WPFUIThread -ScriptBlock {
|
||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = $false
|
||||
}
|
||||
Set-WinUtilISOStep -Step "Working" -Label "Modifying install.wim"
|
||||
$workingLabel = if ($InjectDrivers) { "Modifying install.wim" } else { "Preparing setup media" }
|
||||
Set-WinUtilISOStep -Step "Working" -Label $workingLabel
|
||||
|
||||
$modified = $false
|
||||
try {
|
||||
|
||||
@@ -185,6 +185,21 @@ function Invoke-WinUtilISOScript {
|
||||
return @($survivingFolders)
|
||||
}
|
||||
|
||||
$knownExitCode = @{
|
||||
112 = "Disk is full"
|
||||
5 = "Access denied"
|
||||
2 = "File not found"
|
||||
3 = "Path not found"
|
||||
87 = "Invalid parameter"
|
||||
1168 = "Element not found"
|
||||
1392 = "File or directory is corrupted"
|
||||
32 = "File in use / sharing violation"
|
||||
21 = "Device not ready"
|
||||
1460 = "Operation timed out"
|
||||
1223 = "Operation cancelled by user"
|
||||
50 = "Request not supported"
|
||||
}
|
||||
|
||||
function Invoke-WinUtilISODism {
|
||||
param (
|
||||
[Parameter(Mandatory)][string[]]$Arguments,
|
||||
@@ -199,7 +214,11 @@ function Invoke-WinUtilISOScript {
|
||||
& $Logger " dism[$Operation]: $line"
|
||||
}
|
||||
}
|
||||
throw "DISM $Operation failed with exit code $exitCode."
|
||||
if ($knownExitCode.ContainsKey($exitCode)) {
|
||||
throw "DISM $Operation failed with exit code $exitCode ($($knownExitCode[$exitCode]))."
|
||||
} else {
|
||||
throw "DISM $Operation failed with exit code $exitCode."
|
||||
}
|
||||
}
|
||||
if ($Operation -ne 'metadata') {
|
||||
& $Logger "DISM $Operation completed."
|
||||
@@ -538,7 +557,6 @@ $appxList
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ReserveManager' 'ShippedWithReserves' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\CurrentControlSet\Control\BitLocker' 'PreventDeviceEncryption' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Chat' 'ChatIcon' 'REG_DWORD' '3'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\OneDrive' 'DisableFileSyncNGSC' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\DataCollection' 'AllowTelemetry' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\CurrentControlSet\Services\dmwappushservice' 'Start' 'REG_DWORD' '4'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot' 'TurnOffWindowsCopilot' 'REG_DWORD' '1'
|
||||
|
||||
@@ -23,6 +23,9 @@ function Invoke-WinUtilTweaks {
|
||||
|
||||
$action = if ($undo) { "Undo" } else { "Apply" }
|
||||
Write-WinUtilLog -Component "Tweaks" -Message "$action tweak: $CheckBox"
|
||||
# The counter lives in this runspace, so an error a concurrent job logs from its own
|
||||
# runspace cannot be charged to a toggle flipped on the UI thread
|
||||
$errorsBefore = [int]$global:WinUtilJobErrorCount
|
||||
|
||||
if ($undo) {
|
||||
$Values = @{
|
||||
@@ -45,14 +48,21 @@ function Invoke-WinUtilTweaks {
|
||||
|
||||
# The check for !($undo) is required, without it the script will throw an error for accessing unavailable member, which's the 'OriginalService' Property
|
||||
if ($KeepServiceStartup -AND !($undo)) {
|
||||
$serviceName = $psitem.Name
|
||||
try {
|
||||
# Check if the service exists
|
||||
$service = Get-Service -Name $psitem.Name -ErrorAction Stop
|
||||
$service = Get-Service -Name $serviceName -ErrorAction Stop
|
||||
if(!($service.StartType.ToString() -eq $psitem.$($values.OriginalService))) {
|
||||
$changeservice = $false
|
||||
}
|
||||
} catch [System.ServiceProcess.ServiceNotFoundException] {
|
||||
Write-Warning "Service $($psitem.Name) was not found."
|
||||
} catch {
|
||||
if ($_.FullyQualifiedErrorId -like "NoServiceFoundForGivenName,*") {
|
||||
$changeservice = $false
|
||||
Write-Warning "Service $serviceName was not found."
|
||||
Write-WinUtilLog -Level "WARN" -Component "Service" -Message "Service $serviceName was not found."
|
||||
} else {
|
||||
throw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,5 +91,10 @@ function Invoke-WinUtilTweaks {
|
||||
Remove-WinUtilProvisionedAPPX -PackageList $sync.configs.tweaks.$CheckBox.appx
|
||||
}
|
||||
}
|
||||
Write-WinUtilLog -Component "Tweaks" -Message "$action tweak completed: $CheckBox"
|
||||
$errorCount = [int]$global:WinUtilJobErrorCount - $errorsBefore
|
||||
if ($errorCount -gt 0) {
|
||||
Write-WinUtilLog -Level "WARN" -Component "Tweaks" -Message "$action tweak finished with $errorCount error(s): $CheckBox"
|
||||
} else {
|
||||
Write-WinUtilLog -Component "Tweaks" -Message "$action tweak completed: $CheckBox"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,10 @@ function Write-WinUtilLog {
|
||||
$null = $sync.LoggedErrors.Add("[$Component] $Message")
|
||||
}
|
||||
|
||||
if ($Level -eq "ERROR" -and -not $Detail -and $global:WinUtilIsJobWorker) {
|
||||
# Global scope is per runspace, so this counter only ever sees errors logged by the
|
||||
# runspace that owns it: a job worker reads its own, and a tweak on the UI thread reads the
|
||||
# UI thread's
|
||||
if ($Level -eq "ERROR" -and -not $Detail) {
|
||||
$global:WinUtilJobErrorCount++
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ function Invoke-WPFUpdatessecurity {
|
||||
1. Disables driver offering through Windows Update
|
||||
2. Defers feature updates for 365 days
|
||||
3. Defers quality updates for 4 days
|
||||
4. Prevents automatic restarts while a user is signed in
|
||||
4. Configures automatic updates to notify when downloaded updates are ready to install
|
||||
|
||||
#>
|
||||
|
||||
@@ -67,13 +67,16 @@ function Invoke-WPFUpdatessecurity {
|
||||
Remove-ItemProperty -Path $legacySettingsPath -Name $legacyValue -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Host "Preventing automatic restarts while users are signed in..."
|
||||
Write-WinUtilLog -Component "Updates" -Message "Configuring scheduled automatic updates without restarting while users are signed in."
|
||||
Write-Host "Configuring automatic updates to download and notify before installation..."
|
||||
Write-WinUtilLog -Component "Updates" -Message "Configuring automatic updates to download and notify before installation."
|
||||
|
||||
New-Item -Path $automaticUpdatePolicyPath -Force
|
||||
# NoAutoRebootWithLoggedOnUsers only applies when automatic updates use option 4.
|
||||
Set-ItemProperty -Path $automaticUpdatePolicyPath -Name "AUOptions" -Type DWord -Value 4
|
||||
Set-ItemProperty -Path $automaticUpdatePolicyPath -Name "NoAutoRebootWithLoggedOnUsers" -Type DWord -Value 1
|
||||
|
||||
# Remove the previous scheduled-install reboot policy when switching to download-and-notify.
|
||||
Remove-ItemProperty -Path $automaticUpdatePolicyPath -Name "NoAutoRebootWithLoggedOnUsers" -ErrorAction SilentlyContinue
|
||||
|
||||
# AUOptions 3 downloads updates and notifies before installation; it does not control restarts.
|
||||
Set-ItemProperty -Path $automaticUpdatePolicyPath -Name "AUOptions" -Type DWord -Value 3
|
||||
Set-ItemProperty -Path $automaticUpdatePolicyPath -Name "AUPowerManagement" -Type DWord -Value 0
|
||||
|
||||
Write-WinUtilLog -Component "Updates" -Message "Recommended Windows Update settings workflow completed."
|
||||
|
||||
@@ -131,7 +131,7 @@ Describe "Write-WinUtilLog" {
|
||||
Should -Invoke -CommandName Write-Warning -Times 0 -Exactly
|
||||
}
|
||||
|
||||
It "counts only headline errors written by the active job worker" {
|
||||
It "counts headline errors in the logging runspace whether or not it is a job worker" {
|
||||
$script:sync = [hashtable]::Synchronized(@{
|
||||
winutildir = $script:testRoot
|
||||
LoggedErrors = [System.Collections.ArrayList]::Synchronized([System.Collections.ArrayList]::new())
|
||||
@@ -142,9 +142,9 @@ Describe "Write-WinUtilLog" {
|
||||
Write-WinUtilLog -Level "ERROR" -Component "Test" -Message "job error"
|
||||
Write-WinUtilLog -Level "ERROR" -Detail -Component "Test" -Message "error detail"
|
||||
$global:WinUtilIsJobWorker = $false
|
||||
Write-WinUtilLog -Level "ERROR" -Component "UI" -Message "unrelated error"
|
||||
Write-WinUtilLog -Level "ERROR" -Component "UI" -Message "toggle error"
|
||||
|
||||
$global:WinUtilJobErrorCount | Should -Be 1
|
||||
$global:WinUtilJobErrorCount | Should -Be 2
|
||||
$script:sync.LoggedErrors.Count | Should -Be 2
|
||||
}
|
||||
|
||||
|
||||
@@ -177,6 +177,148 @@ Describe "Invoke-WinUtilTweaks" {
|
||||
$Name -eq "DiagTrack" -and $StartupType -eq "Disabled"
|
||||
}
|
||||
}
|
||||
|
||||
It "skips a missing service and applies the remaining services and tweaks" {
|
||||
$script:sync.configs.tweaks.WPFTweaksExample.service = @(
|
||||
[pscustomobject]@{
|
||||
Name = "CscService"
|
||||
StartupType = "Disabled"
|
||||
OriginalType = "Manual"
|
||||
}
|
||||
) + $script:sync.configs.tweaks.WPFTweaksExample.service
|
||||
|
||||
Mock Get-Service {
|
||||
$exception = [Microsoft.PowerShell.Commands.ServiceCommandException]::new("Cannot find any service with service name '$Name'.")
|
||||
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
|
||||
$exception,
|
||||
"NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand",
|
||||
[System.Management.Automation.ErrorCategory]::ObjectNotFound,
|
||||
$Name
|
||||
)
|
||||
throw $errorRecord
|
||||
} -ParameterFilter { $Name -eq "CscService" -and $ErrorAction -eq "Stop" }
|
||||
|
||||
foreach ($tweak in @("WPFTweaksExample", "WPFTweaksServiceOnly")) {
|
||||
Invoke-WinUtilTweaks -CheckBox $tweak
|
||||
}
|
||||
|
||||
Should -Invoke -CommandName Write-Warning -Times 1 -Exactly -ParameterFilter {
|
||||
$Message -eq "Service CscService was not found."
|
||||
}
|
||||
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
|
||||
$Level -eq "WARN" -and $Component -eq "Service" -and $Message -eq "Service CscService was not found."
|
||||
}
|
||||
Should -Invoke -CommandName Set-WinUtilService -Times 0 -Exactly -ParameterFilter {
|
||||
$Name -eq "CscService"
|
||||
}
|
||||
Should -Invoke -CommandName Set-WinUtilService -Times 2 -Exactly -ParameterFilter {
|
||||
$Name -eq "DiagTrack" -and $StartupType -eq "Disabled"
|
||||
}
|
||||
Should -Invoke -CommandName Invoke-WinUtilScript -Times 1 -Exactly -ParameterFilter {
|
||||
$Name -eq "WPFTweaksExample"
|
||||
}
|
||||
}
|
||||
|
||||
It "rethrows service lookup errors that do not identify a missing service" {
|
||||
Mock Get-Service {
|
||||
$exception = [Microsoft.PowerShell.Commands.ServiceCommandException]::new("Service lookup failed.")
|
||||
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
|
||||
$exception,
|
||||
"ServiceLookupFailed",
|
||||
[System.Management.Automation.ErrorCategory]::OpenError,
|
||||
$Name
|
||||
)
|
||||
throw $errorRecord
|
||||
}
|
||||
|
||||
{ Invoke-WinUtilTweaks -CheckBox "WPFTweaksExample" } | Should -Throw -ExpectedMessage "*Service lookup failed.*"
|
||||
|
||||
Should -Invoke -CommandName Set-WinUtilService -Times 0 -Exactly
|
||||
Should -Invoke -CommandName Set-WinUtilRegistry -Times 0 -Exactly
|
||||
Should -Invoke -CommandName Invoke-WinUtilScript -Times 0 -Exactly
|
||||
Should -Invoke -CommandName Write-Warning -Times 0 -Exactly
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Invoke-WinUtilTweaks completion status" {
|
||||
BeforeAll {
|
||||
. (Join-Path $script:repoRoot "functions\private\Write-WinUtilLog.ps1")
|
||||
. (Join-Path $script:repoRoot "functions\private\Invoke-WinUtilScript.ps1")
|
||||
}
|
||||
|
||||
BeforeEach {
|
||||
$script:testRoot = Join-Path ([System.IO.Path]::GetTempPath()) "winutil-tweaks-$([guid]::NewGuid())"
|
||||
$script:logPath = Join-Path $script:testRoot "logs\winutil_2026-09-15_12-00-00.log"
|
||||
$script:sync = [Hashtable]::Synchronized(@{
|
||||
logPath = $script:logPath
|
||||
configs = @{
|
||||
tweaks = [pscustomobject]@{
|
||||
WPFTweaksFailing = [pscustomobject]@{
|
||||
InvokeScript = @("throw 'simulated icacls failure'")
|
||||
UndoScript = @("throw 'simulated icacls undo failure'")
|
||||
}
|
||||
WPFTweaksClean = [pscustomobject]@{
|
||||
InvokeScript = @("Write-Output 'apply tweak'")
|
||||
}
|
||||
# A job worker logging an error from its own runspace lands in the shared
|
||||
# list without passing through this runspace's logger
|
||||
WPFTweaksDuringJob = [pscustomobject]@{
|
||||
InvokeScript = @("`$null = `$sync.LoggedErrors.Add('[Job] error from a concurrent job'); Write-Output 'apply tweak'")
|
||||
}
|
||||
}
|
||||
}
|
||||
# Seeded with an earlier error: only errors logged during this tweak may count
|
||||
LoggedErrors = [System.Collections.ArrayList]::Synchronized([System.Collections.ArrayList]::new(@("[UI] earlier unrelated failure")))
|
||||
})
|
||||
# Toggle switches run the tweak on the UI thread, outside any job worker. The runspace
|
||||
# counter starts non-zero: only errors logged during this tweak may count
|
||||
Remove-Variable -Name WinUtilIsJobWorker -Scope Global -ErrorAction SilentlyContinue
|
||||
$global:WinUtilJobErrorCount = 3
|
||||
|
||||
Mock Write-Host { }
|
||||
Mock Write-Warning { }
|
||||
}
|
||||
|
||||
AfterEach {
|
||||
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
|
||||
Remove-Variable -Name WinUtilJobErrorCount -Scope Global -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $script:testRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
It "warns instead of reporting completion when a tweak step logged an error" {
|
||||
Invoke-WinUtilTweaks -CheckBox "WPFTweaksFailing"
|
||||
|
||||
$log = Get-Content -Path $script:logPath -Raw
|
||||
$log | Should -Match "\[ERROR\] \[Script\] Runtime exception while running script for WPFTweaksFailing"
|
||||
$log | Should -Match "\[WARN\] \[Tweaks\] Apply tweak finished with 1 error\(s\): WPFTweaksFailing"
|
||||
$log | Should -Not -Match "tweak completed: WPFTweaksFailing"
|
||||
}
|
||||
|
||||
It "warns when an undo step logged an error" {
|
||||
Invoke-WinUtilTweaks -CheckBox "WPFTweaksFailing" -undo $true
|
||||
|
||||
$log = Get-Content -Path $script:logPath -Raw
|
||||
$log | Should -Match "\[ERROR\] \[Script\] Runtime exception while running script for WPFTweaksFailing"
|
||||
$log | Should -Match "\[WARN\] \[Tweaks\] Undo tweak finished with 1 error\(s\): WPFTweaksFailing"
|
||||
$log | Should -Not -Match "tweak completed: WPFTweaksFailing"
|
||||
}
|
||||
|
||||
It "reports completion when every tweak step succeeded" {
|
||||
Invoke-WinUtilTweaks -CheckBox "WPFTweaksClean"
|
||||
|
||||
$log = Get-Content -Path $script:logPath -Raw
|
||||
$log | Should -Match "\[INFO\] \[Tweaks\] Apply tweak completed: WPFTweaksClean"
|
||||
$log | Should -Not -Match "\[WARN\]"
|
||||
$log | Should -Not -Match "\[ERROR\]"
|
||||
}
|
||||
|
||||
It "ignores an error another runspace logged while the tweak ran" {
|
||||
Invoke-WinUtilTweaks -CheckBox "WPFTweaksDuringJob"
|
||||
|
||||
$log = Get-Content -Path $script:logPath -Raw
|
||||
$log | Should -Match "\[INFO\] \[Tweaks\] Apply tweak completed: WPFTweaksDuringJob"
|
||||
$log | Should -Not -Match "tweak finished with"
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Invoke-WPFtweaksbutton" {
|
||||
|
||||
@@ -333,7 +333,7 @@ Describe "Invoke-WPFUpdatessecurity" {
|
||||
}
|
||||
}
|
||||
|
||||
It "sets recommended update deferral and auto-reboot policy values" {
|
||||
It "sets recommended update deferral and installation notification policy values" {
|
||||
Invoke-WPFUpdatessecurity
|
||||
|
||||
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||
@@ -360,17 +360,15 @@ Describe "Invoke-WPFUpdatessecurity" {
|
||||
$Type -eq "DWord" -and
|
||||
$Value -eq 4
|
||||
}
|
||||
Should -Invoke -CommandName Remove-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||
$Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and
|
||||
$Name -eq "NoAutoRebootWithLoggedOnUsers"
|
||||
}
|
||||
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||
$Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and
|
||||
$Name -eq "AUOptions" -and
|
||||
$Type -eq "DWord" -and
|
||||
$Value -eq 4
|
||||
}
|
||||
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||
$Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and
|
||||
$Name -eq "NoAutoRebootWithLoggedOnUsers" -and
|
||||
$Type -eq "DWord" -and
|
||||
$Value -eq 1
|
||||
$Value -eq 3
|
||||
}
|
||||
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||
$Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and
|
||||
|
||||
@@ -950,6 +950,109 @@ Describe "Win11 Creator setup media" {
|
||||
}
|
||||
}
|
||||
|
||||
It "reports a friendly explanation when DISM fails with a known exit code" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoMountFailureKnownCode_$([guid]::NewGuid())"
|
||||
$installWim = Join-Path $contentRoot 'sources\install.wim'
|
||||
$template = Get-Content -Path $script:autoUnattendPath -Raw
|
||||
$script:dismCalls = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
function dism.exe {
|
||||
param([Parameter(ValueFromRemainingArguments)][string[]]$Arguments)
|
||||
|
||||
$script:dismCalls.Add(($Arguments -join '|'))
|
||||
if ($Arguments -contains '/Get-WimInfo') {
|
||||
$global:LASTEXITCODE = 0
|
||||
'Languages : en-US'
|
||||
'Installation : Client'
|
||||
'Edition : Professional'
|
||||
'ProductSuite : Terminal Server'
|
||||
'ProductType : WinNT'
|
||||
} elseif ($Arguments -contains '/Mount-Image') {
|
||||
$global:LASTEXITCODE = 112
|
||||
'Mount failed'
|
||||
} elseif ($Arguments -contains '/Get-MountedImageInfo') {
|
||||
$global:LASTEXITCODE = 0
|
||||
"Mount Dir : $(Join-Path (Split-Path -Path $contentRoot -Parent) 'wim_mount')"
|
||||
} elseif ($Arguments -contains '/Export-Driver') {
|
||||
$global:LASTEXITCODE = 0
|
||||
Export-WinUtilTestDriverPackage -Arguments $Arguments -Fixtures @(
|
||||
@{ Path = 'storage_pkg'; Name = 'iaStorAC.inf'; Class = 'System' }
|
||||
)
|
||||
} else {
|
||||
$global:LASTEXITCODE = 0
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
New-Item -Path (Split-Path $installWim -Parent) -ItemType Directory -Force | Out-Null
|
||||
Set-Content -Path $installWim -Value 'mock-wim'
|
||||
. $script:isoScriptPath
|
||||
|
||||
{ Invoke-WinUtilISOScript -ISOContentsDir $contentRoot -AutoUnattendXml $template -InjectCurrentSystemDrivers $true -InstallImagePath $installWim -InstallImageIndex 6 -InstallEditionId 'Professional' } |
|
||||
Should -Throw '*112*Disk is full*'
|
||||
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Get-MountedImageInfo' }).Count | Should -Be 1
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Unmount-Image\|.*\|/Discard' }).Count | Should -Be 1
|
||||
} finally {
|
||||
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $contentRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
It "falls back to the plain exit code for an unmapped DISM failure" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoMountFailureUnknownCode_$([guid]::NewGuid())"
|
||||
$installWim = Join-Path $contentRoot 'sources\install.wim'
|
||||
$template = Get-Content -Path $script:autoUnattendPath -Raw
|
||||
$script:dismCalls = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
function dism.exe {
|
||||
param([Parameter(ValueFromRemainingArguments)][string[]]$Arguments)
|
||||
|
||||
$script:dismCalls.Add(($Arguments -join '|'))
|
||||
if ($Arguments -contains '/Get-WimInfo') {
|
||||
$global:LASTEXITCODE = 0
|
||||
'Languages : en-US'
|
||||
'Installation : Client'
|
||||
'Edition : Professional'
|
||||
'ProductSuite : Terminal Server'
|
||||
'ProductType : WinNT'
|
||||
} elseif ($Arguments -contains '/Mount-Image') {
|
||||
$global:LASTEXITCODE = 999
|
||||
'Mount failed'
|
||||
} elseif ($Arguments -contains '/Get-MountedImageInfo') {
|
||||
$global:LASTEXITCODE = 0
|
||||
"Mount Dir : $(Join-Path (Split-Path -Path $contentRoot -Parent) 'wim_mount')"
|
||||
} elseif ($Arguments -contains '/Export-Driver') {
|
||||
$global:LASTEXITCODE = 0
|
||||
Export-WinUtilTestDriverPackage -Arguments $Arguments -Fixtures @(
|
||||
@{ Path = 'storage_pkg'; Name = 'iaStorAC.inf'; Class = 'System' }
|
||||
)
|
||||
} else {
|
||||
$global:LASTEXITCODE = 0
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
New-Item -Path (Split-Path $installWim -Parent) -ItemType Directory -Force | Out-Null
|
||||
Set-Content -Path $installWim -Value 'mock-wim'
|
||||
. $script:isoScriptPath
|
||||
|
||||
$thrown = $null
|
||||
try {
|
||||
Invoke-WinUtilISOScript -ISOContentsDir $contentRoot -AutoUnattendXml $template -InjectCurrentSystemDrivers $true -InstallImagePath $installWim -InstallImageIndex 6 -InstallEditionId 'Professional'
|
||||
} catch {
|
||||
$thrown = $_.Exception.Message
|
||||
}
|
||||
$thrown | Should -Be 'DISM mount failed with exit code 999.'
|
||||
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Get-MountedImageInfo' }).Count | Should -Be 1
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Unmount-Image\|.*\|/Discard' }).Count | Should -Be 1
|
||||
} finally {
|
||||
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $contentRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
It "does not add driver setup artifacts when injection is disabled" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoNoDrivers_$([guid]::NewGuid())"
|
||||
|
||||
@@ -1017,4 +1120,83 @@ Describe "Win11 Creator setup media" {
|
||||
$exportRunIndex | Should -BeGreaterThan $exportDialogIndex
|
||||
$script:exportFunction | Should -Match ([regex]::Escape('return'))
|
||||
}
|
||||
|
||||
Context "FirstLogon update service restoration" {
|
||||
BeforeAll {
|
||||
[xml]$unattend = Get-Content -LiteralPath $script:autoUnattendPath -Raw
|
||||
$firstLogon = $unattend.SelectSingleNode("//*[local-name()='File' and @path='C:\Windows\Setup\Scripts\FirstLogon.ps1']").InnerText
|
||||
$tokens = $null
|
||||
$parseErrors = $null
|
||||
$ast = [System.Management.Automation.Language.Parser]::ParseInput($firstLogon, [ref]$tokens, [ref]$parseErrors)
|
||||
if ($parseErrors.Count) { throw 'FirstLogon script failed to parse.' }
|
||||
$blocks = @($ast.FindAll({
|
||||
param($node)
|
||||
$node -is [System.Management.Automation.Language.ScriptBlockExpressionAst] -and
|
||||
$node.ScriptBlock.Find({
|
||||
param($command)
|
||||
$command -is [System.Management.Automation.Language.CommandAst] -and
|
||||
$command.GetCommandName() -eq 'Set-Service'
|
||||
}, $false)
|
||||
}, $true))
|
||||
if ($blocks.Count -ne 1) { throw 'Expected exactly one service restoration block.' }
|
||||
# Never execute the surrounding FirstLogon cleanup, downloads, or installer.
|
||||
$commands = $blocks[0].ScriptBlock.FindAll({
|
||||
param($node)
|
||||
$node -is [System.Management.Automation.Language.CommandAst]
|
||||
}, $true)
|
||||
foreach ($command in $commands) {
|
||||
if ($command.GetCommandName() -notin @('reg.exe', 'Set-Service', 'Set-ItemProperty')) {
|
||||
throw "Unexpected command in restoration block: $($command.Extent.Text)"
|
||||
}
|
||||
}
|
||||
$script:restoreServices = $blocks[0].ScriptBlock.GetScriptBlock()
|
||||
function reg.exe { param([Parameter(ValueFromRemainingArguments)][string[]]$Arguments) }
|
||||
}
|
||||
|
||||
BeforeEach {
|
||||
Mock reg.exe { }
|
||||
Mock Set-Service { }
|
||||
Mock Set-ItemProperty { }
|
||||
}
|
||||
|
||||
It "restores ordinary services and writes the protected Medic startup value directly" {
|
||||
& $script:restoreServices
|
||||
|
||||
Should -Invoke Set-Service -Times 3 -Exactly
|
||||
Should -Invoke Set-Service -Times 1 -Exactly -ParameterFilter { $Name -eq 'BITS' -and $StartupType -eq 'Manual' -and $ErrorAction -eq 'Continue' }
|
||||
Should -Invoke Set-Service -Times 1 -Exactly -ParameterFilter { $Name -eq 'wuauserv' -and $StartupType -eq 'Manual' -and $ErrorAction -eq 'Continue' }
|
||||
Should -Invoke Set-Service -Times 1 -Exactly -ParameterFilter { $Name -eq 'UsoSvc' -and $StartupType -eq 'Automatic' -and $ErrorAction -eq 'Continue' }
|
||||
Should -Invoke Set-Service -Times 0 -Exactly -ParameterFilter { $Name -eq 'WaaSMedicSvc' }
|
||||
Should -Invoke Set-ItemProperty -Times 1 -Exactly
|
||||
Should -Invoke Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||
$Path -eq 'HKLM:\SYSTEM\CurrentControlSet\Services\WaaSMedicSvc' -and
|
||||
$Name -eq 'Start' -and $Value -eq 3 -and $Type -eq 'DWord' -and $ErrorAction -eq 'Continue'
|
||||
}
|
||||
}
|
||||
|
||||
It "keeps failures observable while attempting the remaining restoration work" {
|
||||
$script:restorationCalls = [System.Collections.Generic.List[string]]::new()
|
||||
Mock Set-Service {
|
||||
param($Name, $ErrorAction)
|
||||
$script:restorationCalls.Add($Name)
|
||||
if ($script:restorationCalls.Count -eq 1) {
|
||||
Write-Error 'simulated service restoration failure' -ErrorAction $ErrorAction
|
||||
}
|
||||
}
|
||||
Mock Set-ItemProperty {
|
||||
param($ErrorAction)
|
||||
$script:restorationCalls.Add('Medic registry')
|
||||
Write-Error 'simulated Medic registry failure' -ErrorAction $ErrorAction
|
||||
}
|
||||
|
||||
$output = @(& $script:restoreServices 2>&1)
|
||||
$failures = @($output | Where-Object { $_ -is [System.Management.Automation.ErrorRecord] })
|
||||
$failures.Count | Should -Be 2
|
||||
$failures[0].Exception.Message | Should -Be 'simulated service restoration failure'
|
||||
$failures[1].Exception.Message | Should -Be 'simulated Medic registry failure'
|
||||
$script:restorationCalls.Count | Should -Be 4
|
||||
@($script:restorationCalls | Select-Object -First 3 | Sort-Object) | Should -Be @('BITS', 'UsoSvc', 'wuauserv')
|
||||
$script:restorationCalls[3] | Should -Be 'Medic registry'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,10 +453,11 @@ $scripts = @(
|
||||
reg.exe delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" /v DODownloadMode /f;
|
||||
reg.exe add "HKLM\Software\Policies\Microsoft\Windows\OneDrive" /v DisableFileSyncNGSC /t REG_DWORD /d 0 /f;
|
||||
reg.exe add "HKCU\Software\Microsoft\Windows\CurrentVersion\GameDVR" /v AppCaptureEnabled /t REG_DWORD /d 0 /f;
|
||||
$services = @{ BITS = 'Manual'; wuauserv = 'Manual'; UsoSvc = 'Automatic'; WaaSMedicSvc = 'Manual' };
|
||||
$services = @{ BITS = 'Manual'; wuauserv = 'Manual'; UsoSvc = 'Automatic' };
|
||||
foreach ($name in $services.Keys) {
|
||||
Set-Service -Name $name -StartupType $services[$name] -ErrorAction SilentlyContinue;
|
||||
Set-Service -Name $name -StartupType $services[$name] -ErrorAction Continue;
|
||||
}
|
||||
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\WaaSMedicSvc' -Name 'Start' -Value 3 -Type DWord -ErrorAction Continue;
|
||||
};
|
||||
{
|
||||
reg.exe add "HKLM\SOFTWARE\Microsoft\PolicyManager\current\device\Education" /f;
|
||||
|
||||
+8
-8
@@ -1423,7 +1423,7 @@
|
||||
<Button Name="WPFminimal" Content=" Minimal " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
<Button Name="WPFAdvanced" Content=" Advanced " 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=" Get Installed Tweaks " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
<Button Name="WPFGetInstalledTweaks" Content=" Select Installed Tweaks " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
<Button Name="WPFAppxRemoval" Content=" AppX Removal " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
@@ -1503,7 +1503,7 @@
|
||||
<TextBlock Text="- Defers feature updates for 365 days" TextWrapping="Wrap" Margin="0,0,0,7" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||
<TextBlock Text="- Defers quality updates for 4 days" TextWrapping="Wrap" Margin="0,0,0,7" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||
<TextBlock Text="- Excludes drivers from quality updates" TextWrapping="Wrap" Margin="0,0,0,7" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||
<TextBlock Text="- Prevents automatic restarts while a user is signed in" TextWrapping="Wrap" Margin="0,0,0,12" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||
<TextBlock Text="- Notifies when downloaded updates are ready to install" TextWrapping="Wrap" Margin="0,0,0,12" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||
<TextBlock Text="Available on Windows Pro, Enterprise, and Education editions."
|
||||
FontSize="11"
|
||||
FontStyle="Italic"
|
||||
@@ -1966,7 +1966,7 @@
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
IsChecked="False"
|
||||
Cursor="Hand"
|
||||
ToolTip="Stages boot-storage drivers for Setup and adds all exported drivers to the selected install.wim edition in one DISM pass."/>
|
||||
ToolTip="Injects boot-storage drivers for Setup and adds exported drivers to the selected install.wim edition individually, skipping incompatible packages."/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@@ -2215,7 +2215,7 @@
|
||||
<Label Content="Selections:" FontSize="{DynamicResource FontSize}" VerticalAlignment="Center" Margin="2"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,2,0,0">
|
||||
<Button Name="WPFDefaultAppxSelection" Content=" Default " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
<Button Name="WPFGetInstalledAppx" Content=" Get Installed " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
<Button Name="WPFGetInstalledAppx" Content=" Select Installed " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
<Button Name="WPFSelectAllAppx" Content=" Select All " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
<Button Name="WPFClearAppxSelection" Content=" Clear Selection " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
</StackPanel>
|
||||
@@ -2225,13 +2225,13 @@
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Row="2" Style="{StaticResource BorderStyle}" Margin="5,15,5,5">
|
||||
<StackPanel Background="{DynamicResource MainBackgroundColor}" Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<WrapPanel Background="{DynamicResource MainBackgroundColor}" Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Padding="10" TextWrapping="Wrap" Foreground="{DynamicResource MainForegroundColor}">
|
||||
Note: Select the Windows AppX packages you wish to install or remove.
|
||||
<LineBreak/>Install Selected registers a local manifest when available, then falls back to the Microsoft Store.
|
||||
<LineBreak/>Remove Selected removes packages for the current user and all new user profiles.
|
||||
<LineBreak/>'Install Selected' registers a local manifest for the current user when available, then falls back to the Microsoft Store.
|
||||
<LineBreak/>'Remove Selected' removes packages for all users and stops new profiles from getting them by default.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
Reference in New Issue
Block a user