This guide is for administrators, architects, and contributors who need to understand how WinUtil is built and how it behaves on a managed system. It covers the build pipeline, runtime architecture, the configuration-driven UI engine, every category of system change WinUtil makes, security considerations, deployment/automation, and the repository internals needed to extend or audit the tool.
For end-user installation and usage, see the companion User Guide.
1. Executive Summary
WinUtil is a single-file PowerShell + WPF desktop application for post-install Windows 11 administration. It is configuration-driven: the application catalog, tweaks, optional features, presets, themes, and DNS providers all live in JSON files under config/, and the GUI is generated dynamically from those JSON definitions at runtime. There is no compiled binary and no installer — distribution is a single winutil.ps1 script fetched from GitHub releases and executed in memory.
| Attribute | Value |
|---|---|
| Language / UI | PowerShell 5.1+ / WPF (PresentationFramework) + WinForms helpers |
| Distribution | Single winutil.ps1 (built by Compile.ps1); run via irm https://christitus.com/win | iex |
| Target OS | Windows 11 (Windows 10 EOL 2025-10-14); Server/LTSC with caveats |
| Privileges | Requires Administrator; self-elevates on launch |
| Concurrency | RunspacePool (1 → $env:NUMBER_OF_PROCESSORS threads) with a synchronized $sync hashtable |
| Config model | JSON files in config/ define apps, tweaks, features, presets, themes, DNS, navigation |
| Persistence | None on the host except logs and a working dir under %LocalAppData%\winutil |
| Source repo | https://github.com/ChrisTitusTech/winutil (MIT-licensed core) |
| This revision | Commit e83033a, 239 files, ~257 KB of source, medium complexity tier |
Design philosophy: keep the engine tiny and data-driven. Adding an app or a tweak means editing JSON, not PowerShell. The PowerShell layer is a generic interpreter that renders the JSON into WPF controls and dispatches each control's data (registry entries, services, appx names, scripts) to a small set of apply functions.
Important
WinUtil's user-facing surface area is defined almost entirely by
config/*.jsonandxaml/inputXML.xaml. Static code analysis of the PowerShell alone will under-report what the tool does — the JSON is the specification. AST extraction over this repo yields essentially nothing; the configuration files are the source of truth.
2. Architecture
2.1 Build-time: compilation into a single script
WinUtil ships as one file, winutil.ps1, assembled by Compile.ps1 at the repo root. The build concatenates, in order:
scripts/start.ps1— the preamble (parameters, elevation,$syncbootstrap, logging). The build replaces the#{replaceme}version token with the current date (yy.MM.dd).- Every
*.ps1underfunctions/(recursively) — all private and public functions. - Each
config/*.json, embedded as a here-string and parsed back withConvertFrom-Jsoninto$sync.configs.<name>. Forapplications.json, every key is prefixed withWPFInstallso app keys become control names (e.g.chrome→WPFInstallchrome). xaml/inputXML.xaml— embedded as$inputXML.tools/autounattend.xml— embedded as$WinUtilAutounattendXml(used by MicroWin).scripts/main.ps1— the GUI bootstrapper and event wiring (runs last).
Compile.ps1
├── scripts/start.ps1 (params, elevation, $sync, logging)
├── functions/**/*.ps1 (private + public functions)
├── config/*.json ──embed──▶ $sync.configs.<name>
├── xaml/inputXML.xaml ─embed─▶ $inputXML
├── tools/autounattend.xml ──▶ $WinUtilAutounattendXml
└── scripts/main.ps1 (load XAML, wire events, ShowDialog)
└──▶ winutil.ps1 (single distributable file)
Compile.ps1 -Run builds and immediately launches the result. The windev.ps1 helper fetches and runs the latest pre-release winutil.ps1 from GitHub tags.
Note
Because the public release is a single generated file, the GitHub release
winutil.ps1is the only artifact end users ever execute. Theconfig/,functions/, andxaml/layout exists for development and is collapsed into that one file at build time.
2.2 Runtime: startup sequence
irm christitus.com/win | iex
│
▼
scripts/start.ps1
• parse params (-Config / -Preset / -Noui / -Offline)
• verify FullLanguage mode (abort if Constrained)
• verify Administrator; relaunch elevated via wt.exe / pwsh / powershell if not
• Add-Type PresentationFramework + System.Windows.Forms
• build synchronized $sync hashtable (selections, prefs, version)
• create %LocalAppData%\winutil + \logs, Start-Transcript
│
▼
(function bodies + embedded JSON/XAML are defined inline here)
│
▼
scripts/main.ps1
• print CTT ASCII banner; define PackageManagers enum (Winget, Choco)
• create RunspacePool (1..NUMBER_OF_PROCESSORS) seeded with all winutil/WPF functions + $sync
• define exception classes (WingetFailedInstall, ChocoFailedInstall, GenericException)
• build applicationsHashtable; Set-Preferences
• IF -Preset: apply preset → Invoke-WinUtilAutoRun → exit (no GUI)
• IF -Noui: require -Config → import → Invoke-WinUtilAutoRun → exit
• else: load XAML → bind named controls into $sync → render panels
(Invoke-WPFUIElements for apps/tweaks/features) → wire events → ShowDialog
2.3 The $sync synchronized hashtable
$sync = [Hashtable]::Synchronized(@{}) is the single shared-state object across the main thread and all runspace threads. It holds:
$sync.configs.*— parsed JSON (applications, tweaks, feature, preset, dns, themes, appnavigation) plusapplicationsHashtable.$sync.Formand every named XAML element (each control withName="X"is stored as$sync.XviaFindName).- Selection lists:
selectedApps,selectedTweaks,selectedToggles,selectedFeatures. $sync.preferences(e.g. persistedpackagemanager),$sync.ProcessRunning,$sync.currentTab,$sync.runspace(the RunspacePool),$sync.version.
Because it's a synchronized hashtable, background runspaces can safely read selections and update progress while the WPF thread owns the UI.
2.4 Concurrency model
Long-running work (installs, tweaks, feature installs) is dispatched to the RunspacePool via Invoke-WPFRunspace, which creates a [powershell] instance bound to $sync.runspace and BeginInvoke()s a scriptblock. UI updates from inside a runspace are marshaled back to the WPF dispatcher with Invoke-WPFUIThread ($sync.form.Dispatcher.Invoke). The $sync.ProcessRunning flag guards against starting overlapping operations and disables keyboard shortcuts while busy.
2.5 Component inventory
| Component | Location | Responsibility |
|---|---|---|
| Build script | Compile.ps1 |
Bundle config + xaml + functions → winutil.ps1 |
| Entry preamble | scripts/start.ps1 |
Params, elevation, $sync, logging |
| GUI bootstrap | scripts/main.ps1 |
XAML load, event wiring, ShowDialog |
| UI definition | xaml/inputXML.xaml |
WPF layout, styles, tab structure |
| App catalog | config/applications.json |
Installable apps (winget/choco IDs, metadata) |
| Tweak definitions | config/tweaks.json |
Registry/service/appx/script per tweak |
| Feature/Config buttons | config/feature.json |
Config-tab features, fixes, panels |
| Presets | config/preset.json |
Standard / Minimal / Advanced tweak sets |
| DNS providers | config/dns.json |
IPv4/IPv6 server pairs |
| Themes | config/themes.json |
Shared/Light/Dark color + sizing tokens |
| Install-tab nav | config/appnavigation.json |
Action buttons, package-manager radios |
| UI engine | functions/public/Invoke-WPFUIElements.ps1 |
Render JSON → WPF controls |
| Tweak engine | functions/private/Invoke-WinUtilTweaks.ps1 |
Dispatch tweak data to apply functions |
| Apply primitives | functions/private/Set-WinUtil*.ps1 |
Registry, service, DNS writes; appx removal |
| Install engine | functions/private/Install-WinUtil*.ps1 |
winget/choco install/uninstall |
| MicroWin | functions/private/Invoke-WinUtilISO*.ps1 |
Build custom Windows 11 ISO/USB |
| Tests | pester/*.Tests.ps1 |
Pester validation of configs + functions |
| Lint config | lint/PSScriptAnalyser.ps1 |
PSScriptAnalyzer settings |
| Docs generator | tools/devdocs-generator.ps1 |
Generate Hugo docs from tweaks/feature JSON |
3. The Configuration-Driven UI Engine
The heart of WinUtil is Invoke-WPFUIElements, a generic renderer that turns a JSON config object into WPF controls inside a named grid. main.ps1 calls it three times:
Invoke-WPFUIElements -configVariable $sync.configs.appnavigation -targetGridName "appscategory" -columncount 1
Invoke-WPFUIElements -configVariable $sync.configs.tweaks -targetGridName "tweakspanel" -columncount 2
Invoke-WPFUIElements -configVariable $sync.configs.feature -targetGridName "featurespanel" -columncount 2
The engine organizes entries by Panel then Category, sorts each category (checkboxes → buttons → comboboxes, then alphabetically by Content), and emits a control based on each entry's Type:
Type |
Rendered control | Behavior |
|---|---|---|
| (default / none) | CheckBox + optional (?) link |
Adds/removes from a selection list on check |
Toggle |
Colorful toggle switch | Applies/undoes a tweak immediately via Invoke-WinUtilTweaks |
Button |
Button | Runs the mapped function/script on click |
Combobox |
Label + ComboBox | e.g. the DNS selector (ComboItems split on spaces) |
RadioButton |
RadioButton (grouped by GroupName) |
e.g. WinGet/Chocolatey selector |
ToggleButton |
ToggleButton with on/off content | State-labeled buttons |
Panels use virtualizing stack panels (VirtualizingStackPanel, recycling mode) for performance with large lists. The Install tab's app area is built separately by Initialize-InstallAppArea / Initialize-InstallCategoryAppList, which also use virtualization and a WrapPanel for responsive columns.
Tip
To add a tweak, app, or feature, you edit the relevant JSON file — no engine changes are needed. The control type, category placement, tooltip (
Description), and docs link (link) all come from the JSON entry.
3.1 Button dispatch
Invoke-WPFButton resolves a clicked button in two stages:
- If the button name exists in
config/feature.jsonand has afunctionorInvokeScript, run that. - Otherwise fall back to a hard-coded
switchmapping button names to functions (WPFInstall→Invoke-WPFInstall,WPFtweaksbutton→Invoke-WPFtweaksbutton, tab buttons →Invoke-WPFTab, preset buttons →Invoke-WPFPresets, etc.).
4. System Changes — What WinUtil Actually Does
This section is the audit-focused inventory of every category of change. All changes require Administrator rights.
4.1 Tweak application engine
Invoke-WinUtilTweaks -CheckBox <name> [-undo] [-KeepServiceStartup] reads the tweak's JSON definition and applies up to four kinds of change, choosing Value/StartupType/InvokeScript normally or OriginalValue/OriginalType/UndoScript when -undo is set:
| JSON key | Apply function | Effect |
|---|---|---|
service[] |
Set-WinUtilService |
Set each service's StartupType (Manual/Disabled/Automatic/AutomaticDelayedStart) |
registry[] |
Set-WinUtilRegistry |
Create/set/remove a registry value (<RemoveEntry> deletes it) |
InvokeScript[] / UndoScript[] |
Invoke-WinUtilScript |
Run an embedded PowerShell scriptblock |
appx[] |
Remove-WinUtilAPPX |
Remove AppX/provisioned packages (apply only; not undone) |
-KeepServiceStartup (default $true) skips changing a service whose current startup type no longer matches the recorded OriginalType, so WinUtil won't override a choice you (or another tool) deliberately made.
Set-WinUtilRegistry creates the key path if missing, mounts HKU: if needed, and either sets the value with the given Type (DWord/QWord/String/Binary) or removes it when the value is the sentinel <RemoveEntry>. Exceptions (security, unauthorized, not-found) are caught and logged as warnings rather than crashing the run.
4.2 Tweak catalog (config/tweaks.json)
Tweaks are grouped into four categories. Highlights of what each Essential tweak changes (all reversible via Undo unless noted):
| Tweak | Mechanism |
|---|---|
| Activity History – Disable | Removes EnableActivityFeed/PublishUserActivities/UploadUserActivities policies |
| Telemetry – Disable | ~12 registry values + disables DiagTrack/wermgr, opts out of PowerShell telemetry, Defender sample submission |
| Location Tracking – Disable | Disables lfsvc, denies location consent, blocks sensor |
| Services – Set to Manual | Manual/Disabled on several services + tunes SvcHostSplitThresholdInKB to RAM |
| Hibernation – Disable | powercfg /hibernate off + registry (Undo re-enables) |
| ConsumerFeatures – Disable | DisableWindowsConsumerFeatures policy |
| Delivery Optimization – Disable | DODownloadMode=0 |
| Unwanted Pre-Installed Apps – Remove (DeBloat) | Removes ~19 AppX packages + uninstalls classic Teams |
| Disk Cleanup / Temporary Files | Run cleanup / clear temp |
| End Task With Right Click | Taskbar developer option |
| Restore Point – Create | Creates a System Restore point (applied first when selected) |
| Widgets / Start Menu layout / Store search / Explorer auto-discovery / WPBT | Various registry/appx/script changes |
Advanced ("z__Advanced Tweaks – CAUTION") tweaks include Brave/Edge debloat, Remove Edge, Remove OneDrive, Remove File Explorer Home, Visual Effects → performance, Xbox removal, IPv6 preferences/disable, Teredo disable, background apps disable, fullscreen optimizations disable, Razer auto-install block, notifications disable, Adobe URL block, classic right-click menu, UTC clock, storage sense disable, Windows AI disable, O&O ShutUp10++, and the DNS selector.
Caution
Advanced tweaks make aggressive changes (removing Edge/OneDrive, disabling IPv6, blocking domains). Several download external helper tools at runtime (e.g. Revert Start Menu downloads ViVeTool; O&O ShutUp10++ downloads OOSU10.exe). Review the
InvokeScriptbody inconfig/tweaks.jsonbefore deploying to managed fleets.
4.3 Customize Preferences (live toggles)
Toggle-type entries apply immediately on flip (and reverse on un-flip) through the same Invoke-WinUtilTweaks path. Examples: Dark mode, show file extensions/hidden files, taskbar alignment/search/task-view icons, Bing search in Start, Start recommendations, battery percentage, mouse acceleration, sticky keys, NumLock on startup, Game Mode, long paths, verbose logon, verbose BSoD, new Outlook, scrollbars, S0/S3 sleep, settings home page, logon acrylic blur. Their on/off state is read at render time via Get-WinUtilToggleStatus, which compares the live registry against the JSON-defined value.
4.4 Config-tab features & fixes (config/feature.json)
| Entry | Function / mechanism |
|---|---|
| .NET Framework / Hyper-V / WSL / NFS / Sandbox | Enable-WindowsOptionalFeature via Invoke-WinUtilFeatureInstall (+ NFS InvokeScript for anonymous config) |
| Registry Backup / Legacy F8 Recovery | bcdedit / scheduled-task InvokeScript |
| Windows Update – Reset | Invoke-WPFFixesUpdate (stop services, clear caches, re-register 36 DLLs, reset policies, restart services) |
| Network – Reset | Invoke-WPFFixesNetwork (netsh winsock reset, netsh int ip reset) |
| System Corruption Scan | Invoke-WPFSystemRepair (chkdsk /scan, sfc /scannow, DISM /RestoreHealth) |
| WinGet – Reinstall | Invoke-WPFFixesWinget → Install-WinUtilWinget |
| NTP Server – Enable | w32tm repointed at pool.ntp.org |
| AutoLogon | Downloads Sysinternals Autologon.exe |
| Legacy Windows Panels | Launch control, compmgmt.msc, ncpa.cpl, powercfg.cpl, intl.cpl, rstrui.exe, mmsys.cpl, sysdm.cpl, timedate.cpl, printers shell |
| CTT PowerShell Profile – Install/Remove | Invoke-WinUtilInstallPSProfile / ...Uninstall... (backs up existing $Profile) |
| OpenSSH Server – Enable | Invoke-WinUtilSSHServer |
4.5 Windows Update policies (Updates tab)
| Function | Behavior |
|---|---|
Invoke-WPFUpdatesdefault |
Removes WinUtil's WU policy keys, restores BITS/wuauserv/UsoSvc/WaaSMedicSvc startup, re-enables WU scheduled tasks, resets local policy. |
Invoke-WPFUpdatessecurity |
Defers feature updates 365 days, quality updates 4 days; sets BranchReadinessLevel=20; blocks driver offering; disables auto-restart with logged-on users. |
Invoke-WPFUpdatesdisable |
NoAutoUpdate=1, disables BITS/wuauserv/UsoSvc, clears SoftwareDistribution, disables WU scheduled tasks. Blocks security updates. |
4.6 Application install/uninstall
Invoke-WPFInstall / Invoke-WPFUnInstall resolve selected app keys to applicationsHashtable entries, sort them by manager via Get-WinUtilSelectedPackages (Chocolatey entries marked na fall back to WinGet), then run inside a runspace:
- WinGet path:
Install-WinUtilWinget(ensures WinGet present) →Install-WinUtilProgramWinget -Action Install/Uninstall. - Chocolatey path:
Install-WinUtilChoco→Install-WinUtilProgramChoco -Action Install/Uninstall.
Invoke-WPFInstallUpgrade upgrades everything: winget upgrade --all --include-unknown --silent --accept-source-agreements --accept-package-agreements (or choco upgrade all -y). Invoke-WPFGetInstalled queries the system via Invoke-WinUtilCurrentSystem and ticks matching boxes.
4.7 DNS changes
Set-WinUtilDNS -DNSProvider <name> enumerates adapters in the Up state and calls Set-DnsClientServerAddress for both IPv4 and IPv6 using the pair from config/dns.json. Default is a no-op; DHCP resets to automatic. Providers: Google, Cloudflare (+ Malware / Malware+Adult), OpenDNS, Quad9, AdGuard (ads/trackers, and +malware+adult).
4.8 MicroWin (custom ISO builder)
Invoke-WinUtilISO* functions build a debloated Windows 11 image offline:
- Mount/verify a Windows 11 ISO; operate on
install.wim. Invoke-WinUtilISOScriptremoves AppX bloatware and OneDrive, optionally injects the running system's drivers intoinstall.wimandboot.wim(index 2), applies offline registry tweaks (hardware-requirement bypass, privacy, OOBE, telemetry, update suppression), and removes CEIP/WU scheduled-task definitions.- Optionally writes
tools/autounattend.xmlto the ISO root for unattended OOBE; embeds setup scripts underC:\Windows\Setup\Scripts\so they survive Setup stripping unknown-namespace XML. - Export to a new ISO or write directly to USB.
Important
MicroWin operates only on the mounted image, never the running OS. It requires substantial free disk space; driver injection significantly increases time and size. Known open issues include product-key verification failures on some debloated images (#4145, #4616).
5. Security Analysis
| Area | Assessment |
|---|---|
| Elevation | Requires Administrator and self-elevates via Start-Process … -Verb RunAs. By design — every operation is system-level. |
| Language mode | Refuses to run outside FullLanguage mode, which is also the relevant control for AppLocker/Constrained-Language-locked environments. |
| Remote code execution | The standard entry point pipes a remotely fetched script into iex. Trust derives from HTTPS + the official GitHub release. Several tweaks/features also download external EXEs at runtime (Autologon, OOSU10, ViVeTool). |
| Network egress | App installs hit WinGet/Choco sources; specific tweaks/fixes fetch helper tools from sysinternals, oo-software.com, GitHub. |
| Data handling | WinUtil itself transmits no user data. The Sponsors panel scrapes the public GitHub sponsors page. |
| Destructive operations | Remove Edge/OneDrive, AppX removal, SoftwareDistribution deletion, registry deletes, DISM/SFC. Mitigated by Undo scripts and the optional Restore Point tweak. |
| Error handling | Apply primitives wrap operations in typed try/catch and log warnings; a single failed tweak does not abort the batch. |
| Logging | Full session transcript at %LocalAppData%\winutil\logs\winutil_<timestamp>.log. |
Warning
For managed/enterprise fleets, treat the
irm … | iexpattern as remote code execution. Pin to a vettedwinutil.ps1release, review the embeddedconfig/tweaks.jsonandconfig/feature.jsonInvokeScriptbodies, and prefer the-Configexported-profile path over ad-hoc GUI use so changes are reproducible and auditable.
Caution
Disable Updates removes security patching and clears the update cache. Remove Edge/OneDrive and AppX removals are difficult to fully reverse. Always validate on a test machine and keep a restore point.
6. Operations
6.1 Running
| Mode | Command |
|---|---|
| Stable GUI | irm https://christitus.com/win | iex |
| Dev GUI | irm https://christitus.com/windev | iex |
| Direct from GitHub | irm https://github.com/ChrisTitusTech/winutil/releases/latest/download/winutil.ps1 | iex |
| Preset, no GUI | & ([ScriptBlock]::Create((irm https://christitus.com/win))) -Preset Standard |
| Config, no GUI | & ([ScriptBlock]::Create((irm https://christitus.com/win))) -Config '<file-or-url>' -Noui |
6.2 Command-line parameters (scripts/start.ps1)
| Parameter | Type | Effect |
|---|---|---|
-Config <path|url> |
string | Import an exported selection JSON and apply it. |
-Preset <Standard|Minimal|Advanced> |
validated string | Apply a built-in preset's tweaks, then exit. |
-Noui |
switch | Headless run; requires -Config. |
-Offline |
switch | Disable the Install tab and install buttons. |
6.3 Unattended deployment
- Configure selections in the GUI on a reference machine.
- Settings → Export → save
winutil.json. WinUtil copies a run command to the clipboard. - Deploy with
-Config(+-Nouifor silent).Invoke-WinUtilAutoRunthen applies tweaks, features, and installs in order, busy-waiting between phases.
6.4 Config file format
Exported configs are a flat JSON array of selected control keys (apps, tweaks, toggles, features), e.g. ["WPFInstallchrome","WPFTweaksTelemetry","WPFToggleDarkMode"]. Update-WinUtilSelections re-checks the matching boxes; Reset-WPFCheckBoxes restores UI state without retriggering tweak writes (guarded by $sync.ImportInProgress).
6.5 Logs & working files
| Path | Purpose |
|---|---|
%LocalAppData%\winutil\ |
Working directory (created at startup) |
%LocalAppData%\winutil\logs\winutil_<yyyy-MM-dd_HH-mm-ss>.log |
Start-Transcript session log |
6.6 Building from source
# From the repo root, build winutil.ps1 and launch it
.\Compile.ps1 -Run
# Run the latest pre-release build
.\windev.ps1
7. Repository Structure
winutil/
Compile.ps1 Build: bundle config+xaml+functions → winutil.ps1
windev.ps1 Run latest pre-release build
sign.bat Code-signing helper
config/ SOURCE OF TRUTH (JSON)
applications.json App catalog (winget/choco IDs, metadata, foss flag)
tweaks.json Tweak definitions (registry/service/appx/scripts)
feature.json Config-tab features, fixes, panels
preset.json Standard / Minimal / Advanced
dns.json DNS provider IPv4/IPv6 pairs
themes.json Shared/Light/Dark tokens
appnavigation.json Install-tab actions + package-manager radios
functions/
private/ Engine internals (Set-WinUtil*, Invoke-WinUtil*, ISO)
public/ UI-facing handlers (Invoke-WPF*)
scripts/
start.ps1 Entry preamble (params, elevation, $sync, logging)
main.ps1 GUI bootstrap + event wiring
xaml/inputXML.xaml WPF layout + styles
pester/ *.Tests.ps1 (config + function tests)
lint/PSScriptAnalyser.ps1 PSScriptAnalyzer settings
tools/
autounattend.xml MicroWin OOBE answer file
devdocs-generator.ps1 Generate Hugo docs from JSON
docs/ Hugo (hextra theme) documentation site
Note
docs/go.mod/go.sumbelong to the documentation website (Hugo + thehextratheme), not to WinUtil itself. The tool has no Go, Node, or compiled dependencies — it runs on in-box PowerShell and WPF.
7.1 Naming conventions
WPFInstall<app>— app checkbox (app key fromapplications.json, prefixed at build time).WPFTweaks<name>— tweak checkbox;WPFToggle<name>— preference toggle.WPFFeature*/WPFFix*/WPFPanel*— Config-tab entries.Invoke-WPF*(public) — UI event handlers;Invoke-WinUtil*/Set-WinUtil*/Get-WinUtil*(private) — engine.
8. Code Quality & Conventions
- Approved verbs & PSScriptAnalyzer. Functions follow PowerShell verb-noun naming;
lint/PSScriptAnalyser.ps1excludes onlyPSAvoidUsingWriteHost(Write-Host is used intentionally for console feedback). - Comment-based help. Most functions carry
.SYNOPSIS/.PARAMETER/.EXAMPLEblocks. - PowerShell 5.1 compatibility. Search/filter code deliberately avoids ternaries and advanced syntax, with multi-level
$syncfallback, to run on stock Windows PowerShell as well as PowerShell 7. - Defensive UI code. High-frequency handlers (e.g.
Find-AppsByNameOrDescription,Find-TweaksByNameOrDescription) null-check aggressively and swallow exceptions silently to avoid crashing the WPF thread on every keystroke. - Data over code. New apps/tweaks/features are JSON edits; the engine is generic.
- Testing.
pester/configs.Tests.ps1andpester/functions.Tests.ps1validate config integrity and function behavior.
9. Potential Improvements
These are observations from the current source, not defects:
Invoke-WPFUIElementsis monolithic. An inline// TODOnotes it should be wrapped in a runspace to render all panels in parallel; it also mixes layout, event wiring, and control creation. Extracting per-control-type builders would aid testability.Initialize-WPFUIpartially stubbed. Theappscategorybranch contains commented-out future work, anddefaultreturns "not yet implemented" — indicating an in-progress migration of sidebar generation into this function.- Import legacy-format handling is unfinished.
Invoke-WPFImpexhas aTODOabout detecting and flattening the old config JSON shape. Invoke-WPFGetInstalledhas aTODOto support Chocolatey as the detection engine (currently winget-centric).- Driver/ISO operations are long and serial. MicroWin steps could surface more granular progress and resumability (several open issues relate to mid-build failures).
- External-download tweaks (ViVeTool, OOSU10, Autologon) fetch from third-party URLs at runtime; pinning/hash-verification would harden them for enterprise use.
- FAQ/config drift. The shipped FAQ references a "Desktop" preset and a "Clear Tweaks Selection" wording that don't match
preset.json(which defines only Standard/Minimal/Advanced) — keep user docs synchronized with the JSON ground truth.
10. Contributing
- Add an app: add an entry to
config/applications.jsonwithcategory,content,description,link,winget,choco(usenaif none), andfoss. The build prefixes the key withWPFInstall. - Add a tweak: add to
config/tweaks.jsonwithContent,Description,category,panel, and any ofregistry[]/service[]/appx[]/InvokeScript[]/UndoScript[]. ProvideOriginalValue/OriginalType(orUndoScript) so it can be reverted. - Add a feature/fix: add to
config/feature.json; either pointfunctionat a public function or supplyInvokeScript[]. Map buttons inInvoke-WPFButton.ps1if not function-driven. - Regenerate docs:
tools/devdocs-generator.ps1emits Hugo pages fromtweaks.json/feature.json. - Test & lint: run the Pester suites in
pester/and PSScriptAnalyzer withlint/PSScriptAnalyser.ps1before submitting.
See the CONTRIBUTING guide.
11. Quick Reference
Engine functions
| Function | Role |
|---|---|
Invoke-WPFUIElements |
Render a JSON config into WPF controls |
Invoke-WPFButton |
Dispatch a clicked button to its function/script |
Invoke-WinUtilTweaks |
Apply/undo a tweak (service/registry/script/appx) |
Set-WinUtilRegistry |
Create/set/remove a registry value |
Set-WinUtilService |
Set a service startup type |
Set-WinUtilDNS |
Set adapter DNS from dns.json |
Remove-WinUtilAPPX |
Remove AppX/provisioned packages |
Invoke-WPFInstall / Invoke-WPFUnInstall |
Install/uninstall selected apps |
Invoke-WPFRunspace / Invoke-WPFUIThread |
Background work / UI-thread marshaling |
Invoke-WinUtilAutoRun |
Headless apply (tweaks → features → installs) |
Invoke-WinUtilISOScript |
Apply MicroWin modifications to a mounted WIM |
Config files
| File | Defines |
|---|---|
applications.json |
Installable apps |
tweaks.json |
Tweaks & toggles |
feature.json |
Config-tab features/fixes/panels |
preset.json |
Standard / Minimal / Advanced |
dns.json |
DNS providers |
themes.json |
Theme tokens |
appnavigation.json |
Install-tab actions |
Key paths & links
| Item | Value |
|---|---|
| Working dir | %LocalAppData%\winutil\ |
| Logs | %LocalAppData%\winutil\logs\winutil_<timestamp>.log |
| Build script | Compile.ps1 (-Run to build + launch) |
| Source | https://github.com/ChrisTitusTech/winutil |
| Docs | https://winutil.christitus.com/ |