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/                     # Documentation

Key Components

1. Compile.ps1

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 single script
  5. 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 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.

4. functions/private/

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

5. config/*.json

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

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 message

Tweak 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 completion

Undo 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 UI

Configuration 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 tab
  • content: Display name in GUI
  • description: Tooltip/description text
  • winget: WinGet package ID
  • choco: Chocolatey package name
  • link: 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 name
  • Description: What it does
  • category: Essential/Advanced/Customize
  • registry: Registry changes to make
  • ScheduledTask: Scheduled tasks to modify
  • service: Services to change
  • OriginalValue/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-agreements

Chocolatey Integration

# Check if installed
if (!(Get-Command choco -ErrorAction SilentlyContinue)) {
    Install-WinUtilChoco
}

# Install package
choco install $app.choco -y

MicroWin Architecture

MicroWin operates in phases:

  1. Mount ISO: Extract Windows image
  2. Modify Image: Remove components, apply tweaks
  3. Inject Drivers: Add custom drivers (optional)
  4. Create Unattend: Generate automated install configuration
  5. Rebuild ISO: Package modified image into new ISO

Key Files:

  • Invoke-Microwin.ps1: Main orchestration
  • Microwin-RemovePackages.ps1: Remove Windows packages
  • Microwin-RemoveFeatures.ps1: Disable features
  • Microwin-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-Json

Sync 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

  1. Edit 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 Install tab

Adding a New Tweak

  1. 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"
      }
    ]
  }
}
  1. Recompile: .\Compile.ps1
  2. Tweak appears in Tweaks tab

Adding a New Function

  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

Testing

Manual Testing

# Compile and run with -run flag
.\Compile.ps1 -run

Automated Tests

Tests are in /pester/:

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

Run tests:

Invoke-Pester

Build Process

Development Build

.\Compile.ps1

Outputs winutil.ps1 in the root directory.

Production Release

  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

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*.ps1 or Invoke-Winutil*.ps1
  • Private functions: Get-WinUtil*.ps1 or 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

Additional Resources


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