Skip to content

Architecture & Design

WinUtil is a PowerShell-based Windows utility with a WPF (Windows Presentation Foundation) GUI. This document explains the architecture, code structure, and how different components work together.

┌─────────────────────────────────────────────────────┐
│ Winutil GUI │
│ (WPF XAML Interface) │
└──────────────────┬──────────────────────────────────┘
┌─────────┴─────────┐
│ │
┌────────▼──────┐ ┌───────▼────────┐
│ Public APIs │ │ Private APIs │
│ (User-facing)│ │ (Internal) │
└───────┬───────┘ └───────┬────────┘
│ │
└────────┬──────────┘
┌────────────▼────────────┐
│ Configuration Files │
│ (JSON definitions) │
└────────────┬────────────┘
┌────────────▼────────────┐
│ External Tools │
│ (WinGet, Chocolatey) │
└─────────────────────────┘
winutil/
├── Compile.ps1 # Build script that combines all files
├── winutil.ps1 # Compiled output (generated)
├── scripts/
│ ├── main.ps1 # Entry point and GUI initialization
│ └── start.ps1 # Startup logic
├── functions/
│ ├── private/ # Internal helper functions
│ │ ├── Get-WinUtilVariables.ps1
│ │ ├── Install-WinUtilWinget.ps1
│ │ └── ...
│ ├── public/ # User-facing functions
│ │ ├── Initialize-WPFUI.ps1
│ │ └── ...
├── config/ # JSON configuration files
│ ├── applications.json # Application definitions
│ ├── tweaks.json # Tweak definitions
│ ├── feature.json # Windows feature definitions
│ └── preset.json # Preset configurations
├── xaml/
│ └── inputXML.xaml # GUI layout definition
└── docs/ # Documentation

Purpose: Combines all separate script files into a single winutil.ps1 for distribution.

Process:

  1. Reads all function files from /functions/
  2. Includes configuration JSON files
  3. Embeds XAML GUI definition
  4. Combines into a single script
  5. Outputs winutil.ps1

Why: Makes distribution easier (single file) and improves load time.

Purpose: Entry point that initializes the GUI and event system.

Responsibilities:

  • Load XAML and create WPF window
  • Initialize form elements
  • Set up event handlers
  • Load configurations
  • Display the GUI

Purpose: User-facing functions that implement main features.

Key Functions:

  • Initialize-WPFUI.ps1: Sets up the GUI
  • Invoke-WPFTweak*: Applies system tweaks
  • Invoke-WPFFeature*: Enables Windows features
  • Install-WinUtilProgram*: Installs applications

Naming Convention: Functions start with WPF or Winutil to be loaded into the runspace.

Purpose: Internal helper functions not directly called by users.

Key Functions:

  • Get-WinUtilVariables.ps1: Retrieves UI element references
  • Install-WinUtilWinget.ps1: Ensures WinGet is installed
  • Get-WinUtilCheckBoxes.ps1: Gets checkbox states
  • Invoke-WinUtilCurrentSystem.ps1: Gets system information

Purpose: Define available applications, tweaks, and features declaratively.

Files:

  • applications.json: Application definitions with WinGet/Choco IDs
  • tweaks.json: Registry tweaks and their undo actions
  • feature.json: Windows features that can be enabled/disabled
  • preset.json: Predefined tweak combinations
  • dns.json: DNS provider configurations

Purpose: WPF GUI layout and design.

Structure:

  • Buttons with event handlers
  • TextBoxes for input
  • CheckBoxes for options
  • ListBoxes for selections

The Win11 Creator is a specialized subsystem within Winutil that creates customized Windows 11 ISOs. It operates independently from the main package installation and tweak system.

Core Functions (functions/private/):

  • Invoke-WinUtilISO.ps1: Main orchestrator containing all Win11 Creator functions

    • Invoke-WinUtilISOBrowse: ISO file selection dialog
    • Invoke-WinUtilISOMountAndVerify: Validates and mounts ISO, verifies it’s official Windows 11
    • Invoke-WinUtilISOModify: Launches modification in background runspace
    • Invoke-WinUtilISOExport: Handles ISO and USB export
    • Invoke-WinUtilISOCheckExistingWork: Recovers incomplete work sessions
    • Invoke-WinUtilISOCleanAndReset: Cleans up temp directories and resets UI
  • Invoke-WinUtilISOScript.ps1: Applies modifications to mounted install.wim

    • Removes provisioned AppX packages (40+ bloatware apps)
    • Injects drivers (optional) from the current system
    • Removes OneDrive setup files
    • Applies offline registry tweaks (hardware bypass, privacy, telemetry, OOBE)
    • Deletes telemetry scheduled task definitions
    • Pre-stages setup scripts from autounattend.xml
    • Removes unused Windows editions
    • Cleans component store via DISM
User selects official Windows 11 ISO
Invoke-WinUtilISOBrowse → OpenFileDialog, validates file size
Invoke-WinUtilISOMountAndVerify
├─ Mount ISO via Mount-DiskImage
├─ Verify install.wim or install.esd exists
├─ Check for "Windows 11" in image metadata
├─ Extract available editions (Home, Pro, Enterprise, etc.)
└─ Store ISO path, drive letter, WIM path, image info in $sync
User optionally enables the Driver Injection checkbox
Invoke-WinUtilISOModify (runs in background runspace)
├─ Create work directory: ~WinUtil_Win11ISO_[timestamp]
├─ Copy ISO contents to disk (~5-6 GB)
├─ Mount install.wim at selected edition/index
├─ Invoke-WinUtilISOScript:
│ ├─ Remove 40+ bloat AppX packages
│ ├─ Export and inject drivers (if enabled)
│ ├─ Remove OneDrive setup
│ ├─ Load offline registry hives
│ ├─ Apply 50+ registry tweaks (hardware bypass, privacy, telemetry, OOBE, etc.)
│ ├─ Delete telemetry scheduled task files
│ ├─ Pre-stage setup scripts from autounattend.xml to C:\Windows\Setup\Scripts\
│ └─ Unload registry hives
├─ DISM /Cleanup-Image /StartComponentCleanup /ResetBase (saves 300-800 MB)
├─ Dismount and save the modified install.wim (~10+ minutes, slowest step)
├─ Export selected edition only (removes all other editions, saves 1-2 GB each)
├─ Dismount source ISO
└─ Report completion, enable export options
Invoke-WinUtilISOExport (user chooses output)
├─ Option 1: Save as ISO
│ ├─ Build bootable ISO via oscdimg.exe (BIOS/UEFI dual-boot)
│ └─ Output: Win11_Modified_[date].iso (2.5-3.5 GB)
└─ Option 2: Write to USB
├─ Format USB as GPT
├─ Create 512 MB EFI partition
├─ Copy modified ISO contents
└─ Output: Bootable USB (minimum 8 GB)
Invoke-WinUtilISOCleanAndReset (optional)
└─ Delete temp working directory (~10-15 GB)
└─ Reset UI to initial state

ISO Validation:

  • Only accepts official Microsoft Windows 11 ISOs
  • Validates presence of install.wim or install.esd
  • Checks image metadata for “Windows 11” string
  • Rejects custom, modified, or non-Windows 11 ISOs

Work Session Recovery:

  • Auto-detects incomplete work from previous sessions
  • Allows resuming Step 4 (export) without re-running Steps 1-3
  • Prevents redundant modifications

Modification Safety:

  • All registry changes are documented in a script (reversible)
  • Original ISO never modified; only working copy
  • Logged to WinUtil_Win11ISO.log for debugging
  • DISM handles image dismount with automatic cleanup on error

The Invoke-WinUtilISOScript function applies 50+ offline registry tweaks:

Hardware Bypass:

  • TPM 2.0 check bypass
  • Secure Boot requirement bypass
  • CPU compatibility bypass
  • RAM requirement bypass
  • Storage check bypass

Privacy & Telemetry:

  • Disable advertising ID
  • Disable tailored experiences
  • Disable input personalization
  • Disable speech online privacy
  • Disable cloud content suggestions
  • Disable app suggestion subscriptions
  • Remove CEIP, Appraiser, WaaSMedic, etc.

OOBE & Setup:

  • Enable local account setup
  • Skip Microsoft account requirement
  • Dark mode by default
  • Empty taskbar and Start Menu

Post-Setup Installations:

  • Prevent DevHome auto-installation
  • Prevent new Outlook Mail app installation
  • Prevent Teams auto-installation

System Features:

  • Disable BitLocker and device encryption
  • Disable Chat icon from the Taskbar
  • Disable OneDrive folder backup
  • Disable Copilot
  • Disable Windows Update during OOBE (re-enabled at first login)

Optional Enhancement: When enabled, exports all drivers from the running system and injects them into both:

  • install.wim (main OS image)
  • boot.wim index 2 (Windows Setup PE environment)

Use Case: Enables offline installation on systems with missing drivers.

  • Temporary working directory: ~10-15 GB
  • Original ISO: 4-6 GB
  • Modified ISO: 2.5-3.5 GB
  • Total needed: ~25 GB for safe operation
User clicks "Install"
Get-WinUtilCheckBoxes → Retrieves selected apps
For each selected app:
Check if WinGet/Choco is installed
Install-WinUtilWinget/Choco (if needed)
Install-WinUtilProgramWinget/Choco → Install app
Update UI with progress
Display completion message
User selects tweaks and clicks "Run Tweaks"
Get-WinUtilCheckBoxes → Get selected tweaks
For each selected tweak:
Load tweak definition from tweaks.json
Invoke-WPFTweak → Apply registry/service changes
Log changes
Store original values (for undo)
Update UI
Display completion
User selects tweaks and clicks "Undo"
Get-WinUtilCheckBoxes → Get selected tweaks
For each tweak:
Retrieve "OriginalState" from tweak definition
Invoke-WPFUndoTweak → Restore original values
Remove from the applied tweaks log
Update UI
config/applications.json
{
"WPFInstall<AppName>": {
"category": "Browsers",
"choco": "googlechrome",
"content": "Google Chrome",
"description": "Google Chrome browser",
"link": "https://chrome.google.com",
"winget": "Google.Chrome"
}
}

Fields:

  • category: Which section in the Install tab
  • content: Display name in GUI
  • description: Tooltip/description text
  • winget: WinGet package ID
  • choco: Chocolatey package name
  • link: Official website
config/tweaks.json
{
"WPFTweaksTelemetry": {
"Content": "Disable Telemetry",
"Description": "Disables Microsoft Telemetry",
"category": "Essential Tweaks",
"panel": "1",
"registry": [
{
"Path": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\DataCollection",
"Name": "AllowTelemetry",
"Type": "DWord",
"Value": "0",
"OriginalValue": "1"
}
]
}
}

Fields:

  • Content: Display name
  • Description: What it does
  • category: Essential/Advanced/Customize
  • registry: Registry changes to make
  • service: Services to change
  • OriginalValue/State: For undo functionality

Winutil uses PowerShell runspaces for the GUI to remain responsive:

Terminal window
# Create runspace
$sync.runspace = [runspacefactory]::CreateRunspace()
$sync.runspace.Open()
$sync.runspace.SessionStateProxy.SetVariable("sync", $sync)
# Run code in background
$powershell = [powershell]::Create().AddScript($scriptblock)
$powershell.Runspace = $sync.runspace
$handle = $powershell.BeginInvoke()

Why: Prevents UI freezing during long-running operations.

Events are wired up via XAML element names:

Terminal window
# Get all named elements
$sync.keys | ForEach-Object {
if($sync.$_.GetType().Name -eq "Button") {
$sync.$_.Add_Click({
$button = $sync.$($args[0].Name)
& "Invoke-$($args[0].Name)"
})
}
}

Convention: Button named WPFInstallButton calls function Invoke-WPFInstallButton.

Terminal window
# Check if installed
if (!(Get-Command winget -ErrorAction SilentlyContinue)) {
Install-WinUtilWinget
}
# Install package
winget install --id $app.winget --silent --accept-source-agreements
Terminal window
# Check if installed
if (!(Get-Command choco -ErrorAction SilentlyContinue)) {
Install-WinUtilChoco
}
# Install package
choco install $app.choco -y

Winutil uses PowerShell error handling:

Terminal window
try {
# Attempt operation
Invoke-SomeOperation
}
catch {
Write-Host "Error: $_" -ForegroundColor Red
# Log error
Add-Content -Path $logfile -Value "ERROR: $_"
}

Logging: Errors and operations are logged for debugging.

At startup, Winutil loads all configurations:

Terminal window
# Load JSON configs
$sync.configs = @{}
$sync.configs.applications = Get-Content "config/applications.json" | ConvertFrom-Json
$sync.configs.tweaks = Get-Content "config/tweaks.json" | ConvertFrom-Json
$sync.configs.features = Get-Content "config/feature.json" | ConvertFrom-Json

Sync Hash: $sync hashtable shares state across runspaces.

UI updates must happen on the UI thread:

Terminal window
$sync.form.Dispatcher.Invoke([action]{
$sync.WPFStatusLabel.Content = "Installing..."
}, "Normal")

Why: WPF requires UI updates on the main thread.

  1. Edit config/applications.json:
config/applications.json
{
"WPFInstallNewApp": {
"category": "Utilities",
"content": "New App",
"description": "Description of new app",
"winget": "Publisher.AppName",
"choco": "appname"
}
}
  1. Recompile: .\Compile.ps1
  2. The app appears automatically in the Install tab
  1. Edit config/tweaks.json:
config/tweaks.json
{
"WPFTweaksNewTweak": {
"Content": "New Tweak",
"Description": "What it does",
"category": "Essential Tweaks",
"registry": [
{
"Path": "HKLM:\\Path\\To\\Key",
"Name": "ValueName",
"Type": "DWord",
"Value": "1",
"OriginalValue": "0"
}
]
}
}
  1. Recompile: .\Compile.ps1
  2. Tweak appears in the Tweaks tab
  1. Create file in functions/public/ or functions/private/:
functions/public/Invoke-WPFNewFeature.ps1
function Invoke-WPFNewFeature {
<#
.SYNOPSIS
Does something new
#>
# Implementation
}
  1. File naming must include “WPF” or “Winutil” to load
  2. Recompile: .\Compile.ps1
Terminal window
# Compile and run with -run flag
.\Compile.ps1 -run

Tests are in /pester/:

  • configs.Tests.ps1: Validates JSON configurations
  • functions.Tests.ps1: Tests PowerShell functions

Run tests:

Terminal window
Install-Module -Name Pester -RequiredVersion 5.8.0 -Scope CurrentUser -Force -SkipPublisherCheck
Import-Module Pester -RequiredVersion 5.8.0 -Force
Invoke-Pester -Path 'pester/*.Tests.ps1' -Output Detailed -CI
Terminal window
.\Compile.ps1

Outputs winutil.ps1 in the root directory.

  1. Tag release in Git
  2. GitHub Actions builds and uploads winutil.ps1
  3. Release appears on GitHub Releases
  4. Users download via irm christitus.com/win

Required:

  • PowerShell 5.1+
  • .NET Framework 4.5+
  • Windows 11

Optional (auto-installed):

  • WinGet (Windows Package Manager)
  • Chocolatey

Optimization Strategies:

  • Lazy-load configurations (only when needed)
  • Use runspaces for long operations
  • Cache expensive lookups
  • Minimize registry reads/writes
  • Batch operations when possible

Safety Measures:

  • All operations logged
  • Registry backups for undo
  • No credential storage
  • Open source (auditable)
  • Digitally signed (future)

Code Standards:

  • Use proper PowerShell cmdlet naming (Verb-Noun)
  • Include comment-based help
  • Follow existing code style
  • Test thoroughly before PR
  • Document significant changes

File Naming:

  • Public functions: Invoke-WPF*.ps1 or Invoke-Winutil*.ps1
  • Private functions: Get-WinUtil*.ps1 or verb-WinUtil*.ps1`
  • Must include “WPF” or “Winutil” to load

Roadmap Considerations:

  • Plugin system for community extensions
  • Config import/export
  • Cloud sync for configurations
  • Enhanced logging dashboard
  • Modular compilation (choose features)

Last Updated: January 2026 Maintainers: Chris Titus Tech and contributors