mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-10-08 11:39:10 +11:00
Compare commits
14
Commits
9ac45d6c31
...
f65d2631a0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f65d2631a0 | ||
|
|
5f39d3b42d | ||
|
|
448543870a | ||
|
|
91a4f62068 | ||
|
|
2a73455872 | ||
|
|
52ebc931fd | ||
|
|
7aff652d4c | ||
|
|
be394d2ce0 | ||
|
|
b4181bd008 | ||
|
|
da7354bfc0 | ||
|
|
9de91c3999 | ||
|
|
c2d65b6f99 | ||
|
|
700725a878 | ||
|
|
f9139c75ff |
@@ -30,7 +30,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install uv and Python
|
||||
uses: astral-sh/setup-uv@v9.0.0
|
||||
uses: astral-sh/setup-uv@v10.1.0
|
||||
with:
|
||||
version: "0.12.0"
|
||||
python-version: "3.13"
|
||||
|
||||
@@ -17,6 +17,15 @@
|
||||
"winget": "7zip.7zip",
|
||||
"foss": true
|
||||
},
|
||||
"abdownloadmanager": {
|
||||
"category": "Utilities",
|
||||
"choco": "ab-download-manager",
|
||||
"content": "AB Download Manager",
|
||||
"description": "AB Download Manager is an open-source download accelerator and manager with multi-threaded downloads, queue scheduling, speed limiting, and browser integration.",
|
||||
"link": "https://abdownloadmanager.com/",
|
||||
"winget": "amir1376.ABDownloadManager",
|
||||
"foss": true
|
||||
},
|
||||
"adobe": {
|
||||
"category": "Document",
|
||||
"choco": "adobereader",
|
||||
@@ -1609,6 +1618,23 @@
|
||||
"winget": "LizardByte.Sunshine",
|
||||
"foss": true
|
||||
},
|
||||
"synctrayzor": {
|
||||
"category": "Selfhosted Tools",
|
||||
"choco": "synctrayzor",
|
||||
"content": "SyncTrayzor",
|
||||
"description": "SyncTrayzor is a Windows tray utility that bundles and wraps Syncthing, making it behave like a native application to easily manage and monitor file synchronization.",
|
||||
"link": "https://github.com/GermanCoding/SyncTrayzor",
|
||||
"winget": "GermanCoding.SyncTrayzor"
|
||||
},
|
||||
"syncthing": {
|
||||
"category": "Selfhosted Tools",
|
||||
"choco": "syncthing",
|
||||
"content": "Syncthing (CLI / Web UI)",
|
||||
"description": "Syncthing is a decentralized, peer-to-peer file synchronization tool that securely syncs files directly across devices without cloud servers, managed via a local web interface.",
|
||||
"link": "https://syncthing.net/",
|
||||
"winget": "Syncthing.Syncthing",
|
||||
"foss": true
|
||||
},
|
||||
"tcpview": {
|
||||
"category": "Microsoft Tools",
|
||||
"choco": "tcpview",
|
||||
@@ -2016,9 +2042,8 @@
|
||||
},
|
||||
"Overwolf": {
|
||||
"category": "Games",
|
||||
"choco": "overwolf",
|
||||
"content": "Overwolf",
|
||||
"description": "Popular platform for game overlays and companion apps (mod managers, trackers, etc.), widely used by gamers.",
|
||||
"content": "CurseForge",
|
||||
"description": "CurseForge is a desktop application for managing mods and modpacks across multiple games, powered by Overwolf.",
|
||||
"link": "https://www.overwolf.com/app/overwolf-curseforge",
|
||||
"winget": "Overwolf.CurseForge",
|
||||
"foss": false
|
||||
@@ -2096,3 +2121,4 @@
|
||||
"foss": false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,59 +54,5 @@
|
||||
"Primary6": "2a10:50c0::bad1:ff",
|
||||
"Secondary6": "2a10:50c0::bad2:ff",
|
||||
"DohTemplate": "https://family.adguard-dns.com/dns-query"
|
||||
},
|
||||
"Mullvad":{
|
||||
"Primary": "194.242.2.2",
|
||||
"Secondary": "194.242.2.3",
|
||||
"Primary6": "2a07:e340::2",
|
||||
"Secondary6": "2a07:e340::3",
|
||||
"DohOnly": true,
|
||||
"DohTemplate": "https://dns.mullvad.net/dns-query",
|
||||
"SecondaryDohTemplate": "https://adblock.dns.mullvad.net/dns-query"
|
||||
},
|
||||
"Mullvad_Ads_Trackers":{
|
||||
"Primary": "194.242.2.3",
|
||||
"Secondary": "194.242.2.2",
|
||||
"Primary6": "2a07:e340::3",
|
||||
"Secondary6": "2a07:e340::2",
|
||||
"DohOnly": true,
|
||||
"DohTemplate": "https://adblock.dns.mullvad.net/dns-query",
|
||||
"SecondaryDohTemplate": "https://dns.mullvad.net/dns-query"
|
||||
},
|
||||
"Mullvad_Ads_Trackers_Malware":{
|
||||
"Primary": "194.242.2.4",
|
||||
"Secondary": "194.242.2.3",
|
||||
"Primary6": "2a07:e340::4",
|
||||
"Secondary6": "2a07:e340::3",
|
||||
"DohOnly": true,
|
||||
"DohTemplate": "https://base.dns.mullvad.net/dns-query",
|
||||
"SecondaryDohTemplate": "https://adblock.dns.mullvad.net/dns-query"
|
||||
},
|
||||
"Mullvad_Ads_Trackers_Malware_Social":{
|
||||
"Primary": "194.242.2.5",
|
||||
"Secondary": "194.242.2.4",
|
||||
"Primary6": "2a07:e340::5",
|
||||
"Secondary6": "2a07:e340::4",
|
||||
"DohOnly": true,
|
||||
"DohTemplate": "https://extended.dns.mullvad.net/dns-query",
|
||||
"SecondaryDohTemplate": "https://base.dns.mullvad.net/dns-query"
|
||||
},
|
||||
"Mullvad_Ads_Trackers_Malware_Adult_Gambling":{
|
||||
"Primary": "194.242.2.6",
|
||||
"Secondary": "194.242.2.5",
|
||||
"Primary6": "2a07:e340::6",
|
||||
"Secondary6": "2a07:e340::5",
|
||||
"DohOnly": true,
|
||||
"DohTemplate": "https://family.dns.mullvad.net/dns-query",
|
||||
"SecondaryDohTemplate": "https://extended.dns.mullvad.net/dns-query"
|
||||
},
|
||||
"Mullvad_Ads_Trackers_Malware_Adult_Gambling_Social":{
|
||||
"Primary": "194.242.2.9",
|
||||
"Secondary": "194.242.2.6",
|
||||
"Primary6": "2a07:e340::9",
|
||||
"Secondary6": "2a07:e340::6",
|
||||
"DohOnly": true,
|
||||
"DohTemplate": "https://all.dns.mullvad.net/dns-query",
|
||||
"SecondaryDohTemplate": "https://family.dns.mullvad.net/dns-query"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
"FontSize": "12",
|
||||
"FontFamily": "Arial",
|
||||
"HeaderFontSize": "16",
|
||||
"Win11StepTitleFontSize": "22",
|
||||
"Win11StepHeroFontSize": "40",
|
||||
"Win11LogFontFamily": "Consolas, Monaco",
|
||||
"HeaderFontFamily": "Consolas, Monaco",
|
||||
"CheckBoxBulletDecoratorSize": "14",
|
||||
"CheckBoxMargin": "15,0,0,2",
|
||||
|
||||
+2
-2
@@ -1686,7 +1686,7 @@
|
||||
],
|
||||
"link": "https://winutil.christitus.com/code-reference/tweaks/customize-preferences/loginblur"
|
||||
},
|
||||
"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",
|
||||
@@ -1885,7 +1885,7 @@
|
||||
"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 Google Cloudflare Cloudflare_Malware Cloudflare_Malware_Adult Open_DNS Quad9 AdGuard_Ads_Trackers AdGuard_Ads_Trackers_Malware_Adult",
|
||||
"link": "https://winutil.christitus.com/code-reference/tweaks/z--advanced-tweaks---caution/changedns"
|
||||
},
|
||||
"WPFAddUltPerf": {
|
||||
|
||||
Generated
+537
-266
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -13,7 +13,7 @@
|
||||
"@astrojs/starlight": "^0.41.5",
|
||||
"@fontsource-variable/jetbrains-mono": "^5.3.0",
|
||||
"@fontsource/geist-sans": "^5.3.0",
|
||||
"astro": "^7.0.2",
|
||||
"sharp": "^0.35.3"
|
||||
"astro": "^7.3.2",
|
||||
"sharp": "^0.35.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ Invoke-WinUtilISOCleanAndReset (optional)
|
||||
|
||||
**Work Session Recovery**:
|
||||
- Auto-detects incomplete work from previous sessions
|
||||
- Allows resuming Step 4 (export) without re-running Steps 1-3
|
||||
- Allows resuming the export step without re-running selection and modification
|
||||
- Prevents redundant modifications
|
||||
|
||||
**Modification Safety**:
|
||||
|
||||
@@ -73,14 +73,6 @@ Use the DNS section to switch both IPv4 and IPv6 DNS providers without editing a
|
||||
* [**Quad9**](https://quad9.net/): Focuses on security by blocking known malicious domains.
|
||||
* [**AdGuard_Ads_Trackers**](https://adguard-dns.io/en/welcome.html): AdGuard DNS blocks ads, trackers, and other unwanted DNS requests. Visit the website and sign in for a dashboard, statistics, and additional server-side customization.
|
||||
* [**AdGuard_Ads_Trackers_Malware_Adult**](https://adguard-dns.io/en/welcome.html): AdGuard DNS blocks ads, trackers, malware, and adult content, and enables Safe Search and Safe Mode where possible.
|
||||
* [**Mullvad**](https://mullvad.net/en/help/dns-over-https-and-dns-over-tls): Mullvad DNS without content blocking.
|
||||
* [**Mullvad_Ads_Trackers**](https://mullvad.net/en/help/dns-over-https-and-dns-over-tls): Blocks ads and trackers.
|
||||
* [**Mullvad_Ads_Trackers_Malware**](https://mullvad.net/en/help/dns-over-https-and-dns-over-tls): Blocks ads, trackers, and malware.
|
||||
* [**Mullvad_Ads_Trackers_Malware_Social**](https://mullvad.net/en/help/dns-over-https-and-dns-over-tls): Blocks ads, trackers, malware, and social media.
|
||||
* [**Mullvad_Ads_Trackers_Malware_Adult_Gambling**](https://mullvad.net/en/help/dns-over-https-and-dns-over-tls): Blocks ads, trackers, malware, adult content, and gambling.
|
||||
* [**Mullvad_Ads_Trackers_Malware_Adult_Gambling_Social**](https://mullvad.net/en/help/dns-over-https-and-dns-over-tls): Applies all available Mullvad filters.
|
||||
|
||||
Mullvad profiles require DNS over HTTPS support in Windows. If the selected primary resolver is unavailable, WinUtil uses the closest Mullvad secondary resolver to preserve connectivity; that fallback may use a different filtering level and can be less restrictive.
|
||||
|
||||
### Customize Preferences
|
||||
|
||||
|
||||
@@ -23,29 +23,26 @@ This workflow is intended for fresh Windows installs, not in-place upgrades of a
|
||||
|
||||
---
|
||||
|
||||
The tab is a three-step wizard. The step you are on is shown in the header row, and you can click an earlier step to go back to it until the image has been modified. The status log on the right stays visible throughout.
|
||||
|
||||
### Step 1 — Select Your Official Windows 11 ISO
|
||||
|
||||
1. Open WinUtil and go to the **Win11 Creator** tab.
|
||||
2. Click **Browse** and select your **official Windows 11 ISO file** from Microsoft (must be 4 GB or larger). Custom or modified ISOs are not supported.
|
||||
3. The file path and size will appear on screen once selected.
|
||||
|
||||
---
|
||||
|
||||
### Step 2 — Mount & Verify
|
||||
|
||||
1. Click **Mount & Verify ISO**.
|
||||
2. WinUtil mounts the ISO, checks for a valid `install.wim` or `install.esd`, and reads the available editions (Home, Pro, Enterprise, etc.).
|
||||
3. Once verified, select your desired **edition** from the dropdown — Pro is selected by default if available.
|
||||
4. Click **Mount & Verify ISO**. WinUtil mounts the ISO, checks for a valid `install.wim` or `install.esd`, and reads the available editions (Home, Pro, Enterprise, etc.).
|
||||
|
||||
:::note
|
||||
This step takes around 10–30 seconds, depending on your drive speed.
|
||||
Mounting and verification takes around 10–30 seconds, depending on your drive speed.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
### Step 3 — Run the Modification
|
||||
### Step 2 — Modify the Image
|
||||
|
||||
Click **Run Windows ISO Modification and Creator** to start the customization process. WinUtil will:
|
||||
Once the ISO is verified, WinUtil moves to this step and shows the mounted drive and image file. Choose your **edition** from the dropdown — Pro is selected by default if available — and tick **Inject current system drivers** if you want them.
|
||||
|
||||
Then click **Run Windows ISO Modification and Creator** to start the customization process. WinUtil will:
|
||||
|
||||
**App & Component Removal:**
|
||||
- **Remove 40+ bloat apps** — Clipchamp, Teams, Copilot, Dev Home, new Outlook, Bing apps, Solitaire, and more
|
||||
@@ -70,7 +67,7 @@ Click **Run Windows ISO Modification and Creator** to start the customization pr
|
||||
- **Disable Copilot and search box suggestions**
|
||||
|
||||
**Optional: Driver Injection**
|
||||
- If enabled, WinUtil exports the drivers from your current system, stages boot-storage drivers for Windows Setup, and injects eligible packages into the selected `install.wim` image. Extension-class packages and stale duplicates are excluded. Each remaining package is added separately, so one incompatible package does not stop the others. If a package fails, WinUtil discards the partial mount and retries the remaining packages against the original image. Check the live log for warnings: if every package fails, WinUtil keeps the original `install.wim` and still produces the ISO.
|
||||
- If enabled, WinUtil exports the drivers from your current system, stages boot-storage drivers for Windows Setup, and injects eligible packages into the selected `install.wim` image. Stale duplicate packages are excluded before injection. Each remaining package is added separately, so one incompatible package does not stop the others. If a package fails, WinUtil discards the partial mount and retries the remaining packages against the original image. Check the live log for warnings: if every package fails, WinUtil keeps the original `install.wim` and still produces the ISO.
|
||||
|
||||
A live log shows progress as each step completes. This stage usually takes **10–30 minutes** depending on disk speed. The WIM dismount near the end is the slowest part, so do not close WinUtil while it is running.
|
||||
|
||||
@@ -80,7 +77,7 @@ The resulting ISO is close to the size of the source ISO. WinUtil does not remov
|
||||
|
||||
---
|
||||
|
||||
### Step 4 — Export Your Result
|
||||
### Step 3 — Export Your Result
|
||||
|
||||
Once the modification is complete, choose how to save your image:
|
||||
|
||||
@@ -110,9 +107,9 @@ Double-check you have selected the correct drive before confirming. This operati
|
||||
|
||||
---
|
||||
|
||||
### Step 5 — Clean Up (Optional)
|
||||
### Start Over (Optional)
|
||||
|
||||
Click **Clean & Reset** to delete the temporary working directory (~10–15 GB) and return the tool to its initial state, ready for a new ISO. You will be asked to confirm before anything is deleted.
|
||||
Click **Start Over** to delete the temporary working directory (~10–15 GB) and return the tool to its initial state, ready for a new ISO. You will be asked to confirm before anything is deleted.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ function Invoke-WinUtilFontScaling {
|
||||
"FontSize",
|
||||
"ButtonFontSize",
|
||||
"HeaderFontSize",
|
||||
"Win11StepTitleFontSize",
|
||||
"Win11StepHeroFontSize",
|
||||
"TabButtonFontSize",
|
||||
"ConfigTabButtonFontSize",
|
||||
"IconFontSize",
|
||||
|
||||
@@ -64,6 +64,45 @@ function Write-WinUtilISOLog {
|
||||
}
|
||||
}
|
||||
|
||||
function Set-WinUtilISOStep {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Selects a page of the Win11 Creator wizard and sets which pages can be navigated back to
|
||||
|
||||
.PARAMETER Step
|
||||
Select, Modify, Working or Output
|
||||
|
||||
.PARAMETER Label
|
||||
Headline shown on the working page while a long operation runs
|
||||
|
||||
.PARAMETER Reverse
|
||||
Spins the working page icon backwards
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateSet("Select", "Modify", "Working", "Output")]
|
||||
[string]$Step,
|
||||
|
||||
[string]$Label,
|
||||
|
||||
[switch]$Reverse
|
||||
)
|
||||
|
||||
Invoke-WPFUIThread -Parameters @{ Step = $Step; Label = $Label; Reverse = [bool]$Reverse } -ScriptBlock {
|
||||
param($Step, $Label, $Reverse)
|
||||
|
||||
if ($Label) { $sync["WPFWin11ISOWorkingLabel"].Text = $Label }
|
||||
|
||||
$sync["WPFWin11ISOWorkingSpinner"].Tag = if ($Reverse) { "Reverse" } else { "Forward" }
|
||||
|
||||
$sync["WPFWin11ISOSelectSection"].IsEnabled = $Step -in @("Select", "Modify")
|
||||
$sync["WPFWin11ISOModifySection"].IsEnabled = $Step -eq "Modify"
|
||||
$sync["WPFWin11ISOOutputSection"].IsEnabled = $Step -eq "Output"
|
||||
|
||||
$sync["WPFWin11ISO$($Step)Section"].IsSelected = $true
|
||||
}
|
||||
}
|
||||
|
||||
function Get-WinUtilEditionIdFromName {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
@@ -108,10 +147,9 @@ function Invoke-WinUtilISOBrowse {
|
||||
$sync["WPFWin11ISOPath"].Text = $isoPath
|
||||
$sync["WPFWin11ISOFileInfo"].Text = "File size: $fileSizeGB GB"
|
||||
$sync["WPFWin11ISOFileInfo"].Visibility = "Visible"
|
||||
$sync["WPFWin11ISOMountSection"].Visibility = "Visible"
|
||||
$sync["WPFWin11ISOVerifyResultPanel"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOModifySection"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOOutputSection"].Visibility = "Collapsed"
|
||||
|
||||
Set-WinUtilISOStep -Step "Select"
|
||||
|
||||
Write-WinUtilISOLog "ISO selected: $isoPath ($fileSizeGB GB)"
|
||||
}
|
||||
@@ -134,17 +172,32 @@ function Invoke-WinUtilISOMountAndVerify {
|
||||
$sync["WPFWin11ISOMountButton"].IsEnabled = $false
|
||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = $false
|
||||
$sync["WPFWin11ISOVerifyResultPanel"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOModifySection"].Visibility = "Collapsed"
|
||||
}
|
||||
Set-WinUtilISOStep -Step "Working" -Label "Mounting and verifying the ISO"
|
||||
|
||||
$verified = $false
|
||||
$mountedByThisRun = $false
|
||||
$sync["Win11ISOImageInfo"] = $null
|
||||
$sync["Win11ISODriveLetter"] = $null
|
||||
$sync["Win11ISOWimPath"] = $null
|
||||
$sync["Win11ISOImagePath"] = $null
|
||||
|
||||
try {
|
||||
$previous = $sync["Win11ISOImagePath"]
|
||||
if ($previous -and $previous -ne $isoPath -and (Get-DiskImage -ImagePath $previous -ErrorAction SilentlyContinue).Attached) {
|
||||
try {
|
||||
Dismount-DiskImage -ImagePath $previous -ErrorAction Stop
|
||||
Write-WinUtilISOLog "Dismounted the previously verified ISO: $previous"
|
||||
} catch {
|
||||
Write-WinUtilISOLog -Level "ERROR" -Message "Could not dismount the previously verified ISO ${previous}: $_"
|
||||
Show-WinUtilMessage -Message "The previously verified ISO is still mounted and could not be dismounted:`n`n$previous`n`nDismount it yourself, then select an ISO again." -Title "Previous ISO Still Mounted" -Button "OK" -Icon "Error" | Out-Null
|
||||
$stillMounted = [System.InvalidOperationException]::new("Could not dismount the previously verified ISO $previous.")
|
||||
$stillMounted.Data["WinUtilErrorReported"] = $true
|
||||
throw $stillMounted
|
||||
}
|
||||
}
|
||||
|
||||
$sync["Win11ISOImageInfo"] = $null
|
||||
$sync["Win11ISODriveLetter"] = $null
|
||||
$sync["Win11ISOWimPath"] = $null
|
||||
$sync["Win11ISOImagePath"] = $null
|
||||
|
||||
Write-WinUtilISOLog "Mounting ISO: $isoPath"
|
||||
Step-WinUtilJob -Status "Mounting ISO..." -Percent 10
|
||||
|
||||
@@ -207,7 +260,8 @@ function Invoke-WinUtilISOMountAndVerify {
|
||||
} -ScriptBlock {
|
||||
param($DriveLetter, $ImageFileName, $imageInfo)
|
||||
|
||||
$sync["WPFWin11ISOMountDriveLetter"].Text = "Mounted at: $DriveLetter | Image file: $ImageFileName"
|
||||
$sync["WPFWin11ISOMountDriveLetter"].Text = $DriveLetter
|
||||
$sync["WPFWin11ISOImageFile"].Text = $ImageFileName
|
||||
$sync["WPFWin11ISOEditionComboBox"].Items.Clear()
|
||||
foreach ($img in $imageInfo) {
|
||||
[void]$sync["WPFWin11ISOEditionComboBox"].Items.Add("$($img.ImageIndex): $($img.ImageName)")
|
||||
@@ -222,7 +276,7 @@ function Invoke-WinUtilISOMountAndVerify {
|
||||
$sync["WPFWin11ISOEditionComboBox"].SelectedIndex = if ($proIndex -ge 0) { $proIndex } else { 0 }
|
||||
}
|
||||
$sync["WPFWin11ISOVerifyResultPanel"].Visibility = "Visible"
|
||||
$sync["WPFWin11ISOModifySection"].Visibility = "Visible"
|
||||
Set-WinUtilISOStep -Step "Modify"
|
||||
}
|
||||
|
||||
$verified = $true
|
||||
@@ -246,6 +300,10 @@ function Invoke-WinUtilISOMountAndVerify {
|
||||
$sync["WPFWin11ISOBrowseButton"].IsEnabled = $true
|
||||
$sync["WPFWin11ISOMountButton"].IsEnabled = $true
|
||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = [bool]$Verified
|
||||
|
||||
if ($sync["WPFWin11ISOWorkingSection"].IsSelected) {
|
||||
Set-WinUtilISOStep -Step "Select"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,7 +315,7 @@ function Invoke-WinUtilISOModify {
|
||||
$wimPath = $sync["Win11ISOWimPath"]
|
||||
|
||||
if (-not $isoPath) {
|
||||
Show-WinUtilMessage -Message "No verified ISO found. Please complete Steps 1 and 2 first." -Title "Not Ready" -Button "OK" -Icon "Warning" | Out-Null
|
||||
Show-WinUtilMessage -Message "No verified ISO found. Please select and verify an ISO first." -Title "Not Ready" -Button "OK" -Icon "Warning" | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
@@ -297,10 +355,8 @@ function Invoke-WinUtilISOModify {
|
||||
|
||||
Invoke-WPFUIThread -ScriptBlock {
|
||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = $false
|
||||
$sync["WPFWin11ISOSelectSection"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOMountSection"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOModifySection"].Visibility = "Collapsed"
|
||||
}
|
||||
Set-WinUtilISOStep -Step "Working" -Label "Modifying install.wim"
|
||||
|
||||
$modified = $false
|
||||
try {
|
||||
@@ -362,11 +418,9 @@ function Invoke-WinUtilISOModify {
|
||||
$sync["Win11ISOContentsDir"] = $isoContents
|
||||
|
||||
Step-WinUtilJob -Status "Modification complete" -Percent 100
|
||||
Write-WinUtilISOLog "install.wim modification complete. Choose an output option in Step 4."
|
||||
Write-WinUtilISOLog "install.wim modification complete. Choose an output option in the last step."
|
||||
|
||||
Invoke-WPFUIThread -ScriptBlock {
|
||||
$sync["WPFWin11ISOOutputSection"].Visibility = "Visible"
|
||||
}
|
||||
Set-WinUtilISOStep -Step "Output"
|
||||
$modified = $true
|
||||
} catch {
|
||||
Write-WinUtilISOLog -Level "ERROR" -Message "Modification failed: $_"
|
||||
@@ -404,10 +458,9 @@ function Invoke-WinUtilISOModify {
|
||||
param($Modified)
|
||||
|
||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = [bool]$Modified
|
||||
if ($sync["WPFWin11ISOOutputSection"].Visibility -ne "Visible") {
|
||||
$sync["WPFWin11ISOSelectSection"].Visibility = "Visible"
|
||||
$sync["WPFWin11ISOMountSection"].Visibility = "Visible"
|
||||
$sync["WPFWin11ISOModifySection"].Visibility = if ($Modified) { "Visible" } else { "Collapsed" }
|
||||
|
||||
if ($sync["WPFWin11ISOWorkingSection"].IsSelected) {
|
||||
Set-WinUtilISOStep -Step "Select"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -443,24 +496,21 @@ function Invoke-WinUtilISOCheckExistingWork {
|
||||
$sync["Win11ISOWorkDir"] = $existingWorkDir.FullName
|
||||
$sync["Win11ISOContentsDir"] = $isoContents
|
||||
|
||||
$sync["WPFWin11ISOSelectSection"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOMountSection"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOModifySection"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOOutputSection"].Visibility = "Visible"
|
||||
Set-WinUtilISOStep -Step "Output"
|
||||
|
||||
$modified = $existingWorkDir.LastWriteTime.ToString("yyyy-MM-dd HH:mm")
|
||||
Write-WinUtilISOLog "Existing working directory found: $($existingWorkDir.FullName)"
|
||||
Write-WinUtilISOLog "Last modified: $modified - Skipping Steps 1-3 and resuming at Step 4."
|
||||
Write-WinUtilISOLog "Click 'Clean & Reset' if you want to start over with a new ISO."
|
||||
Write-WinUtilISOLog "Last modified: $modified - Skipping the earlier steps and resuming at the output step."
|
||||
Write-WinUtilISOLog "Click 'Start Over' if you want to start over with a new ISO."
|
||||
|
||||
Show-WinUtilMessage -Message "A previous WinUtil ISO working directory was found:`n`n$($existingWorkDir.FullName)`n`n(Last modified: $modified)`n`nStep 4 (output options) has been restored so you can save the already-modified image.`n`nClick 'Clean & Reset' in Step 4 if you want to start over." -Title "Existing Work Found" -Button "OK" -Icon "Info" | Out-Null
|
||||
Show-WinUtilMessage -Message "A previous WinUtil ISO working directory was found:`n`n$($existingWorkDir.FullName)`n`n(Last modified: $modified)`n`nThe output step has been restored so you can save the already-modified image.`n`nClick 'Start Over' there if you want to start over." -Title "Existing Work Found" -Button "OK" -Icon "Info" | Out-Null
|
||||
}
|
||||
|
||||
function Invoke-WinUtilISOCleanAndReset {
|
||||
$workDir = $sync["Win11ISOWorkDir"]
|
||||
|
||||
if ($workDir -and (Test-Path $workDir)) {
|
||||
$confirm = Show-WinUtilMessage -Message "This will delete the temporary working directory:`n`n$workDir`n`nAnd reset the interface back to the start.`n`nContinue?" -Title "Clean & Reset" -Button "YesNo" -Icon "Warning"
|
||||
$confirm = Show-WinUtilMessage -Message "This will delete the temporary working directory:`n`n$workDir`n`nAnd reset the interface back to the start.`n`nContinue?" -Title "Start Over" -Button "YesNo" -Icon "Warning"
|
||||
if ($confirm -ne "Yes") { return }
|
||||
}
|
||||
|
||||
@@ -470,6 +520,7 @@ function Invoke-WinUtilISOCleanAndReset {
|
||||
param($workDir)
|
||||
|
||||
Invoke-WPFUIThread -ScriptBlock { $sync["WPFWin11ISOCleanResetButton"].IsEnabled = $false }
|
||||
Set-WinUtilISOStep -Step "Working" -Label "Starting over" -Reverse
|
||||
|
||||
try {
|
||||
if ($workDir) {
|
||||
@@ -544,19 +595,23 @@ function Invoke-WinUtilISOCleanAndReset {
|
||||
|
||||
Invoke-WPFUIThread -ScriptBlock {
|
||||
$sync["WPFWin11ISOPath"].Text = "No ISO selected..."
|
||||
$sync["WPFWin11ISOFileInfo"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOFileInfo"].Visibility = "Hidden"
|
||||
$sync["WPFWin11ISOVerifyResultPanel"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOOptionUSB"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOOutputSection"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOModifySection"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOMountSection"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOSelectSection"].Visibility = "Visible"
|
||||
$sync["WPFWin11ISODonePanel"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = $true
|
||||
$sync["WPFWin11ISOStatusLog"].Text = "Ready. Please select a Windows 11 ISO to begin."
|
||||
Set-WinUtilISOStep -Step "Select"
|
||||
}
|
||||
Step-WinUtilJob -Hide
|
||||
} finally {
|
||||
Invoke-WPFUIThread -ScriptBlock { $sync["WPFWin11ISOCleanResetButton"].IsEnabled = $true }
|
||||
Invoke-WPFUIThread -ScriptBlock {
|
||||
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $true
|
||||
|
||||
if ($sync["WPFWin11ISOWorkingSection"].IsSelected) {
|
||||
Set-WinUtilISOStep -Step "Select"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -565,7 +620,7 @@ function Invoke-WinUtilISOExport {
|
||||
$contentsDir = $sync["Win11ISOContentsDir"]
|
||||
|
||||
if (-not $contentsDir -or -not (Test-Path $contentsDir)) {
|
||||
Show-WinUtilMessage -Message "No modified ISO content found. Please complete Steps 1-3 first." -Title "Not Ready" -Button "OK" -Icon "Warning" | Out-Null
|
||||
Show-WinUtilMessage -Message "No modified ISO content found. Please run the modification step first." -Title "Not Ready" -Button "OK" -Icon "Warning" | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
@@ -586,10 +641,12 @@ function Invoke-WinUtilISOExport {
|
||||
param($contentsDir, $outputISO)
|
||||
|
||||
Invoke-WPFUIThread -ScriptBlock { $sync["WPFWin11ISOChooseISOButton"].IsEnabled = $false }
|
||||
Set-WinUtilISOStep -Step "Working" -Label "Building the ISO file"
|
||||
|
||||
try {
|
||||
$oscdimg = Get-WinUtilOscdimgPath
|
||||
if (-not $oscdimg) {
|
||||
Set-WinUtilISOStep -Step "Output"
|
||||
Show-WinUtilMessage -Message "oscdimg.exe could not be found or installed automatically.`n`nPlease install it manually:`n winget install -e --id Microsoft.OSCDIMG`n`nOr install the Windows ADK from:`nhttps://learn.microsoft.com/windows-hardware/get-started/adk-install" -Title "oscdimg Not Found" -Button "OK" -Icon "Warning" | Out-Null
|
||||
throw "oscdimg.exe could not be found or installed automatically."
|
||||
}
|
||||
@@ -648,14 +705,29 @@ function Invoke-WinUtilISOExport {
|
||||
|
||||
Step-WinUtilJob -Status "ISO exported" -Percent 100
|
||||
Write-WinUtilISOLog "ISO exported successfully: $outputISO"
|
||||
Invoke-WPFUIThread -Parameters @{ OutputISO = $outputISO } -ScriptBlock {
|
||||
param($OutputISO)
|
||||
|
||||
$sync["WPFWin11ISODoneLabel"].Text = "ISO saved to $OutputISO"
|
||||
$sync["WPFWin11ISODonePanel"].Visibility = "Visible"
|
||||
}
|
||||
Set-WinUtilISOStep -Step "Output"
|
||||
Show-WinUtilMessage -Message "ISO exported successfully!`n`n$outputISO" -Title "Export Complete" -Button "OK" -Icon "Info" | Out-Null
|
||||
} catch {
|
||||
Write-WinUtilISOLog -Level "ERROR" -Message "ISO export failed: $_"
|
||||
$_.Exception.Data["WinUtilErrorReported"] = $true
|
||||
Set-WinUtilISOStep -Step "Output"
|
||||
Show-WinUtilMessage -Message "ISO export failed:`n`n$_" -Title "Error" -Button "OK" -Icon "Error" | Out-Null
|
||||
throw
|
||||
} finally {
|
||||
Invoke-WPFUIThread -ScriptBlock { $sync["WPFWin11ISOChooseISOButton"].IsEnabled = $true }
|
||||
Invoke-WPFUIThread -ScriptBlock {
|
||||
$sync["WPFWin11ISOChooseISOButton"].IsEnabled = $true
|
||||
|
||||
# Cancellation skips catch, so the working page can still be up here
|
||||
if ($sync["WPFWin11ISOWorkingSection"].IsSelected) {
|
||||
Set-WinUtilISOStep -Step "Output"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,17 +84,6 @@ function Invoke-WinUtilISOScript {
|
||||
}
|
||||
}
|
||||
|
||||
function Test-WinUtilISODriverExtensionClass {
|
||||
param ([Parameter(Mandatory)][System.IO.FileInfo]$InfFile)
|
||||
|
||||
try {
|
||||
return (Get-Content -LiteralPath $InfFile.FullName -Raw -ErrorAction Stop) -match '(?im)^\s*Class\s*=\s*"?Extension"?\s*(?:;.*)?$'
|
||||
} catch {
|
||||
$null = & $Logger "Warning: could not classify driver '$($InfFile.FullName)': $_"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
function Get-WinUtilISODriverPackageVersion {
|
||||
param ([Parameter(Mandatory)][System.IO.FileInfo]$InfFile)
|
||||
|
||||
@@ -160,15 +149,6 @@ function Invoke-WinUtilISOScript {
|
||||
|
||||
foreach ($driverFolderGroup in $DriverFolderGroups) {
|
||||
$driverFolder = [string]$driverFolderGroup.Name
|
||||
$isExtension = [bool]@($driverFolderGroup.Group | Where-Object { Test-WinUtilISODriverExtensionClass -InfFile $_ }).Count
|
||||
|
||||
if ($isExtension) {
|
||||
# $null = discards $Logger's own output; this function's return value is captured
|
||||
# by the caller, and an emitting logger (e.g. this function's own default) would
|
||||
# otherwise leak into the surviving-folder list.
|
||||
$null = & $Logger "Excluding extension-class driver package '$driverFolder' from Add-Driver (Class=Extension is not a serviceable hardware driver)."
|
||||
continue
|
||||
}
|
||||
|
||||
# DISM names exported package folders <infname>_<arch>_<hash>; grouping on infname+arch
|
||||
# (dropping the hash) is what lets us recognize two exports of the same driver. When a
|
||||
@@ -346,103 +326,97 @@ function Invoke-WinUtilISOScript {
|
||||
$metadataBefore = Get-WinUtilISOWimMetadata -ImagePath $InstallImagePath -Index $InstallImageIndex
|
||||
Assert-WinUtilISOWimMetadata -Before $metadataBefore
|
||||
|
||||
if ($stagedDriverFolders.Count -eq 0) {
|
||||
# Nothing safe to inject (e.g. every exported package was an Extension-class add-on)
|
||||
# isn't a failure: leave install.wim untouched and continue building the ISO.
|
||||
& $Logger 'No drivers found to inject: every exported package was excluded (Extension class or stale duplicate). Skipping driver injection; install.wim is unchanged.'
|
||||
} else {
|
||||
$excludedDriverFolderGroups = @($driverFolders | Where-Object { $_.Name -notin $stagedDriverFolders })
|
||||
foreach ($excludedDriverFolderGroup in $excludedDriverFolderGroups) {
|
||||
$excludedFolder = [string]$excludedDriverFolderGroup.Name
|
||||
$hasRetainedDescendant = [bool]@($stagedDriverFolders | Where-Object {
|
||||
$_.StartsWith("$excludedFolder\", [System.StringComparison]::OrdinalIgnoreCase)
|
||||
}).Count
|
||||
if ($hasRetainedDescendant) {
|
||||
try {
|
||||
foreach ($excludedInf in $excludedDriverFolderGroup.Group) {
|
||||
Remove-Item -LiteralPath $excludedInf.FullName -Force -ErrorAction Stop
|
||||
}
|
||||
} catch {
|
||||
throw "Failed to remove excluded driver INF files from package '$excludedFolder' before injection: $_"
|
||||
$excludedDriverFolderGroups = @($driverFolders | Where-Object { $_.Name -notin $stagedDriverFolders })
|
||||
foreach ($excludedDriverFolderGroup in $excludedDriverFolderGroups) {
|
||||
$excludedFolder = [string]$excludedDriverFolderGroup.Name
|
||||
$hasRetainedDescendant = [bool]@($stagedDriverFolders | Where-Object {
|
||||
$_.StartsWith("$excludedFolder\", [System.StringComparison]::OrdinalIgnoreCase)
|
||||
}).Count
|
||||
if ($hasRetainedDescendant) {
|
||||
try {
|
||||
foreach ($excludedInf in $excludedDriverFolderGroup.Group) {
|
||||
Remove-Item -LiteralPath $excludedInf.FullName -Force -ErrorAction Stop
|
||||
}
|
||||
} catch {
|
||||
throw "Failed to remove excluded driver INF files from package '$excludedFolder' before injection: $_"
|
||||
}
|
||||
|
||||
& $Logger "Keeping excluded driver package directory '$excludedFolder' because it contains a retained nested package, after removing its excluded INF files."
|
||||
continue
|
||||
& $Logger "Keeping excluded driver package directory '$excludedFolder' because it contains a retained nested package, after removing its excluded INF files."
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
Remove-Item -LiteralPath $excludedFolder -Recurse -Force -ErrorAction Stop
|
||||
} catch {
|
||||
throw "Failed to remove excluded driver package '$excludedFolder' before injection: $_"
|
||||
}
|
||||
}
|
||||
|
||||
& $Logger "Exported $($stagedDriverFolders.Count) of $($driverFolders.Count) driver packages ($storageCount staged for WinPE, $($excludedDriverFolderGroups.Count) excluded)."
|
||||
|
||||
Set-ItemProperty -LiteralPath $InstallImagePath -Name IsReadOnly -Value $false
|
||||
New-Item -Path $mountDir -ItemType Directory -Force | Out-Null
|
||||
|
||||
# Add each package separately so one bad driver cannot fail the rest. Because
|
||||
# /Recurse covers descendants, only the highest surviving folder in each tree
|
||||
# needs its own DISM call.
|
||||
$rootPackageFolders = @($stagedDriverFolders | Where-Object {
|
||||
$candidate = $_
|
||||
-not ($stagedDriverFolders | Where-Object { $candidate.StartsWith("$_\", [System.StringComparison]::OrdinalIgnoreCase) })
|
||||
})
|
||||
|
||||
& $Logger "Adding $($rootPackageFolders.Count) root driver packages to install.wim."
|
||||
$remainingDriverFolders = @($rootPackageFolders)
|
||||
while ($remainingDriverFolders.Count -gt 0) {
|
||||
& $Logger "Mounting install.wim index $InstallImageIndex for driver injection..."
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Mount-Image', "/ImageFile:$InstallImagePath", "/Index:$InstallImageIndex", "/MountDir:$mountDir") -Operation 'mount' | Out-Null
|
||||
$imageMounted = $true
|
||||
|
||||
$failedDriverFolder = $null
|
||||
foreach ($driverFolder in $remainingDriverFolders) {
|
||||
$driverName = $driverFolder
|
||||
if ($driverFolder.StartsWith($driverExportRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
$driverName = $driverFolder.Substring($driverExportRoot.Length).TrimStart('\')
|
||||
}
|
||||
|
||||
try {
|
||||
Remove-Item -LiteralPath $excludedFolder -Recurse -Force -ErrorAction Stop
|
||||
Invoke-WinUtilISODism -Arguments @('/English', "/Image:$mountDir", '/Add-Driver', "/Driver:$driverFolder", '/Recurse') -Operation "add-driver:$driverName" | Out-Null
|
||||
} catch {
|
||||
throw "Failed to remove excluded driver package '$excludedFolder' before injection: $_"
|
||||
}
|
||||
}
|
||||
|
||||
& $Logger "Exported $($stagedDriverFolders.Count) of $($driverFolders.Count) driver packages ($storageCount staged for WinPE, $($excludedDriverFolderGroups.Count) excluded)."
|
||||
|
||||
Set-ItemProperty -LiteralPath $InstallImagePath -Name IsReadOnly -Value $false
|
||||
New-Item -Path $mountDir -ItemType Directory -Force | Out-Null
|
||||
|
||||
# Add each package separately so one bad driver cannot fail the rest. Because
|
||||
# /Recurse covers descendants, only the highest surviving folder in each tree
|
||||
# needs its own DISM call.
|
||||
$rootPackageFolders = @($stagedDriverFolders | Where-Object {
|
||||
$candidate = $_
|
||||
-not ($stagedDriverFolders | Where-Object { $candidate.StartsWith("$_\", [System.StringComparison]::OrdinalIgnoreCase) })
|
||||
})
|
||||
|
||||
& $Logger "Adding $($rootPackageFolders.Count) root driver packages to install.wim."
|
||||
$remainingDriverFolders = @($rootPackageFolders)
|
||||
while ($remainingDriverFolders.Count -gt 0) {
|
||||
& $Logger "Mounting install.wim index $InstallImageIndex for driver injection..."
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Mount-Image', "/ImageFile:$InstallImagePath", "/Index:$InstallImageIndex", "/MountDir:$mountDir") -Operation 'mount' | Out-Null
|
||||
$imageMounted = $true
|
||||
|
||||
$failedDriverFolder = $null
|
||||
foreach ($driverFolder in $remainingDriverFolders) {
|
||||
$driverName = $driverFolder
|
||||
if ($driverFolder.StartsWith($driverExportRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
$driverName = $driverFolder.Substring($driverExportRoot.Length).TrimStart('\')
|
||||
}
|
||||
|
||||
try {
|
||||
Invoke-WinUtilISODism -Arguments @('/English', "/Image:$mountDir", '/Add-Driver', "/Driver:$driverFolder", '/Recurse') -Operation "add-driver:$driverName" | Out-Null
|
||||
} catch {
|
||||
& $Logger "Warning: failed to add driver package '$driverName': $_"
|
||||
$failedDriverFolder = $driverFolder
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $failedDriverFolder) {
|
||||
& $Logger "Warning: failed to add driver package '$driverName': $_"
|
||||
$failedDriverFolder = $driverFolder
|
||||
break
|
||||
}
|
||||
|
||||
& $Logger "Discarding the potentially partial install.wim mount before continuing without '$driverName'."
|
||||
try {
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Unmount-Image', "/MountDir:$mountDir", '/Discard') -Operation 'discard' | Out-Null
|
||||
$imageMounted = $false
|
||||
} catch {
|
||||
throw "Failed to discard the potentially partial install.wim mount after driver package '$driverName' failed: $_"
|
||||
}
|
||||
|
||||
$remainingDriverFolders = @($remainingDriverFolders | Where-Object { $_ -ne $failedDriverFolder })
|
||||
}
|
||||
|
||||
$addedCount = $remainingDriverFolders.Count
|
||||
if ($addedCount -eq 0) {
|
||||
# Boot-storage drivers staged for WinPE remain available to Windows Setup.
|
||||
& $Logger "Warning: none of the $($rootPackageFolders.Count) exported driver packages could be added; continuing with an unmodified install.wim."
|
||||
} else {
|
||||
& $Logger "Added $addedCount of $($rootPackageFolders.Count) driver packages to install.wim."
|
||||
& $Logger 'Committing the driver-only install.wim change...'
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Unmount-Image', "/MountDir:$mountDir", '/Commit') -Operation 'commit' | Out-Null
|
||||
if (-not $failedDriverFolder) {
|
||||
break
|
||||
}
|
||||
|
||||
& $Logger "Discarding the potentially partial install.wim mount before continuing without '$driverName'."
|
||||
try {
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Unmount-Image', "/MountDir:$mountDir", '/Discard') -Operation 'discard' | Out-Null
|
||||
$imageMounted = $false
|
||||
|
||||
$metadataAfter = Get-WinUtilISOWimMetadata -ImagePath $InstallImagePath -Index $InstallImageIndex
|
||||
Assert-WinUtilISOWimMetadata -Before $metadataBefore -After $metadataAfter
|
||||
& $Logger 'Driver injection complete; install.wim metadata validation passed.'
|
||||
$DriversInjected.Value = $true
|
||||
} catch {
|
||||
throw "Failed to discard the potentially partial install.wim mount after driver package '$driverName' failed: $_"
|
||||
}
|
||||
|
||||
$remainingDriverFolders = @($remainingDriverFolders | Where-Object { $_ -ne $failedDriverFolder })
|
||||
}
|
||||
|
||||
$addedCount = $remainingDriverFolders.Count
|
||||
if ($addedCount -eq 0) {
|
||||
# Boot-storage drivers staged for WinPE remain available to Windows Setup.
|
||||
& $Logger "Warning: none of the $($rootPackageFolders.Count) exported driver packages could be added; continuing with an unmodified install.wim."
|
||||
} else {
|
||||
& $Logger "Added $addedCount of $($rootPackageFolders.Count) driver packages to install.wim."
|
||||
& $Logger 'Committing the driver-only install.wim change...'
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Unmount-Image', "/MountDir:$mountDir", '/Commit') -Operation 'commit' | Out-Null
|
||||
$imageMounted = $false
|
||||
|
||||
$metadataAfter = Get-WinUtilISOWimMetadata -ImagePath $InstallImagePath -Index $InstallImageIndex
|
||||
Assert-WinUtilISOWimMetadata -Before $metadataBefore -After $metadataAfter
|
||||
& $Logger 'Driver injection complete; install.wim metadata validation passed.'
|
||||
$DriversInjected.Value = $true
|
||||
}
|
||||
} finally {
|
||||
if ($imageMounted -or (Test-WinUtilISOMountedImage -Path $mountDir)) {
|
||||
|
||||
@@ -39,7 +39,7 @@ function Invoke-WinUtilISOWriteUSB {
|
||||
$usbDisks = $sync["Win11ISOUSBDisks"]
|
||||
|
||||
if (-not $contentsDir -or -not (Test-Path $contentsDir)) {
|
||||
Show-WinUtilMessage -Message "No modified ISO content found. Please complete Steps 1-3 first." -Title "Not Ready" -Button "OK" -Icon "Warning" | Out-Null
|
||||
Show-WinUtilMessage -Message "No modified ISO content found. Please run the modification step first." -Title "Not Ready" -Button "OK" -Icon "Warning" | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ function Invoke-WinUtilISOWriteUSB {
|
||||
param($DiskNumber, $contentsDir)
|
||||
|
||||
Invoke-WPFUIThread -ScriptBlock { $sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $false }
|
||||
Set-WinUtilISOStep -Step "Working" -Label "Writing the USB drive"
|
||||
|
||||
try {
|
||||
Write-WinUtilISOLog "Starting USB write to Disk $DiskNumber..."
|
||||
@@ -233,14 +234,29 @@ function Invoke-WinUtilISOWriteUSB {
|
||||
Step-WinUtilJob -Status "USB write complete" -Percent 100
|
||||
Write-WinUtilISOLog "USB drive is ready for use."
|
||||
|
||||
Invoke-WPFUIThread -Parameters @{ DiskNumber = $DiskNumber } -ScriptBlock {
|
||||
param($DiskNumber)
|
||||
|
||||
$sync["WPFWin11ISODoneLabel"].Text = "Disk $DiskNumber is ready to boot from."
|
||||
$sync["WPFWin11ISODonePanel"].Visibility = "Visible"
|
||||
}
|
||||
Set-WinUtilISOStep -Step "Output"
|
||||
Show-WinUtilMessage -Message "USB drive created successfully!`n`nYou can now boot from this drive to install Windows 11." -Title "USB Ready" -Button "OK" -Icon "Info" | Out-Null
|
||||
} catch {
|
||||
Write-WinUtilISOLog -Level "ERROR" -Message "USB write failed: $_"
|
||||
$_.Exception.Data["WinUtilErrorReported"] = $true
|
||||
Set-WinUtilISOStep -Step "Output"
|
||||
Show-WinUtilMessage -Message "USB write failed:`n`n$_" -Title "USB Write Error" -Button "OK" -Icon "Error" | Out-Null
|
||||
throw
|
||||
} finally {
|
||||
Invoke-WPFUIThread -ScriptBlock { $sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $true }
|
||||
Invoke-WPFUIThread -ScriptBlock {
|
||||
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $true
|
||||
|
||||
# Cancellation skips catch, so the working page can still be up here
|
||||
if ($sync["WPFWin11ISOWorkingSection"].IsSelected) {
|
||||
Set-WinUtilISOStep -Step "Output"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,6 +512,14 @@ Version : <a href="https://github.com/ChrisTitusTech/winutil/releases/tag/$($sy
|
||||
Invoke-WinUtilISOCleanAndReset
|
||||
})
|
||||
|
||||
$sync["WPFWin11ISOBackButton"].Add_Click({
|
||||
$sync["WPFWin11ISOSelectSection"].IsSelected = $true
|
||||
})
|
||||
|
||||
$sync["WPFWin11ISOForwardButton"].Add_Click({
|
||||
$sync["WPFWin11ISOModifySection"].IsSelected = $true
|
||||
})
|
||||
|
||||
$buildClock.Stop()
|
||||
Write-WinUtilLog -Level "DEBUG" -Component "UI" -Message "Interface built in $($buildClock.ElapsedMilliseconds) ms, showing the window."
|
||||
Write-WinUtilTimingSummary -Scope "UI" -TotalMilliseconds $buildClock.ElapsedMilliseconds
|
||||
|
||||
+23
-23
@@ -53,22 +53,22 @@ Describe "Set-WinUtilDNS" {
|
||||
Secondary6 = "2606:4700:4700::1001"
|
||||
DohTemplate = "https://cloudflare-dns.com/dns-query"
|
||||
}
|
||||
Mullvad = [pscustomobject]@{
|
||||
Primary = "194.242.2.2"
|
||||
Secondary = "194.242.2.3"
|
||||
Primary6 = "2a07:e340::2"
|
||||
Secondary6 = "2a07:e340::3"
|
||||
DohOnlyProvider = [pscustomobject]@{
|
||||
Primary = "192.0.2.1"
|
||||
Secondary = "192.0.2.2"
|
||||
Primary6 = "2001:db8::1"
|
||||
Secondary6 = "2001:db8::2"
|
||||
DohOnly = $true
|
||||
DohTemplate = "https://dns.mullvad.net/dns-query"
|
||||
SecondaryDohTemplate = "https://adblock.dns.mullvad.net/dns-query"
|
||||
DohTemplate = "https://doh.example.com/dns-query"
|
||||
SecondaryDohTemplate = "https://secondary.doh.example.com/dns-query"
|
||||
}
|
||||
MullvadNoSecondary = [pscustomobject]@{
|
||||
Primary = "194.242.2.2"
|
||||
DohOnlyNoSecondary = [pscustomobject]@{
|
||||
Primary = "192.0.2.1"
|
||||
Secondary = ""
|
||||
Primary6 = "2a07:e340::2"
|
||||
Primary6 = "2001:db8::1"
|
||||
Secondary6 = ""
|
||||
DohOnly = $true
|
||||
DohTemplate = "https://dns.mullvad.net/dns-query"
|
||||
DohTemplate = "https://doh.example.com/dns-query"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,58 +151,58 @@ Describe "Set-WinUtilDNS" {
|
||||
}
|
||||
|
||||
It "filters empty DNS server addresses" {
|
||||
Set-WinUtilDNS -DNSProvider "MullvadNoSecondary"
|
||||
Set-WinUtilDNS -DNSProvider "DohOnlyNoSecondary"
|
||||
|
||||
Should -Invoke -CommandName Set-DnsClientServerAddress -Times 1 -Exactly -ParameterFilter {
|
||||
$InterfaceIndex -eq 7 -and
|
||||
$ServerAddresses.Count -eq 1 -and
|
||||
$ServerAddresses[0] -eq "194.242.2.2"
|
||||
$ServerAddresses[0] -eq "192.0.2.1"
|
||||
}
|
||||
Should -Invoke -CommandName Set-DnsClientServerAddress -Times 1 -Exactly -ParameterFilter {
|
||||
$InterfaceIndex -eq 7 -and
|
||||
$ServerAddresses.Count -eq 1 -and
|
||||
$ServerAddresses[0] -eq "2a07:e340::2"
|
||||
$ServerAddresses[0] -eq "2001:db8::1"
|
||||
}
|
||||
Should -Invoke -CommandName Add-DnsClientDohServerAddress -Times 2 -Exactly
|
||||
}
|
||||
|
||||
It "applies the matching DoH template to secondary resolvers" {
|
||||
Set-WinUtilDNS -DNSProvider "Mullvad"
|
||||
Set-WinUtilDNS -DNSProvider "DohOnlyProvider"
|
||||
|
||||
Should -Invoke -CommandName Add-DnsClientDohServerAddress -Times 2 -Exactly -ParameterFilter {
|
||||
$ServerAddress -in @("194.242.2.2", "2a07:e340::2") -and
|
||||
$DohTemplate -eq "https://dns.mullvad.net/dns-query"
|
||||
$ServerAddress -in @("192.0.2.1", "2001:db8::1") -and
|
||||
$DohTemplate -eq "https://doh.example.com/dns-query"
|
||||
}
|
||||
Should -Invoke -CommandName Add-DnsClientDohServerAddress -Times 2 -Exactly -ParameterFilter {
|
||||
$ServerAddress -in @("194.242.2.3", "2a07:e340::3") -and
|
||||
$DohTemplate -eq "https://adblock.dns.mullvad.net/dns-query"
|
||||
$ServerAddress -in @("192.0.2.2", "2001:db8::2") -and
|
||||
$DohTemplate -eq "https://secondary.doh.example.com/dns-query"
|
||||
}
|
||||
}
|
||||
|
||||
It "does not apply a DoH-only provider when DoH is unsupported" {
|
||||
Mock Get-Command { return $null } -ParameterFilter { $Name -eq "Add-DnsClientDohServerAddress" }
|
||||
|
||||
$result = Set-WinUtilDNS -DNSProvider "Mullvad"
|
||||
$result = Set-WinUtilDNS -DNSProvider "DohOnlyProvider"
|
||||
|
||||
$result | Should -BeFalse
|
||||
Should -Invoke -CommandName Set-DnsClientServerAddress -Times 0 -Exactly
|
||||
Should -Invoke -CommandName Add-DnsClientDohServerAddress -Times 0 -Exactly
|
||||
Should -Invoke -CommandName Write-Warning -Times 1 -Exactly -ParameterFilter {
|
||||
$Message -eq "DNS provider Mullvad requires DNS over HTTPS, which is not supported on this system."
|
||||
$Message -eq "DNS provider DohOnlyProvider requires DNS over HTTPS, which is not supported on this system."
|
||||
}
|
||||
}
|
||||
|
||||
It "does not change adapter DNS when DoH registration fails" {
|
||||
Mock Add-DnsClientDohServerAddress { throw "DoH registration failed" }
|
||||
|
||||
$result = Set-WinUtilDNS -DNSProvider "Mullvad"
|
||||
$result = Set-WinUtilDNS -DNSProvider "DohOnlyProvider"
|
||||
|
||||
$result | Should -BeFalse
|
||||
Should -Invoke -CommandName Set-DnsClientServerAddress -Times 0 -Exactly
|
||||
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
|
||||
$Level -eq "ERROR" -and
|
||||
$Component -eq "DNS" -and
|
||||
$Message -like "DNS provider Mullvad was not completed: *"
|
||||
$Message -like "DNS provider DohOnlyProvider was not completed: *"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -306,7 +306,26 @@ Describe "Win11 Creator setup media" {
|
||||
$script:mountAndVerifyFunction | Should -Match ([regex]::Escape('$verified = $true'))
|
||||
$script:mountAndVerifyFunction | Should -Match ([regex]::Escape('Dismount-DiskImage -ImagePath $isoPath -ErrorAction Stop'))
|
||||
$script:mountAndVerifyFunction | Should -Match ([regex]::Escape('$sync["Win11ISOImagePath"] = $null'))
|
||||
$script:mountAndVerifyFunction.IndexOf('Dismount-DiskImage') | Should -BeGreaterThan $script:mountAndVerifyFunction.IndexOf('finally')
|
||||
$script:mountAndVerifyFunction.IndexOf('Dismount-DiskImage -ImagePath $isoPath') | Should -BeGreaterThan $script:mountAndVerifyFunction.IndexOf('finally')
|
||||
}
|
||||
|
||||
It "dismounts a previously verified ISO before mounting a replacement" {
|
||||
$script:mountAndVerifyFunction | Should -Match ([regex]::Escape('$previous = $sync["Win11ISOImagePath"]'))
|
||||
$script:mountAndVerifyFunction | Should -Match ([regex]::Escape('Dismount-DiskImage -ImagePath $previous -ErrorAction Stop'))
|
||||
|
||||
$script:mountAndVerifyFunction.IndexOf('Dismount-DiskImage -ImagePath $previous') |
|
||||
Should -BeLessThan $script:mountAndVerifyFunction.IndexOf('$sync["Win11ISOImagePath"] = $null')
|
||||
}
|
||||
|
||||
It "keeps the stored path and restores the interface when the previous ISO will not dismount" {
|
||||
$dismountIndex = $script:mountAndVerifyFunction.IndexOf('Dismount-DiskImage -ImagePath $previous')
|
||||
$resetIndex = $script:mountAndVerifyFunction.IndexOf('$sync["Win11ISOImagePath"] = $null')
|
||||
|
||||
$throwIndex = $script:mountAndVerifyFunction.IndexOf('throw $stillMounted')
|
||||
$throwIndex | Should -BeGreaterThan $dismountIndex
|
||||
$throwIndex | Should -BeLessThan $resetIndex
|
||||
|
||||
$script:mountAndVerifyFunction.IndexOf('try {') | Should -BeLessThan $dismountIndex
|
||||
}
|
||||
|
||||
It "keeps ISO cleanup in finally so stopping modification cannot bypass it" {
|
||||
@@ -454,11 +473,11 @@ Describe "Win11 Creator setup media" {
|
||||
Test-Path (Join-Path $winpeDriverRoot 'net_pkg\network.inf') | Should -BeFalse
|
||||
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Mount-Image' }).Count | Should -Be 1
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Add-Driver' }).Count | Should -Be $script:expectedRootPackages
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Add-Driver' }).Count | Should -Be 9
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Unmount-Image\|.*\|/Commit' }).Count | Should -Be 1
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Get-WimInfo' }).Count | Should -Be 2
|
||||
($script:dismCalls -join "`n") | Should -Not -Match '/Cleanup-Image|/Export-Image'
|
||||
($logs -join '|') | Should -Match "Added $script:expectedRootPackages of $script:expectedRootPackages driver packages"
|
||||
($logs -join '|') | Should -Match "Added 9 of 9 driver packages"
|
||||
|
||||
# nested_pkg holds INFs at its root and under x64. The root add already recurses
|
||||
# into x64, so the subfolder must not get an /Add-Driver call of its own.
|
||||
@@ -469,14 +488,13 @@ Describe "Win11 Creator setup media" {
|
||||
$nsMgr = New-Object System.Xml.XmlNamespaceManager($answerFile.NameTable)
|
||||
$nsMgr.AddNamespace('sg', 'https://schneegans.de/windows/unattend-generator/')
|
||||
$answerFile.SelectSingleNode('//sg:File[@path="C:\Windows\Setup\Scripts\WinUtil-InstallDrivers.ps1"]', $nsMgr) | Should -BeNullOrEmpty
|
||||
($logs -join '|') | Should -Match 'Exported 9 of 11 driver packages \(2 staged for WinPE, 2 excluded\)'
|
||||
($logs -join '|') | Should -Match "Excluding extension-class driver package '.*hdx_asusext_apot_g5-tse.*'"
|
||||
($logs -join '|') | Should -Match 'Exported 10 of 11 driver packages \(2 staged for WinPE, 1 excluded\)'
|
||||
($logs -join '|') | Should -Match "Excluding stale duplicate driver package '.*ntprint\.inf_x86_7426e1b60aa62272' \(DriverVer 1/1/2023,10\.0\.26100\.8875\) superseded by '.*ntprint\.inf_x86_58e7118cdecb935e' \(DriverVer 6/1/2024,10\.0\.26100\.9168\)"
|
||||
($logs -join '|') | Should -Match 'install.wim metadata validation passed'
|
||||
($logs -join '|') | Should -Match 'DISM mount completed.'
|
||||
($logs -join '|') | Should -Not -Match '100.0%'
|
||||
|
||||
$script:exportRootAtAddDriver | Should -Not -Contain (Join-Path $script:driverExportRoot 'hdx_asusext_apot_g5-tse.inf_amd64_aabbccddeeff0011')
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'hdx_asusext_apot_g5-tse.inf_amd64_aabbccddeeff0011')
|
||||
$script:exportRootAtAddDriver | Should -Not -Contain (Join-Path $script:driverExportRoot 'ntprint.inf_x86_7426e1b60aa62272')
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'ntprint.inf_x86_58e7118cdecb935e')
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'system_pkg')
|
||||
@@ -487,8 +505,8 @@ Describe "Win11 Creator setup media" {
|
||||
}
|
||||
}
|
||||
|
||||
It "excludes Class=Extension driver packages from Add-Driver regardless of vendor or case" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoExtensionExclude_$([guid]::NewGuid())"
|
||||
It "injects Class=Extension driver packages like any other export" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoExtensionInject_$([guid]::NewGuid())"
|
||||
$installWim = Join-Path $contentRoot 'sources\install.wim'
|
||||
$template = Get-Content -Path $script:autoUnattendPath -Raw
|
||||
$logs = [System.Collections.Generic.List[string]]::new()
|
||||
@@ -509,15 +527,12 @@ Describe "Win11 Creator setup media" {
|
||||
$logs.Add([string]$message)
|
||||
}
|
||||
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Add-Driver' }).Count | Should -Be 1
|
||||
($logs -join '|') | Should -Match 'Exported 1 of 3 driver packages \(0 staged for WinPE, 2 excluded\)'
|
||||
($logs -join '|') | Should -Match "Excluding extension-class driver package '.*ext_pkg_lower'"
|
||||
($logs -join '|') | Should -Match "Excluding extension-class driver package '.*ext_pkg_quoted'"
|
||||
($logs -join '|') | Should -Not -Match "Excluding extension-class driver package '.*net_pkg'"
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Add-Driver' }).Count | Should -Be 3
|
||||
($logs -join '|') | Should -Match 'Exported 3 of 3 driver packages \(0 staged for WinPE, 0 excluded\)'
|
||||
$driversInjected.Value | Should -BeTrue
|
||||
|
||||
$script:exportRootAtAddDriver | Should -Not -Contain (Join-Path $script:driverExportRoot 'ext_pkg_lower')
|
||||
$script:exportRootAtAddDriver | Should -Not -Contain (Join-Path $script:driverExportRoot 'ext_pkg_quoted')
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'ext_pkg_lower')
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'ext_pkg_quoted')
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'net_pkg')
|
||||
} finally {
|
||||
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
|
||||
@@ -525,15 +540,16 @@ Describe "Win11 Creator setup media" {
|
||||
}
|
||||
}
|
||||
|
||||
It "preserves a retained nested package when its parent package is excluded" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoNestedRetained_$([guid]::NewGuid())"
|
||||
It "keeps a stale duplicate folder that contains a retained nested package" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoStaleNestedRetained_$([guid]::NewGuid())"
|
||||
$installWim = Join-Path $contentRoot 'sources\install.wim'
|
||||
$template = Get-Content -Path $script:autoUnattendPath -Raw
|
||||
$logs = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
New-WinUtilDriverExportHarness -Fixtures @(
|
||||
@{ Path = 'parent_pkg'; Name = 'extension.inf'; Class = 'Extension' },
|
||||
@{ Path = 'parent_pkg\retained_child'; Name = 'network.inf'; Class = 'Net' }
|
||||
@{ Path = 'main.inf_amd64_11111111aaaaaaaa'; Name = 'main.inf'; Class = 'Net'; DriverVer = '1/1/2023,1.0.0.0' },
|
||||
@{ Path = 'main.inf_amd64_22222222bbbbbbbb'; Name = 'main.inf'; Class = 'Net'; DriverVer = '6/1/2024,2.0.0.0' },
|
||||
@{ Path = 'main.inf_amd64_11111111aaaaaaaa\retained_child'; Name = 'network.inf'; Class = 'Net' }
|
||||
)
|
||||
|
||||
try {
|
||||
@@ -546,14 +562,12 @@ Describe "Win11 Creator setup media" {
|
||||
$logs.Add([string]$message)
|
||||
}
|
||||
|
||||
$addDriverCalls = @($script:dismCalls | Where-Object { $_ -match '/Add-Driver' })
|
||||
$addDriverCalls.Count | Should -Be 1
|
||||
$addDriverCalls[0] | Should -Match ([regex]::Escape('parent_pkg\retained_child'))
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'parent_pkg')
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'parent_pkg\retained_child')
|
||||
$script:exportedInfsAtAddDriver | Should -Not -Contain (Join-Path $script:driverExportRoot 'parent_pkg\extension.inf')
|
||||
$script:exportedInfsAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'parent_pkg\retained_child\network.inf')
|
||||
($logs -join '|') | Should -Match "Keeping excluded driver package directory '.*parent_pkg' because it contains a retained nested package"
|
||||
($logs -join '|') | Should -Match "Excluding stale duplicate driver package '.*main\.inf_amd64_11111111aaaaaaaa' \(DriverVer 1/1/2023,1\.0\.0\.0\) superseded by '.*main\.inf_amd64_22222222bbbbbbbb' \(DriverVer 6/1/2024,2\.0\.0\.0\)"
|
||||
($logs -join '|') | Should -Match "Keeping excluded driver package directory '.*main\.inf_amd64_11111111aaaaaaaa' because it contains a retained nested package"
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Add-Driver' }).Count | Should -Be 2
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'main.inf_amd64_11111111aaaaaaaa')
|
||||
$script:exportedInfsAtAddDriver | Should -Not -Contain (Join-Path $script:driverExportRoot 'main.inf_amd64_11111111aaaaaaaa\main.inf')
|
||||
$script:exportedInfsAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'main.inf_amd64_11111111aaaaaaaa\retained_child\network.inf')
|
||||
$driversInjected.Value | Should -BeTrue
|
||||
} finally {
|
||||
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
|
||||
@@ -561,15 +575,16 @@ Describe "Win11 Creator setup media" {
|
||||
}
|
||||
}
|
||||
|
||||
It "removes an excluded nested INF before recursively adding its retained ancestor" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoNestedExcluded_$([guid]::NewGuid())"
|
||||
It "removes a stale duplicate INF before recursively adding its retained ancestor" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoStaleNestedExcluded_$([guid]::NewGuid())"
|
||||
$installWim = Join-Path $contentRoot 'sources\install.wim'
|
||||
$template = Get-Content -Path $script:autoUnattendPath -Raw
|
||||
|
||||
New-WinUtilDriverExportHarness -Fixtures @(
|
||||
@{ Path = 'root_pkg'; Name = 'root.inf'; Class = 'Net' },
|
||||
@{ Path = 'root_pkg\excluded_child'; Name = 'extension.inf'; Class = 'Extension' },
|
||||
@{ Path = 'root_pkg\excluded_child\retained_grandchild'; Name = 'storage.inf'; Class = 'SCSIAdapter' }
|
||||
@{ Path = 'root_pkg\dup.inf_amd64_11111111aaaaaaaa'; Name = 'dup.inf'; Class = 'Media'; DriverVer = '1/1/2020,1.0.0.0' },
|
||||
@{ Path = 'root_pkg\dup.inf_amd64_22222222bbbbbbbb'; Name = 'dup.inf'; Class = 'Media'; DriverVer = '6/1/2024,2.0.0.0' },
|
||||
@{ Path = 'root_pkg\dup.inf_amd64_11111111aaaaaaaa\retained_grandchild'; Name = 'storage.inf'; Class = 'SCSIAdapter' }
|
||||
)
|
||||
|
||||
try {
|
||||
@@ -581,43 +596,11 @@ Describe "Win11 Creator setup media" {
|
||||
$addDriverCalls = @($script:dismCalls | Where-Object { $_ -match '/Add-Driver' })
|
||||
$addDriverCalls.Count | Should -Be 1
|
||||
$addDriverCalls[0] | Should -Match ([regex]::Escape('/Driver:' + (Join-Path $script:driverExportRoot 'root_pkg')))
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'root_pkg\excluded_child')
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'root_pkg\excluded_child\retained_grandchild')
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'root_pkg\dup.inf_amd64_11111111aaaaaaaa')
|
||||
$script:exportedInfsAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'root_pkg\root.inf')
|
||||
$script:exportedInfsAtAddDriver | Should -Not -Contain (Join-Path $script:driverExportRoot 'root_pkg\excluded_child\extension.inf')
|
||||
$script:exportedInfsAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'root_pkg\excluded_child\retained_grandchild\storage.inf')
|
||||
} finally {
|
||||
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $contentRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
It "validates WIM metadata and reports no injection when every package is excluded" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoAllExcluded_$([guid]::NewGuid())"
|
||||
$installWim = Join-Path $contentRoot 'sources\install.wim'
|
||||
$template = Get-Content -Path $script:autoUnattendPath -Raw
|
||||
$logs = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
New-WinUtilDriverExportHarness -Fixtures @(
|
||||
@{ Path = 'ext_pkg_a'; Name = 'a.inf'; Class = 'Extension' },
|
||||
@{ Path = 'ext_pkg_b'; Name = 'b.inf'; Class = 'Extension' }
|
||||
)
|
||||
|
||||
try {
|
||||
New-Item -Path (Split-Path $installWim -Parent) -ItemType Directory -Force | Out-Null
|
||||
Set-Content -Path $installWim -Value 'mock-wim'
|
||||
. $script:isoScriptPath
|
||||
$driversInjected = [ref]$true
|
||||
Invoke-WinUtilISOScript -ISOContentsDir $contentRoot -AutoUnattendXml $template -InjectCurrentSystemDrivers $true -InstallImagePath $installWim -InstallImageIndex 6 -InstallEditionId 'Professional' -DriversInjected $driversInjected -Log {
|
||||
param($message)
|
||||
$logs.Add([string]$message)
|
||||
}
|
||||
|
||||
$driversInjected.Value | Should -BeFalse
|
||||
($logs -join '|') | Should -Match 'No drivers found to inject: every exported package was excluded'
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Get-WimInfo' }).Count | Should -Be 1
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Mount-Image' }).Count | Should -Be 0
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Add-Driver' }).Count | Should -Be 0
|
||||
$script:exportedInfsAtAddDriver | Should -Not -Contain (Join-Path $script:driverExportRoot 'root_pkg\dup.inf_amd64_11111111aaaaaaaa\dup.inf')
|
||||
$script:exportedInfsAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'root_pkg\dup.inf_amd64_11111111aaaaaaaa\retained_grandchild\storage.inf')
|
||||
$script:exportedInfsAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'root_pkg\dup.inf_amd64_22222222bbbbbbbb\dup.inf')
|
||||
} finally {
|
||||
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $contentRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
|
||||
@@ -241,12 +241,19 @@ Describe "XAML document" {
|
||||
"WPFWin11ISOBrowseButton",
|
||||
"WPFWin11ISOFileInfo",
|
||||
"WPFWin11ISODownloadLink",
|
||||
"WPFWin11ISOMountSection",
|
||||
"WPFWin11ISOSteps",
|
||||
"WPFWin11ISOBackButton",
|
||||
"WPFWin11ISOForwardButton",
|
||||
"WPFWin11ISOWorkingSection",
|
||||
"WPFWin11ISOWorkingLabel",
|
||||
"WPFWin11ISOWorkingSpinner",
|
||||
"WPFWin11ISODonePanel",
|
||||
"WPFWin11ISODoneLabel",
|
||||
"WPFWin11ISOMountButton",
|
||||
"WPFWin11ISOInjectDrivers",
|
||||
"WPFWin11ISOVerifyResultPanel",
|
||||
"WPFWin11ISOMountDriveLetter",
|
||||
"WPFWin11ISOArchLabel",
|
||||
"WPFWin11ISOImageFile",
|
||||
"WPFWin11ISOEditionComboBox",
|
||||
"WPFWin11ISOModifySection",
|
||||
"WPFWin11ISOModifyButton",
|
||||
|
||||
+580
-292
@@ -305,12 +305,14 @@
|
||||
AllowsTransparency="True"
|
||||
PopupAnimation="Slide">
|
||||
<Border Name="DropDownBorder"
|
||||
MinWidth="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<ScrollViewer>
|
||||
<ItemsPresenter HorizontalAlignment="Left" VerticalAlignment="Center" Margin="4,2"/>
|
||||
<!-- Stretched, so an item's highlight spans the popup rather than just its text -->
|
||||
<ItemsPresenter HorizontalAlignment="Stretch" Margin="4,2"/>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Popup>
|
||||
@@ -320,8 +322,11 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="ComboBoxItem">
|
||||
<Setter Property="Background" Value="{DynamicResource ComboBoxBackgroundColor}"/>
|
||||
<!-- Transparent rather than its own colour: the item used to paint one background and
|
||||
the popup another, which read as a stack of labels on a mismatched sheet -->
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource ComboBoxForegroundColor}"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Padding" Value="6,3"/>
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
@@ -1600,311 +1605,594 @@
|
||||
</TabItem>
|
||||
<TabItem Header="Win11ISO" Visibility="Collapsed" Name="WPFTab5">
|
||||
<Grid Name="Win11ISOPanel" Margin="{DynamicResource TabContentMargin}" Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/> <!-- Steps 1-4 -->
|
||||
<RowDefinition Height="*"/> <!-- Log / Status -->
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Steps 1-4 -->
|
||||
<StackPanel Grid.Row="0">
|
||||
<Border Grid.Column="0" Style="{StaticResource BorderStyle}" Padding="20,16">
|
||||
<Grid>
|
||||
<!-- The lopsided weight is what makes the middle column min(available, 620)
|
||||
rather than a third of the card -->
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="1000*" MaxWidth="620"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Name="WPFWin11ISOBackButton"
|
||||
Grid.Column="0"
|
||||
Margin="0,0,10,0"
|
||||
Content=""
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
FontSize="{DynamicResource Win11StepTitleFontSize}"
|
||||
MinWidth="44" MinHeight="72"
|
||||
Background="Transparent"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Panel.ZIndex="1"
|
||||
ToolTip="Back to the previous step">
|
||||
<Button.Style>
|
||||
<Style TargetType="Button" BasedOn="{StaticResource HoverButtonStyle}">
|
||||
<Setter Property="Visibility" Value="Hidden"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSelected, ElementName=WPFWin11ISOModifySection}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
<Button Name="WPFWin11ISOForwardButton"
|
||||
Grid.Column="2"
|
||||
Margin="10,0,0,0"
|
||||
Content=""
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
FontSize="{DynamicResource Win11StepTitleFontSize}"
|
||||
MinWidth="44" MinHeight="72"
|
||||
Background="Transparent"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Center"
|
||||
Panel.ZIndex="1"
|
||||
ToolTip="Forward to the next step">
|
||||
<Button.Style>
|
||||
<Style TargetType="Button" BasedOn="{StaticResource HoverButtonStyle}">
|
||||
<Setter Property="Visibility" Value="Hidden"/>
|
||||
<Style.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, ElementName=WPFWin11ISOSelectSection}" Value="True"/>
|
||||
<Condition Binding="{Binding IsEnabled, ElementName=WPFWin11ISOModifySection}" Value="True"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
<TabControl Name="WPFWin11ISOSteps"
|
||||
Grid.Column="0" Grid.ColumnSpan="3"
|
||||
Background="Transparent"
|
||||
BorderThickness="0">
|
||||
<TabControl.Resources>
|
||||
<!-- Text colours: the implicit TextBlock style would paint anything unstyled
|
||||
cyan on a label background, so every text block here carries a style. -->
|
||||
<Style x:Key="Win11StepHeaderText" TargetType="TextBlock">
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground"
|
||||
Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11ButtonText" TargetType="TextBlock" BasedOn="{StaticResource Win11StepHeaderText}">
|
||||
<Setter Property="FontSize" Value="{DynamicResource ButtonFontSize}"/>
|
||||
<Setter Property="Foreground"
|
||||
Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11StepIcon" TargetType="TextBlock" BasedOn="{StaticResource Win11StepHeaderText}">
|
||||
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"/>
|
||||
<Setter Property="FontSize" Value="{DynamicResource IconFontSize}"/>
|
||||
<Setter Property="Margin" Value="0,0,8,0"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11ButtonIcon" TargetType="TextBlock" BasedOn="{StaticResource Win11ButtonText}">
|
||||
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"/>
|
||||
<Setter Property="FontSize" Value="{DynamicResource IconFontSize}"/>
|
||||
<Setter Property="Margin" Value="0,0,10,0"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TabItem">
|
||||
<Setter Property="FontSize" Value="{DynamicResource FontSize}"/>
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource ToggleButtonOffColor}"/>
|
||||
<Setter Property="HeaderTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Cursor="Hand">
|
||||
<TextBlock Style="{StaticResource Win11StepIcon}"
|
||||
Text="{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}"/>
|
||||
<TextBlock Style="{StaticResource Win11StepHeaderText}" Text="{Binding}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TabItem">
|
||||
<Border Background="Transparent" Padding="0,0,32,0">
|
||||
<ContentPresenter ContentSource="Header" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter Property="Foreground" Value="{DynamicResource MainForegroundColor}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Opacity" Value="0.45"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style TargetType="TabControl">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TabControl">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TabPanel Grid.Row="0"
|
||||
IsItemsHost="True"
|
||||
Background="Transparent"
|
||||
Margin="0,0,0,10"/>
|
||||
<Border Grid.Row="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="0,1,0,0"
|
||||
Padding="0,10,0,0">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<Grid MinHeight="{Binding ViewportHeight, RelativeSource={RelativeSource AncestorType={x:Type ScrollViewer}}}">
|
||||
<ContentPresenter ContentSource="SelectedContent"/>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="Win11StepColumn" TargetType="StackPanel">
|
||||
<Setter Property="MaxWidth" Value="620"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11StepHero" TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"/>
|
||||
<Setter Property="FontSize" Value="{DynamicResource Win11StepHeroFontSize}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource LabelboxForegroundColor}"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="Margin" Value="0,0,0,14"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11StepTitle" TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="{DynamicResource Win11StepTitleFontSize}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource LabelboxForegroundColor}"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="TextAlignment" Value="Center"/>
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
<Setter Property="Margin" Value="0,0,0,6"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11StepBody" TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="{DynamicResource FontSize}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource MainForegroundColor}"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
<Setter Property="Margin" Value="0,0,0,10"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11StepSubtitle" TargetType="TextBlock" BasedOn="{StaticResource Win11StepBody}">
|
||||
<Setter Property="Opacity" Value="0.75"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="TextAlignment" Value="Center"/>
|
||||
<Setter Property="MaxWidth" Value="520"/>
|
||||
<Setter Property="Margin" Value="0,0,0,20"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11StepLabel" TargetType="TextBlock" BasedOn="{StaticResource Win11StepBody}">
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource LabelboxForegroundColor}"/>
|
||||
<Setter Property="Margin" Value="0,0,0,6"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11StepValue" TargetType="TextBlock" BasedOn="{StaticResource Win11StepBody}">
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11StepCaption" TargetType="TextBlock" BasedOn="{StaticResource Win11StepBody}">
|
||||
<Setter Property="Opacity" Value="0.75"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Margin" Value="0,0,6,0"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="Win11StepButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Height" Value="34"/>
|
||||
<Setter Property="Padding" Value="16,0"/>
|
||||
<!-- The implicit style pins Width to ButtonWidth, which clips the longer labels -->
|
||||
<Setter Property="Width" Value="Auto"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource Win11ButtonIcon}"
|
||||
Text="{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"/>
|
||||
<TextBlock Style="{StaticResource Win11ButtonText}" Text="{Binding}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="Win11InfoPanel" TargetType="Border">
|
||||
<Setter Property="Background" Value="{DynamicResource MainBackgroundColor}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource BorderColor}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CornerRadius" Value="5"/>
|
||||
<Setter Property="Padding" Value="16"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11FormPanel" TargetType="Border" BasedOn="{StaticResource Win11InfoPanel}">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource LabelboxForegroundColor}"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11SummaryIcon" TargetType="TextBlock" BasedOn="{StaticResource Win11StepIcon}">
|
||||
<Setter Property="Foreground" Value="{DynamicResource LabelboxForegroundColor}"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="Margin" Value="0,1,12,0"/>
|
||||
</Style>
|
||||
<Style x:Key="Win11SummaryRow" TargetType="StackPanel">
|
||||
<Setter Property="Orientation" Value="Horizontal"/>
|
||||
<Setter Property="Margin" Value="0,0,0,10"/>
|
||||
</Style>
|
||||
</TabControl.Resources>
|
||||
|
||||
<!-- ─── STEP 1 : Select Windows 11 ISO ─────────────── -->
|
||||
<Grid Name="WPFWin11ISOSelectSection" Margin="5" HorizontalAlignment="Left" MinWidth="{DynamicResource ButtonWidth}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TabItem Name="WPFWin11ISOSelectSection" Tag="" Header="1 Select ISO">
|
||||
<StackPanel Style="{StaticResource Win11StepColumn}">
|
||||
<TextBlock Text="" Style="{StaticResource Win11StepHero}"/>
|
||||
<TextBlock Style="{StaticResource Win11StepTitle}" Text="Select your Windows 11 ISO"/>
|
||||
<TextBlock Style="{StaticResource Win11StepSubtitle}">
|
||||
Choose an official Windows 11 ISO downloaded from Microsoft. It will be
|
||||
mounted and checked before anything is changed.
|
||||
</TextBlock>
|
||||
|
||||
<!-- Left: File Selector -->
|
||||
<StackPanel Grid.Column="0" Margin="5,5,15,5">
|
||||
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
|
||||
Foreground="{DynamicResource MainForegroundColor}" Margin="0,0,0,8">
|
||||
Step 1 - Select Windows 11 ISO
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="{DynamicResource FontSize}" Foreground="{DynamicResource MainForegroundColor}"
|
||||
TextWrapping="Wrap" Margin="0,0,0,6">
|
||||
Browse to your locally saved Windows 11 ISO file. Only official ISOs
|
||||
downloaded from Microsoft are supported.
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="{DynamicResource FontSize}" Foreground="{DynamicResource MainForegroundColor}"
|
||||
TextWrapping="Wrap" Margin="0,0,0,12" FontStyle="Italic">
|
||||
<Run FontWeight="Bold">NOTE:</Run> This is only meant for Fresh and New Windows installs.
|
||||
</TextBlock>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Grid.Column="0"
|
||||
Name="WPFWin11ISOPath"
|
||||
IsReadOnly="True"
|
||||
VerticalAlignment="Center"
|
||||
Padding="6,4"
|
||||
Margin="0,0,6,0"
|
||||
Text="No ISO selected..."
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
Background="{DynamicResource MainBackgroundColor}"/>
|
||||
<Button Grid.Column="1"
|
||||
Name="WPFWin11ISOBrowseButton"
|
||||
Content="Browse"
|
||||
Width="Auto" Padding="12,0"
|
||||
Height="{DynamicResource ButtonHeight}"/>
|
||||
</Grid>
|
||||
<TextBlock Name="WPFWin11ISOFileInfo"
|
||||
FontSize="{DynamicResource FontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
Margin="0,8,0,0"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="Collapsed"/>
|
||||
<Border Style="{StaticResource Win11FormPanel}" Margin="0,0,0,16">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource Win11StepLabel}" Text="ISO file"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Grid.Column="0"
|
||||
Name="WPFWin11ISOPath"
|
||||
IsReadOnly="True"
|
||||
VerticalAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Height="34"
|
||||
Padding="10,0"
|
||||
Margin="0,0,12,0"
|
||||
Text="No ISO selected..."
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
Background="{DynamicResource MainBackgroundColor}"/>
|
||||
<Button Grid.Column="1" Name="WPFWin11ISOBrowseButton" MinWidth="130"
|
||||
Style="{StaticResource Win11StepButton}" Tag="" Content="Browse"/>
|
||||
</Grid>
|
||||
<!-- Hidden rather than collapsed so picking an ISO does not shift the page -->
|
||||
<TextBlock Name="WPFWin11ISOFileInfo"
|
||||
Style="{StaticResource Win11StepBody}"
|
||||
Margin="0,10,0,0"
|
||||
Visibility="Hidden"
|
||||
Text="File size:"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Button Name="WPFWin11ISOMountButton" Margin="0,0,0,20"
|
||||
Style="{StaticResource Win11StepButton}" Tag="" Content="Mount & Verify ISO"/>
|
||||
|
||||
<Border Style="{StaticResource Win11InfoPanel}">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource Win11StepLabel}" Foreground="OrangeRed" Margin="0,0,0,10">
|
||||
<Run FontFamily="Segoe MDL2 Assets" Text=""/>
|
||||
<Run Text=" You must use an official Microsoft ISO"/>
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource Win11StepBody}">
|
||||
Third-party, pre-modified, or unofficial images are not supported
|
||||
and may produce broken results. This is only meant for fresh and
|
||||
new Windows installs.
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource Win11StepBody}" Margin="0,0,0,12">
|
||||
On the download page choose Windows 11, your language, and 64-bit (x64).
|
||||
</TextBlock>
|
||||
<Button Name="WPFWin11ISODownloadLink"
|
||||
Style="{StaticResource Win11StepButton}" Tag="" Content="Open Microsoft Download Page"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
<!-- Right: Download guidance -->
|
||||
<Border Grid.Column="1"
|
||||
Background="{DynamicResource MainBackgroundColor}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1" CornerRadius="5"
|
||||
Margin="5" Padding="15">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
|
||||
Foreground="OrangeRed" Margin="0,0,0,10">
|
||||
!!WARNING!! You must use an official Microsoft ISO
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="{DynamicResource FontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
TextWrapping="Wrap" Margin="0,0,0,8">
|
||||
Download the Windows 11 ISO directly from Microsoft.com.
|
||||
Third-party, pre-modified, or unofficial images are not supported
|
||||
and may produce broken results.
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="{DynamicResource FontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
TextWrapping="Wrap" Margin="0,0,0,6">
|
||||
On the Microsoft download page, choose:
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="{DynamicResource FontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
TextWrapping="Wrap" Margin="12,0,0,12">
|
||||
- Edition : Windows 11
|
||||
<LineBreak/>- Language : your preferred language
|
||||
<LineBreak/>- Architecture : 64-bit (x64)
|
||||
</TextBlock>
|
||||
<Button Name="WPFWin11ISODownloadLink"
|
||||
Content="Open Microsoft Download Page"
|
||||
HorizontalAlignment="Left"
|
||||
Width="Auto" Padding="12,0"
|
||||
Height="{DynamicResource ButtonHeight}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- ─── STEP 2 : Mount & Verify ISO ──────────────────── -->
|
||||
<Grid Name="WPFWin11ISOMountSection"
|
||||
Margin="5"
|
||||
Visibility="Collapsed"
|
||||
HorizontalAlignment="Left" MinWidth="{DynamicResource ButtonWidth}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0" Margin="0,0,20,0" VerticalAlignment="Top">
|
||||
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
|
||||
Foreground="{DynamicResource MainForegroundColor}" Margin="0,0,0,8">
|
||||
Step 2 - Mount & Verify ISO
|
||||
<!-- ─── STEP 2 : Modify install.wim ───────────────────── -->
|
||||
<TabItem Name="WPFWin11ISOModifySection" IsEnabled="False" Tag="" Header="2 Modify Image">
|
||||
<StackPanel Style="{StaticResource Win11StepColumn}">
|
||||
<TextBlock Text="" Style="{StaticResource Win11StepHero}"/>
|
||||
<TextBlock Style="{StaticResource Win11StepTitle}" Text="Modify the image"/>
|
||||
<TextBlock Style="{StaticResource Win11StepSubtitle}">
|
||||
Pick what goes into the image, then start. This takes several minutes
|
||||
depending on your hardware.
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="{DynamicResource FontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
TextWrapping="Wrap" Margin="0,0,0,12" MaxWidth="320">
|
||||
Mount the ISO and confirm it contains a valid Windows 11
|
||||
install.wim before any modifications are made.
|
||||
</TextBlock>
|
||||
<Button Name="WPFWin11ISOMountButton"
|
||||
Content="Mount & Verify ISO"
|
||||
HorizontalAlignment="Left"
|
||||
Width="Auto" Padding="12,0"
|
||||
Height="{DynamicResource ButtonHeight}"/>
|
||||
<CheckBox Name="WPFWin11ISOInjectDrivers"
|
||||
Content="Inject current system drivers"
|
||||
FontSize="{DynamicResource FontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
IsChecked="False"
|
||||
Margin="0,8,0,0"
|
||||
ToolTip="Stages boot-storage drivers for Setup and adds all exported drivers to the selected install.wim edition in one DISM pass."/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Verification results panel -->
|
||||
<Border Grid.Column="1"
|
||||
Name="WPFWin11ISOVerifyResultPanel"
|
||||
Background="{DynamicResource MainBackgroundColor}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1" CornerRadius="5"
|
||||
Padding="12" Margin="0,0,0,0"
|
||||
Visibility="Collapsed">
|
||||
<StackPanel>
|
||||
<TextBlock Name="WPFWin11ISOMountDriveLetter"
|
||||
FontSize="{DynamicResource FontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
Margin="0,0,0,4"/>
|
||||
<TextBlock Name="WPFWin11ISOArchLabel"
|
||||
FontSize="{DynamicResource FontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
Margin="0,0,0,4"/>
|
||||
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
Margin="0,6,0,4">
|
||||
Select Edition:
|
||||
</TextBlock>
|
||||
<ComboBox Name="WPFWin11ISOEditionComboBox"
|
||||
FontSize="{DynamicResource FontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
Background="{DynamicResource MainBackgroundColor}"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="0,0,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- ─── STEP 3 : Modify install.wim ───────────────────── -->
|
||||
<StackPanel Name="WPFWin11ISOModifySection"
|
||||
Margin="5"
|
||||
Visibility="Collapsed"
|
||||
HorizontalAlignment="Left" MinWidth="{DynamicResource ButtonWidth}">
|
||||
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
|
||||
Foreground="{DynamicResource MainForegroundColor}" Margin="0,0,0,8">
|
||||
Step 3 - Modify install.wim
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="{DynamicResource FontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
TextWrapping="Wrap" Margin="0,0,0,12">
|
||||
The ISO contents will be extracted to a temporary working directory,
|
||||
install.wim will be modified (components removed, tweaks applied),
|
||||
and the result will be repackaged. This process may take several minutes
|
||||
depending on your hardware.
|
||||
</TextBlock>
|
||||
<Button Name="WPFWin11ISOModifyButton"
|
||||
Content="Run Windows ISO Modification and Creator"
|
||||
HorizontalAlignment="Left"
|
||||
Width="Auto" Padding="12,0"
|
||||
Height="{DynamicResource ButtonHeight}"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- ─── STEP 4 : Output Options ───────────────────────── -->
|
||||
<StackPanel Name="WPFWin11ISOOutputSection"
|
||||
Margin="5"
|
||||
Visibility="Collapsed"
|
||||
HorizontalAlignment="Left" MinWidth="{DynamicResource ButtonWidth}">
|
||||
<!-- Header row: title + Clean & Reset button -->
|
||||
<Grid Margin="0,0,0,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" FontSize="{DynamicResource FontSize}" FontWeight="Bold"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
VerticalAlignment="Center">
|
||||
Step 4 - Output: What would you like to do with the modified image?
|
||||
</TextBlock>
|
||||
<Button Grid.Column="1"
|
||||
Name="WPFWin11ISOCleanResetButton"
|
||||
Content="Clean & Reset"
|
||||
Foreground="OrangeRed"
|
||||
Width="Auto" Padding="12,0"
|
||||
Height="{DynamicResource ButtonHeight}"
|
||||
ToolTip="Delete the temporary working directory and reset the interface back to Step 1"
|
||||
Margin="12,0,0,0"/>
|
||||
</Grid>
|
||||
|
||||
<!-- ── Choice prompt buttons ── -->
|
||||
<Grid Margin="0,0,0,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Grid.Column="0"
|
||||
Name="WPFWin11ISOChooseISOButton"
|
||||
Content="Save as an ISO File"
|
||||
HorizontalAlignment="Stretch"
|
||||
Width="Auto" Padding="12,0"
|
||||
Height="{DynamicResource ButtonHeight}"/>
|
||||
<Button Grid.Column="2"
|
||||
Name="WPFWin11ISOChooseUSBButton"
|
||||
Content="Write Directly to a USB Drive (ERASES DRIVE)"
|
||||
Foreground="OrangeRed"
|
||||
HorizontalAlignment="Stretch"
|
||||
Width="Auto" Padding="12,0"
|
||||
Height="{DynamicResource ButtonHeight}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- ── USB write sub-panel (revealed on USB choice) ── -->
|
||||
<Border Name="WPFWin11ISOOptionUSB"
|
||||
Style="{StaticResource BorderStyle}"
|
||||
Visibility="Collapsed"
|
||||
Margin="0,8,0,0">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="{DynamicResource FontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
TextWrapping="Wrap" Margin="0,0,0,8">
|
||||
<Run FontWeight="Bold" Foreground="OrangeRed">!! All data on the selected USB drive will be permanently erased !!</Run>
|
||||
<LineBreak/>
|
||||
Select a removable USB drive below, then click Erase & Write.
|
||||
</TextBlock>
|
||||
<!-- USB drive selector row -->
|
||||
<Grid Margin="0,0,0,8">
|
||||
<Border Name="WPFWin11ISOVerifyResultPanel"
|
||||
Style="{StaticResource Win11InfoPanel}"
|
||||
Padding="14,10"
|
||||
Margin="0,0,0,14"
|
||||
Visibility="Collapsed">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox Grid.Column="0"
|
||||
Name="WPFWin11ISOUSBDriveComboBox"
|
||||
<TextBlock Grid.Column="1" Text="" Style="{StaticResource Win11SummaryIcon}"
|
||||
VerticalAlignment="Center" Margin="12,0,0,0"/>
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<TextBlock Text="" Style="{StaticResource Win11StepIcon}"/>
|
||||
<TextBlock Style="{StaticResource Win11StepCaption}" Text="Mounted at"/>
|
||||
<TextBlock Name="WPFWin11ISOMountDriveLetter" Style="{StaticResource Win11StepValue}"/>
|
||||
<TextBlock Text="" Style="{StaticResource Win11StepIcon}" Margin="22,0,8,0"/>
|
||||
<TextBlock Style="{StaticResource Win11StepCaption}" Text="Image file"/>
|
||||
<TextBlock Name="WPFWin11ISOImageFile" Style="{StaticResource Win11StepValue}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Border Style="{StaticResource Win11FormPanel}" Margin="0,0,0,16">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource Win11StepLabel}" Text="Windows edition"/>
|
||||
<ComboBox Name="WPFWin11ISOEditionComboBox"
|
||||
FontSize="{DynamicResource FontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
Background="{DynamicResource MainBackgroundColor}"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,6,0"/>
|
||||
<Button Grid.Column="1"
|
||||
Name="WPFWin11ISORefreshUSBButton"
|
||||
Content="Refresh"
|
||||
Width="Auto" Padding="8,0"
|
||||
Height="{DynamicResource ButtonHeight}"/>
|
||||
</Grid>
|
||||
<Button Name="WPFWin11ISOWriteUSBButton"
|
||||
Content="Erase & Write to USB"
|
||||
Foreground="OrangeRed"
|
||||
HorizontalAlignment="Stretch"
|
||||
Width="Auto" Padding="12,0"
|
||||
Height="{DynamicResource ButtonHeight}"
|
||||
Margin="0,0,0,10"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
HorizontalAlignment="Stretch"
|
||||
Height="34"
|
||||
Margin="0,0,0,14"/>
|
||||
<CheckBox Name="WPFWin11ISOInjectDrivers"
|
||||
Content="Inject current system drivers"
|
||||
FontSize="{DynamicResource FontSize}"
|
||||
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."/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Button Name="WPFWin11ISOModifyButton" Margin="0,0,0,20"
|
||||
Style="{StaticResource Win11StepButton}" Tag=""
|
||||
Content="Run Windows ISO Modification and Creator"/>
|
||||
|
||||
<Border Style="{StaticResource Win11InfoPanel}">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource Win11StepLabel}" Margin="0,0,0,12" Text="What this does to the image"/>
|
||||
<StackPanel Style="{StaticResource Win11SummaryRow}">
|
||||
<TextBlock Text="" Style="{StaticResource Win11SummaryIcon}"/>
|
||||
<TextBlock Style="{StaticResource Win11StepBody}" Margin="0"
|
||||
Text="Removes the preinstalled apps and the OneDrive setup"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource Win11SummaryRow}">
|
||||
<TextBlock Text="" Style="{StaticResource Win11SummaryIcon}"/>
|
||||
<TextBlock Style="{StaticResource Win11StepBody}" Margin="0"
|
||||
Text="Bypasses the TPM, Secure Boot, CPU, RAM and storage checks"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource Win11SummaryRow}">
|
||||
<TextBlock Text="" Style="{StaticResource Win11SummaryIcon}"/>
|
||||
<TextBlock Style="{StaticResource Win11StepBody}" Margin="0"
|
||||
Text="Skips the Microsoft account screen so you can use a local account"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource Win11SummaryRow}">
|
||||
<TextBlock Text="" Style="{StaticResource Win11SummaryIcon}"/>
|
||||
<TextBlock Style="{StaticResource Win11StepBody}" Margin="0"
|
||||
Text="Turns off telemetry, Copilot, the chat icon and search box suggestions"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource Win11SummaryRow}" Margin="0">
|
||||
<TextBlock Text="" Style="{StaticResource Win11SummaryIcon}"/>
|
||||
<TextBlock Style="{StaticResource Win11StepBody}" Margin="0"
|
||||
Text="Pins the edition you picked above so Setup does not choose another one"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
<!-- ─── STEP 3 : Output Options ───────────────────────── -->
|
||||
<TabItem Name="WPFWin11ISOOutputSection" IsEnabled="False" Tag="" Header="3 Output">
|
||||
<StackPanel Style="{StaticResource Win11StepColumn}">
|
||||
<TextBlock Text="" Style="{StaticResource Win11StepHero}"/>
|
||||
<TextBlock Style="{StaticResource Win11StepTitle}" Text="Your image is ready"/>
|
||||
<TextBlock Style="{StaticResource Win11StepSubtitle}">
|
||||
Save the modified image as an ISO file, or write it straight to a USB
|
||||
drive you can boot from.
|
||||
</TextBlock>
|
||||
|
||||
<Border Name="WPFWin11ISODonePanel"
|
||||
Style="{StaticResource Win11InfoPanel}"
|
||||
Padding="14,10"
|
||||
Margin="0,0,0,14"
|
||||
Visibility="Collapsed">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="" Style="{StaticResource Win11SummaryIcon}"
|
||||
VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<TextBlock Name="WPFWin11ISODoneLabel" Style="{StaticResource Win11StepValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Button Name="WPFWin11ISOChooseISOButton" Margin="0,0,0,10"
|
||||
Style="{StaticResource Win11StepButton}" Tag="" Content="Save as an ISO File"/>
|
||||
<Button Name="WPFWin11ISOChooseUSBButton" Foreground="OrangeRed"
|
||||
Style="{StaticResource Win11StepButton}" Tag=""
|
||||
Content="Write Directly to a USB Drive (ERASES DRIVE)"/>
|
||||
|
||||
<Border Name="WPFWin11ISOOptionUSB"
|
||||
Style="{StaticResource Win11FormPanel}"
|
||||
Visibility="Collapsed"
|
||||
Margin="0,14,0,0">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource Win11StepBody}">
|
||||
<Run FontWeight="Bold" Foreground="OrangeRed">!! All data on the selected USB drive will be permanently erased !!</Run>
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource Win11StepLabel}" Text="USB drive"/>
|
||||
<Grid Margin="0,0,0,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox Grid.Column="0"
|
||||
Name="WPFWin11ISOUSBDriveComboBox"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
Background="{DynamicResource MainBackgroundColor}"
|
||||
VerticalAlignment="Center"
|
||||
Height="34"
|
||||
Margin="0,0,12,0"/>
|
||||
<Button Grid.Column="1" Name="WPFWin11ISORefreshUSBButton" MinWidth="130"
|
||||
Style="{StaticResource Win11StepButton}" Tag="" Content="Refresh"/>
|
||||
</Grid>
|
||||
<Button Name="WPFWin11ISOWriteUSBButton" Foreground="OrangeRed"
|
||||
Style="{StaticResource Win11StepButton}" Tag="" Content="Erase & Write to USB"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border BorderBrush="{DynamicResource BorderColor}" BorderThickness="0,1,0,0"
|
||||
Margin="0,20,0,0" Padding="0,14,0,0">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource Win11StepSubtitle}" Margin="0,0,0,10"
|
||||
Text="Done with this image? Clear the temporary files and start again from step 1."/>
|
||||
<Button Name="WPFWin11ISOCleanResetButton"
|
||||
HorizontalAlignment="Center" MinWidth="240"
|
||||
Style="{StaticResource Win11StepButton}" Tag="" Content="Start Over"
|
||||
ToolTip="Delete the temporary working directory and go back to the first step"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
<!-- ─── Working page: selected while a step runs, no header of its own ─── -->
|
||||
<TabItem Name="WPFWin11ISOWorkingSection" Visibility="Collapsed">
|
||||
<StackPanel Style="{StaticResource Win11StepColumn}">
|
||||
<!-- BitmapCache because this is text: without it WPF re-rasterises the
|
||||
glyph every frame on the interface thread and the spin stutters.
|
||||
Cached, the rotation is a transform on an image the GPU already has. -->
|
||||
<TextBlock Name="WPFWin11ISOWorkingSpinner"
|
||||
Text=""
|
||||
Tag="Forward"
|
||||
RenderTransformOrigin="0.5,0.5"
|
||||
CacheMode="BitmapCache">
|
||||
<TextBlock.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</TextBlock.RenderTransform>
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock" BasedOn="{StaticResource Win11StepHero}">
|
||||
<Style.Triggers>
|
||||
<!-- Tied to visibility, not to Loaded: the working page stays loaded
|
||||
while collapsed, so a Loaded trigger left it spinning forever -->
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsVisible, RelativeSource={RelativeSource Self}}" Value="True"/>
|
||||
<Condition Binding="{Binding Tag, RelativeSource={RelativeSource Self}}" Value="Forward"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.EnterActions>
|
||||
<BeginStoryboard Name="Win11ISOSpin">
|
||||
<Storyboard RepeatBehavior="Forever">
|
||||
<DoubleAnimation
|
||||
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
|
||||
From="0" To="360" Duration="0:0:1.4"/>
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</MultiDataTrigger.EnterActions>
|
||||
<MultiDataTrigger.ExitActions>
|
||||
<StopStoryboard BeginStoryboardName="Win11ISOSpin"/>
|
||||
</MultiDataTrigger.ExitActions>
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsVisible, RelativeSource={RelativeSource Self}}" Value="True"/>
|
||||
<Condition Binding="{Binding Tag, RelativeSource={RelativeSource Self}}" Value="Reverse"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.EnterActions>
|
||||
<BeginStoryboard Name="Win11ISOSpinReverse">
|
||||
<Storyboard RepeatBehavior="Forever">
|
||||
<DoubleAnimation
|
||||
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
|
||||
From="360" To="0" Duration="0:0:1.4"/>
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</MultiDataTrigger.EnterActions>
|
||||
<MultiDataTrigger.ExitActions>
|
||||
<StopStoryboard BeginStoryboardName="Win11ISOSpinReverse"/>
|
||||
</MultiDataTrigger.ExitActions>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
<TextBlock Name="WPFWin11ISOWorkingLabel"
|
||||
Style="{StaticResource Win11StepTitle}"
|
||||
Text="Working..."/>
|
||||
<TextBlock Style="{StaticResource Win11StepSubtitle}"
|
||||
Margin="0"
|
||||
Text="This can take several minutes. Progress is written to the status log."/>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- ─── Status log ─── -->
|
||||
<Border Grid.Column="1" Style="{StaticResource BorderStyle}" Padding="20,16">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="" FontFamily="Segoe MDL2 Assets"
|
||||
FontSize="{DynamicResource IconFontSize}"
|
||||
Background="Transparent"
|
||||
Foreground="{DynamicResource LabelboxForegroundColor}"
|
||||
VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||
<TextBlock FontSize="{DynamicResource FontSize}"
|
||||
FontWeight="Bold"
|
||||
Background="Transparent"
|
||||
Foreground="{DynamicResource LabelboxForegroundColor}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Status Log"/>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<!-- Status Log (fills remaining height) -->
|
||||
<Grid Grid.Row="1" Margin="5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0"
|
||||
FontSize="{DynamicResource FontSize}" FontWeight="Bold"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
Margin="0,0,0,4">
|
||||
Status Log
|
||||
</TextBlock>
|
||||
<TextBox Grid.Row="1"
|
||||
Name="WPFWin11ISOStatusLog"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Visible"
|
||||
VerticalAlignment="Stretch"
|
||||
Padding="6"
|
||||
Background="{DynamicResource MainBackgroundColor}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
Text="Ready. Please select a Windows 11 ISO to begin."/>
|
||||
</Grid>
|
||||
<Border Grid.Row="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="0,1,0,0"
|
||||
Padding="0,10,0,0">
|
||||
<!-- TextAlignment and Effect look redundant but are not: the shared TextBox
|
||||
style stretches content alignment, which WPF renders as justified once
|
||||
text wraps, and its drop shadow lands on the glyphs with no background
|
||||
here to sit under. -->
|
||||
<TextBox Name="WPFWin11ISOStatusLog"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
TextAlignment="Left"
|
||||
FontFamily="{DynamicResource Win11LogFontFamily}"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
VerticalAlignment="Stretch"
|
||||
Padding="0"
|
||||
Background="Transparent"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
BorderThickness="0"
|
||||
Effect="{x:Null}"
|
||||
Text="Ready. Please select a Windows 11 ISO to begin."/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
Reference in New Issue
Block a user