Architecture & Design
Overview
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.
High-Level Architecture
┌─────────────────────────────────────────────────────┐
│ Winutil GUI │
│ (WPF XAML Interface) │
└──────────────────┬──────────────────────────────────┘
│
┌─────────┴─────────┐
│ │
┌────────▼──────┐ ┌───────▼────────┐
│ Public APIs │ │ Private APIs │
│ (User-facing)│ │ (Internal) │
└───────┬───────┘ └───────┬────────┘
│ │
└────────┬──────────┘
│
┌────────────▼────────────┐
│ Configuration Files │
│ (JSON definitions) │
└────────────┬────────────┘
│
┌────────────▼────────────┐
│ External Tools │
│ (WinGet, Chocolatey) │
└─────────────────────────┘Project Structure
Directory Layout
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
│ │ └── ...
│ └── microwin/ # MicroWin specific functions
│ ├── Invoke-Microwin.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/ # DocumentationKey Components
1. Compile.ps1
Purpose: Combines all separate script files into a single winutil.ps1 for distribution.
Process:
- Reads all function files from
/functions/ - Includes configuration JSON files
- Embeds XAML GUI definition
- Combines into single script
- Outputs
winutil.ps1
Why: Makes distribution easier (single file) and improves load time.
2. scripts/main.ps1
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
3. functions/public/
Purpose: User-facing functions that implement main features.
Key Functions:
Initialize-WPFUI.ps1: Sets up the GUIInvoke-WPFTweak*: Applies system tweaksInvoke-WPFFeature*: Enables Windows featuresInstall-WinUtilProgram*: Installs applications
Naming Convention: Functions start with WPF or Winutil to be loaded into the runspace.
4. functions/private/
Purpose: Internal helper functions not directly called by users.
Key Functions:
Get-WinUtilVariables.ps1: Retrieves UI element referencesInstall-WinUtilWinget.ps1: Ensures WinGet is installedGet-WinUtilCheckBoxes.ps1: Gets checkbox statesInvoke-WinUtilCurrentSystem.ps1: Gets system information
5. config/*.json
Purpose: Define available applications, tweaks, and features declaratively.
Files:
applications.json: Application definitions with WinGet/Choco IDstweaks.json: Registry tweaks and their undo actionsfeature.json: Windows features that can be enabled/disabledpreset.json: Predefined tweak combinationsdns.json: DNS provider configurations
6. xaml/inputXML.xaml
Purpose: WPF GUI layout and design.
Structure:
- TabControl for main tabs (Install, Tweaks, Config, MicroWin)
- Buttons with event handlers
- TextBoxes for input
- CheckBoxes for options
- ListBoxes for selections
Data Flow
Application Installation Flow
User clicks "Install"
↓
Get-WinUtilCheckBoxes → Retrieves selected apps
↓
For each selected app:
↓
Check if WinGet/Choco installed
↓
Install-WinUtilWinget/Choco (if needed)
↓
Install-WinUtilProgramWinget/Choco → Install app
↓
Update UI with progress
↓
Display completion messageTweak Application Flow
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 completionUndo Tweak Flow
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 applied tweaks log
↓
Update UIConfiguration File Format
applications.json Structure
{
"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 tabcontent: Display name in GUIdescription: Tooltip/description textwinget: WinGet package IDchoco: Chocolatey package namelink: Official website
tweaks.json Structure
{
"WPFTweaksTelemetry": {
"Content": "Disable Telemetry",
"Description": "Disables Microsoft Telemetry",
"category": "Essential Tweaks",
"panel": "1",
"Order": "a003_",
"registry": [
{
"Path": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\DataCollection",
"Name": "AllowTelemetry",
"Type": "DWord",
"Value": "0",
"OriginalValue": "1"
}
],
"ScheduledTask": [
{
"Name": "Microsoft\\Windows\\Autochk\\Proxy",
"State": "Disabled",
"OriginalState": "Enabled"
}
]
}
}Fields:
Content: Display nameDescription: What it doescategory: Essential/Advanced/Customizeregistry: Registry changes to makeScheduledTask: Scheduled tasks to modifyservice: Services to changeOriginalValue/State: For undo functionality
PowerShell Runspace
Winutil uses PowerShell runspaces for the GUI to remain responsive:
# 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.
WPF Event Handling
Events are wired up via XAML element names:
# 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.
Package Manager Integration
WinGet Integration
# Check if installed
if (!(Get-Command winget -ErrorAction SilentlyContinue)) {
Install-WinUtilWinget
}
# Install package
winget install --id $app.winget --silent --accept-source-agreementsChocolatey Integration
# Check if installed
if (!(Get-Command choco -ErrorAction SilentlyContinue)) {
Install-WinUtilChoco
}
# Install package
choco install $app.choco -yMicroWin Architecture
MicroWin operates in phases:
- Mount ISO: Extract Windows image
- Modify Image: Remove components, apply tweaks
- Inject Drivers: Add custom drivers (optional)
- Create Unattend: Generate automated install configuration
- Rebuild ISO: Package modified image into new ISO
Key Files:
Invoke-Microwin.ps1: Main orchestrationMicrowin-RemovePackages.ps1: Remove Windows packagesMicrowin-RemoveFeatures.ps1: Disable featuresMicrowin-NewUnattend.ps1: Create unattend.xml
Error Handling
Winutil uses PowerShell error handling:
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.
Configuration Loading
At startup, Winutil loads all configurations:
# 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-JsonSync Hash: $sync hashtable shares state across runspaces.
UI Update Pattern
UI updates must happen on the UI thread:
$sync.form.Dispatcher.Invoke([action]{
$sync.WPFStatusLabel.Content = "Installing..."
}, "Normal")Why: WPF requires UI updates on the main thread.
Adding New Features
Adding a New Application
- Edit
config/applications.json:
{
"WPFInstallNewApp": {
"category": "Utilities",
"content": "New App",
"description": "Description of new app",
"winget": "Publisher.AppName",
"choco": "appname"
}
}- Recompile:
.\Compile.ps1 - The app appears automatically in Install tab
Adding a New Tweak
- Edit
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"
}
]
}
}- Recompile:
.\Compile.ps1 - Tweak appears in Tweaks tab
Adding a New Function
- Create file in
functions/public/orfunctions/private/:
# functions/public/Invoke-WPFNewFeature.ps1
function Invoke-WPFNewFeature {
<#
.SYNOPSIS
Does something new
#>
# Implementation
}- File naming must include “WPF” or “Winutil” to load
- Recompile:
.\Compile.ps1
Testing
Manual Testing
# Compile and run with -run flag
.\Compile.ps1 -runAutomated Tests
Tests are in /pester/:
configs.Tests.ps1: Validates JSON configurationsfunctions.Tests.ps1: Tests PowerShell functions
Run tests:
Invoke-PesterBuild Process
Development Build
.\Compile.ps1Outputs winutil.ps1 in the root directory.
Production Release
- Tag release in Git
- GitHub Actions builds and uploads
winutil.ps1 - Release appears on GitHub Releases
- Users download via
irm christitus.com/win
Dependencies
Required:
- PowerShell 5.1+
- .NET Framework 4.5+
- Windows 10 1809+
Optional (auto-installed):
- WinGet (Windows Package Manager)
- Chocolatey
- oscdimg.exe (for MicroWin)
Performance Considerations
Optimization Strategies:
- Lazy-load configurations (only when needed)
- Use runspaces for long operations
- Cache expensive lookups
- Minimize registry reads/writes
- Batch operations when possible
Security Considerations
Safety Measures:
- All operations logged
- Registry backups for undo
- No credential storage
- Open source (auditable)
- Digitally signed (future)
Contributing Guidelines
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*.ps1orInvoke-Winutil*.ps1 - Private functions:
Get-WinUtil*.ps1or verb-WinUtil*.ps1` - Must include “WPF” or “Winutil” to load
Future Architecture Plans
Roadmap Considerations:
- Plugin system for community extensions
- Config import/export
- Cloud sync for configurations
- Enhanced logging dashboard
- Modular compilation (choose features)
Related Documentation
- Contributing Guide - How to contribute code
- User Guide - End-user documentation
- FAQ - Common questions
Additional Resources
- GitHub Repository: ChrisTitusTech/winutil
- PowerShell Docs: Microsoft Docs
- WPF Guide: WPF Documentation
Last Updated: January 2026
Maintainers: Chris Titus Tech and contributors