mirror of
https://github.com/ChrisTitusTech/winutil
synced 2026-04-05 22:28:31 +00:00
Win11 Creator USB and Log Fixes (#4139)
* initial usb fixes * fix full button width * Cleanup and Verbose output for copy * expand ui and fix clean and reset * Add minimal driver injection * initial driver support * fix verbage * fix syntax error * create log file on iso generation * inject to boot.wim for install * fix single driver install issues * fix first run probs * cleanup injection * improve clean up * Fix OSCDIMG output and cleanup comments * Fix Scrollviewer in Status Log * large drive support and change to Exfat * Fix BOOT for older UEFI and Add error checks for small usb drives * fix single usb drive error
This commit is contained in:
2
.github/workflows/docs.yaml
vendored
2
.github/workflows/docs.yaml
vendored
@@ -47,7 +47,7 @@ jobs:
|
|||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
id: pages
|
id: pages
|
||||||
uses: actions/configure-pages@v5
|
uses: actions/configure-pages@v5
|
||||||
|
|
||||||
- name: Generate Dev Docs from JSON
|
- name: Generate Dev Docs from JSON
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -4,50 +4,38 @@ function Invoke-WinUtilISOScript {
|
|||||||
Applies WinUtil modifications to a mounted Windows 11 install.wim image.
|
Applies WinUtil modifications to a mounted Windows 11 install.wim image.
|
||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
Performs the following operations against an already-mounted WIM image:
|
Removes AppX bloatware and OneDrive, optionally injects all drivers exported from
|
||||||
|
the running system into install.wim and boot.wim (controlled by the
|
||||||
|
-InjectCurrentSystemDrivers switch), applies offline registry tweaks (hardware
|
||||||
|
bypass, privacy, OOBE, telemetry, update suppression), deletes CEIP/WU
|
||||||
|
scheduled-task definition files, and optionally writes autounattend.xml to the ISO
|
||||||
|
root and removes the support\ folder from the ISO contents directory.
|
||||||
|
|
||||||
1. Removes provisioned AppX bloatware packages via DISM.
|
All setup scripts embedded in the autounattend.xml <Extensions><File> nodes are
|
||||||
2. Removes OneDriveSetup.exe from the system image.
|
written directly into the WIM at their target paths under C:\Windows\Setup\Scripts\
|
||||||
3. Loads offline registry hives (COMPONENTS, DEFAULT, NTUSER, SOFTWARE, SYSTEM)
|
to ensure they survive Windows Setup stripping unrecognised-namespace XML elements
|
||||||
and applies the following tweaks:
|
from the Panther copy of the answer file.
|
||||||
- Bypasses hardware requirement checks (CPU, RAM, SecureBoot, Storage, TPM).
|
|
||||||
- Disables sponsored-app delivery and ContentDeliveryManager features.
|
|
||||||
- Enables local-account OOBE path (BypassNRO).
|
|
||||||
- Writes autounattend.xml to the Sysprep directory inside the WIM and,
|
|
||||||
optionally, to the ISO/USB root so Windows Setup picks it up at boot.
|
|
||||||
- Disables reserved storage.
|
|
||||||
- Disables BitLocker device encryption.
|
|
||||||
- Hides the Chat (Teams) taskbar icon.
|
|
||||||
- Disables OneDrive folder backup (KFM).
|
|
||||||
- Disables telemetry, advertising ID, and input personalization.
|
|
||||||
- Blocks post-install delivery of DevHome, Outlook, and Teams.
|
|
||||||
- Disables Windows Copilot.
|
|
||||||
- Disables Windows Update during OOBE.
|
|
||||||
4. Deletes unwanted scheduled-task XML definition files (CEIP, Appraiser, etc.).
|
|
||||||
5. Removes the support\ folder from the ISO contents directory (if supplied).
|
|
||||||
|
|
||||||
Mounting and dismounting the WIM is the responsibility of the caller
|
Mounting/dismounting the WIM is the caller's responsibility (e.g. Invoke-WinUtilISO).
|
||||||
(e.g. Invoke-WinUtilISO).
|
|
||||||
|
|
||||||
.PARAMETER ScratchDir
|
.PARAMETER ScratchDir
|
||||||
Mandatory. Full path to the directory where the Windows image is currently mounted.
|
Mandatory. Full path to the directory where the Windows image is currently mounted.
|
||||||
Example: C:\Users\USERNAME\AppData\Local\Temp\WinUtil_Win11ISO_20260222\wim_mount
|
|
||||||
|
|
||||||
.PARAMETER ISOContentsDir
|
.PARAMETER ISOContentsDir
|
||||||
Optional. Root directory of the extracted ISO contents.
|
Optional. Root directory of the extracted ISO contents. When supplied,
|
||||||
When supplied, autounattend.xml is also written here so Windows Setup picks it
|
autounattend.xml is written here and the support\ folder is removed.
|
||||||
up automatically at boot, and the support\ folder is deleted from that location.
|
|
||||||
|
|
||||||
.PARAMETER AutoUnattendXml
|
.PARAMETER AutoUnattendXml
|
||||||
Optional. Full XML content for autounattend.xml.
|
Optional. Full XML content for autounattend.xml. If empty, the OOBE bypass
|
||||||
In compiled winutil.ps1 this is the embedded $WinUtilAutounattendXml here-string;
|
file is skipped and a warning is logged.
|
||||||
in dev mode it is read from tools\autounattend.xml.
|
|
||||||
If empty, the OOBE bypass file is skipped and a warning is logged.
|
.PARAMETER InjectCurrentSystemDrivers
|
||||||
|
Optional. When $true, exports all drivers from the running system and injects
|
||||||
|
them into install.wim and boot.wim index 2 (Windows Setup PE).
|
||||||
|
Defaults to $false.
|
||||||
|
|
||||||
.PARAMETER Log
|
.PARAMETER Log
|
||||||
Optional ScriptBlock used for progress/status logging.
|
Optional ScriptBlock for progress/status logging. Receives a single [string] argument.
|
||||||
Receives a single [string] message argument.
|
|
||||||
Defaults to { param($m) Write-Output $m } when not supplied.
|
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
Invoke-WinUtilISOScript -ScratchDir "C:\Temp\wim_mount"
|
Invoke-WinUtilISOScript -ScratchDir "C:\Temp\wim_mount"
|
||||||
@@ -62,24 +50,19 @@ function Invoke-WinUtilISOScript {
|
|||||||
.NOTES
|
.NOTES
|
||||||
Author : Chris Titus @christitustech
|
Author : Chris Titus @christitustech
|
||||||
GitHub : https://github.com/ChrisTitusTech
|
GitHub : https://github.com/ChrisTitusTech
|
||||||
Version : 26.02.22
|
Version : 26.03.02
|
||||||
#>
|
#>
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory)][string]$ScratchDir,
|
[Parameter(Mandatory)][string]$ScratchDir,
|
||||||
# Root directory of the extracted ISO contents. When supplied, autounattend.xml
|
|
||||||
# is written here so Windows Setup picks it up automatically at boot.
|
|
||||||
[string]$ISOContentsDir = "",
|
[string]$ISOContentsDir = "",
|
||||||
# Autounattend XML content. In compiled winutil.ps1 this comes from the embedded
|
|
||||||
# $WinUtilAutounattendXml here-string; in dev mode it is read from tools\autounattend.xml.
|
|
||||||
[string]$AutoUnattendXml = "",
|
[string]$AutoUnattendXml = "",
|
||||||
|
[bool]$InjectCurrentSystemDrivers = $false,
|
||||||
[scriptblock]$Log = { param($m) Write-Output $m }
|
[scriptblock]$Log = { param($m) Write-Output $m }
|
||||||
)
|
)
|
||||||
|
|
||||||
# ── Resolve admin group name (for takeown / icacls) ──────────────────────
|
|
||||||
$adminSID = New-Object System.Security.Principal.SecurityIdentifier('S-1-5-32-544')
|
$adminSID = New-Object System.Security.Principal.SecurityIdentifier('S-1-5-32-544')
|
||||||
$adminGroup = $adminSID.Translate([System.Security.Principal.NTAccount])
|
$adminGroup = $adminSID.Translate([System.Security.Principal.NTAccount])
|
||||||
|
|
||||||
# ── Local helpers ─────────────────────────────────────────────────────────
|
|
||||||
function Set-ISOScriptReg {
|
function Set-ISOScriptReg {
|
||||||
param ([string]$path, [string]$name, [string]$type, [string]$value)
|
param ([string]$path, [string]$name, [string]$type, [string]$value)
|
||||||
try {
|
try {
|
||||||
@@ -100,15 +83,37 @@ function Invoke-WinUtilISOScript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════
|
function Add-DriversToImage {
|
||||||
# 1. Remove provisioned AppX packages
|
param ([string]$MountPath, [string]$DriverDir, [string]$Label = "image", [scriptblock]$Logger)
|
||||||
# ═════════════════════════════════════════════════════════════════════════
|
& dism /English "/image:$MountPath" /Add-Driver "/Driver:$DriverDir" /Recurse 2>&1 |
|
||||||
|
ForEach-Object { & $Logger " dism[$Label]: $_" }
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-BootWimInject {
|
||||||
|
param ([string]$BootWimPath, [string]$DriverDir, [scriptblock]$Logger)
|
||||||
|
Set-ItemProperty -Path $BootWimPath -Name IsReadOnly -Value $false -ErrorAction SilentlyContinue
|
||||||
|
$mountDir = Join-Path $env:TEMP "WinUtil_BootMount_$(Get-Random)"
|
||||||
|
New-Item -Path $mountDir -ItemType Directory -Force | Out-Null
|
||||||
|
try {
|
||||||
|
& $Logger "Mounting boot.wim (index 2) for driver injection..."
|
||||||
|
Mount-WindowsImage -ImagePath $BootWimPath -Index 2 -Path $mountDir -ErrorAction Stop | Out-Null
|
||||||
|
Add-DriversToImage -MountPath $mountDir -DriverDir $DriverDir -Label "boot" -Logger $Logger
|
||||||
|
& $Logger "Saving boot.wim..."
|
||||||
|
Dismount-WindowsImage -Path $mountDir -Save -ErrorAction Stop | Out-Null
|
||||||
|
& $Logger "boot.wim driver injection complete."
|
||||||
|
} catch {
|
||||||
|
& $Logger "Warning: boot.wim driver injection failed: $_"
|
||||||
|
try { Dismount-WindowsImage -Path $mountDir -Discard -ErrorAction SilentlyContinue | Out-Null } catch {}
|
||||||
|
} finally {
|
||||||
|
Remove-Item -Path $mountDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 1. Remove provisioned AppX packages ──────────────────────────────────
|
||||||
& $Log "Removing provisioned AppX packages..."
|
& $Log "Removing provisioned AppX packages..."
|
||||||
|
|
||||||
$packages = & dism /English "/image:$ScratchDir" /Get-ProvisionedAppxPackages |
|
$packages = & dism /English "/image:$ScratchDir" /Get-ProvisionedAppxPackages |
|
||||||
ForEach-Object {
|
ForEach-Object { if ($_ -match 'PackageName : (.*)') { $matches[1] } }
|
||||||
if ($_ -match 'PackageName : (.*)') { $matches[1] }
|
|
||||||
}
|
|
||||||
|
|
||||||
$packagePrefixes = @(
|
$packagePrefixes = @(
|
||||||
'AppUp.IntelManagementandSecurityStatus',
|
'AppUp.IntelManagementandSecurityStatus',
|
||||||
@@ -155,25 +160,46 @@ function Invoke-WinUtilISOScript {
|
|||||||
'MicrosoftTeams'
|
'MicrosoftTeams'
|
||||||
)
|
)
|
||||||
|
|
||||||
$packagesToRemove = $packages | Where-Object {
|
$packages | Where-Object { $pkg = $_; $packagePrefixes | Where-Object { $pkg -like "*$_*" } } |
|
||||||
$pkg = $_
|
ForEach-Object { & dism /English "/image:$ScratchDir" /Remove-ProvisionedAppxPackage "/PackageName:$_" }
|
||||||
$packagePrefixes | Where-Object { $pkg -like "*$_*" }
|
|
||||||
}
|
# ── 2. Inject current system drivers (optional) ───────────────────────────
|
||||||
foreach ($package in $packagesToRemove) {
|
if ($InjectCurrentSystemDrivers) {
|
||||||
& dism /English "/image:$ScratchDir" /Remove-ProvisionedAppxPackage "/PackageName:$package"
|
& $Log "Exporting all drivers from running system..."
|
||||||
|
$driverExportRoot = Join-Path $env:TEMP "WinUtil_DriverExport_$(Get-Random)"
|
||||||
|
New-Item -Path $driverExportRoot -ItemType Directory -Force | Out-Null
|
||||||
|
try {
|
||||||
|
Export-WindowsDriver -Online -Destination $driverExportRoot | Out-Null
|
||||||
|
|
||||||
|
& $Log "Injecting current system drivers into install.wim..."
|
||||||
|
Add-DriversToImage -MountPath $ScratchDir -DriverDir $driverExportRoot -Label "install" -Logger $Log
|
||||||
|
& $Log "install.wim driver injection complete."
|
||||||
|
|
||||||
|
if ($ISOContentsDir -and (Test-Path $ISOContentsDir)) {
|
||||||
|
$bootWim = Join-Path $ISOContentsDir "sources\boot.wim"
|
||||||
|
if (Test-Path $bootWim) {
|
||||||
|
& $Log "Injecting current system drivers into boot.wim..."
|
||||||
|
Invoke-BootWimInject -BootWimPath $bootWim -DriverDir $driverExportRoot -Logger $Log
|
||||||
|
} else {
|
||||||
|
& $Log "Warning: boot.wim not found — skipping boot.wim driver injection."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
& $Log "Error during driver export/injection: $_"
|
||||||
|
} finally {
|
||||||
|
Remove-Item -Path $driverExportRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
& $Log "Driver injection skipped."
|
||||||
}
|
}
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════
|
# ── 3. Remove OneDrive ────────────────────────────────────────────────────
|
||||||
# 2. Remove OneDrive
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════
|
|
||||||
& $Log "Removing OneDrive..."
|
& $Log "Removing OneDrive..."
|
||||||
& takeown /f "$ScratchDir\Windows\System32\OneDriveSetup.exe" | Out-Null
|
& takeown /f "$ScratchDir\Windows\System32\OneDriveSetup.exe" | Out-Null
|
||||||
& icacls "$ScratchDir\Windows\System32\OneDriveSetup.exe" /grant "$($adminGroup.Value):(F)" /T /C | Out-Null
|
& icacls "$ScratchDir\Windows\System32\OneDriveSetup.exe" /grant "$($adminGroup.Value):(F)" /T /C | Out-Null
|
||||||
Remove-Item -Path "$ScratchDir\Windows\System32\OneDriveSetup.exe" -Force -ErrorAction SilentlyContinue
|
Remove-Item -Path "$ScratchDir\Windows\System32\OneDriveSetup.exe" -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════
|
# ── 4. Registry tweaks ────────────────────────────────────────────────────
|
||||||
# 3. Registry tweaks
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════
|
|
||||||
& $Log "Loading offline registry hives..."
|
& $Log "Loading offline registry hives..."
|
||||||
reg load HKLM\zCOMPONENTS "$ScratchDir\Windows\System32\config\COMPONENTS"
|
reg load HKLM\zCOMPONENTS "$ScratchDir\Windows\System32\config\COMPONENTS"
|
||||||
reg load HKLM\zDEFAULT "$ScratchDir\Windows\System32\config\default"
|
reg load HKLM\zDEFAULT "$ScratchDir\Windows\System32\config\default"
|
||||||
@@ -222,14 +248,37 @@ function Invoke-WinUtilISOScript {
|
|||||||
Set-ISOScriptReg 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\OOBE' 'BypassNRO' 'REG_DWORD' '1'
|
Set-ISOScriptReg 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\OOBE' 'BypassNRO' 'REG_DWORD' '1'
|
||||||
|
|
||||||
if ($AutoUnattendXml) {
|
if ($AutoUnattendXml) {
|
||||||
# ── Place autounattend.xml inside the WIM (Sysprep) ──────────────────
|
try {
|
||||||
$sysprepDest = "$ScratchDir\Windows\System32\Sysprep\autounattend.xml"
|
$xmlDoc = [xml]::new()
|
||||||
Set-Content -Path $sysprepDest -Value $AutoUnattendXml -Encoding UTF8 -Force
|
$xmlDoc.LoadXml($AutoUnattendXml)
|
||||||
& $Log "Written autounattend.xml to Sysprep directory."
|
|
||||||
|
$nsMgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
|
||||||
|
$nsMgr.AddNamespace("sg", "https://schneegans.de/windows/unattend-generator/")
|
||||||
|
|
||||||
|
$fileNodes = $xmlDoc.SelectNodes("//sg:File", $nsMgr)
|
||||||
|
if ($fileNodes -and $fileNodes.Count -gt 0) {
|
||||||
|
foreach ($fileNode in $fileNodes) {
|
||||||
|
$absPath = $fileNode.GetAttribute("path")
|
||||||
|
$relPath = $absPath -replace '^[A-Za-z]:[/\\]', ''
|
||||||
|
$destPath = Join-Path $ScratchDir $relPath
|
||||||
|
New-Item -Path (Split-Path $destPath -Parent) -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null
|
||||||
|
|
||||||
|
$ext = [IO.Path]::GetExtension($destPath).ToLower()
|
||||||
|
$encoding = switch ($ext) {
|
||||||
|
{ $_ -in '.ps1', '.xml' } { [System.Text.Encoding]::UTF8 }
|
||||||
|
{ $_ -in '.reg', '.vbs', '.js' } { [System.Text.UnicodeEncoding]::new($false, $true) }
|
||||||
|
default { [System.Text.Encoding]::Default }
|
||||||
|
}
|
||||||
|
[System.IO.File]::WriteAllBytes($destPath, ($encoding.GetPreamble() + $encoding.GetBytes($fileNode.InnerText.Trim())))
|
||||||
|
& $Log "Pre-staged setup script: $relPath"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
& $Log "Warning: no <Extensions><File> nodes found in autounattend.xml — setup scripts not pre-staged."
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
& $Log "Warning: could not pre-stage setup scripts from autounattend.xml: $_"
|
||||||
|
}
|
||||||
|
|
||||||
# ── Place autounattend.xml at the ISO / USB root ──────────────────────
|
|
||||||
# Windows Setup reads this file first (before booting into the OS),
|
|
||||||
# which is what drives the local-account / OOBE bypass at install time.
|
|
||||||
if ($ISOContentsDir -and (Test-Path $ISOContentsDir)) {
|
if ($ISOContentsDir -and (Test-Path $ISOContentsDir)) {
|
||||||
$isoDest = Join-Path $ISOContentsDir "autounattend.xml"
|
$isoDest = Join-Path $ISOContentsDir "autounattend.xml"
|
||||||
Set-Content -Path $isoDest -Value $AutoUnattendXml -Encoding UTF8 -Force
|
Set-Content -Path $isoDest -Value $AutoUnattendXml -Encoding UTF8 -Force
|
||||||
@@ -272,8 +321,8 @@ function Invoke-WinUtilISOScript {
|
|||||||
Remove-ISOScriptReg 'HKLM\zSOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\DevHomeUpdate'
|
Remove-ISOScriptReg 'HKLM\zSOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\DevHomeUpdate'
|
||||||
|
|
||||||
& $Log "Disabling Copilot..."
|
& $Log "Disabling Copilot..."
|
||||||
Set-ISOScriptReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\WindowsCopilot' 'TurnOffWindowsCopilot' 'REG_DWORD' '1'
|
Set-ISOScriptReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\WindowsCopilot' 'TurnOffWindowsCopilot' 'REG_DWORD' '1'
|
||||||
Set-ISOScriptReg 'HKLM\zSOFTWARE\Policies\Microsoft\Edge' 'HubsSidebarEnabled' 'REG_DWORD' '0'
|
Set-ISOScriptReg 'HKLM\zSOFTWARE\Policies\Microsoft\Edge' 'HubsSidebarEnabled' 'REG_DWORD' '0'
|
||||||
Set-ISOScriptReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Explorer' 'DisableSearchBoxSuggestions' 'REG_DWORD' '1'
|
Set-ISOScriptReg 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Explorer' 'DisableSearchBoxSuggestions' 'REG_DWORD' '1'
|
||||||
|
|
||||||
& $Log "Disabling Windows Update during OOBE (re-enabled on first logon via FirstLogon.ps1)..."
|
& $Log "Disabling Windows Update during OOBE (re-enabled on first logon via FirstLogon.ps1)..."
|
||||||
@@ -304,12 +353,9 @@ function Invoke-WinUtilISOScript {
|
|||||||
reg unload HKLM\zSOFTWARE
|
reg unload HKLM\zSOFTWARE
|
||||||
reg unload HKLM\zSYSTEM
|
reg unload HKLM\zSYSTEM
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════
|
# ── 5. Delete scheduled task definition files ─────────────────────────────
|
||||||
# 4. Delete scheduled task definition files
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════
|
|
||||||
& $Log "Deleting scheduled task definition files..."
|
& $Log "Deleting scheduled task definition files..."
|
||||||
$tasksPath = "$ScratchDir\Windows\System32\Tasks"
|
$tasksPath = "$ScratchDir\Windows\System32\Tasks"
|
||||||
|
|
||||||
Remove-Item "$tasksPath\Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser" -Force -ErrorAction SilentlyContinue
|
Remove-Item "$tasksPath\Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser" -Force -ErrorAction SilentlyContinue
|
||||||
Remove-Item "$tasksPath\Microsoft\Windows\Customer Experience Improvement Program" -Recurse -Force -ErrorAction SilentlyContinue
|
Remove-Item "$tasksPath\Microsoft\Windows\Customer Experience Improvement Program" -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
Remove-Item "$tasksPath\Microsoft\Windows\Application Experience\ProgramDataUpdater" -Force -ErrorAction SilentlyContinue
|
Remove-Item "$tasksPath\Microsoft\Windows\Application Experience\ProgramDataUpdater" -Force -ErrorAction SilentlyContinue
|
||||||
@@ -321,12 +367,9 @@ function Invoke-WinUtilISOScript {
|
|||||||
Remove-Item "$tasksPath\Microsoft\Windows\WaaSMedic" -Recurse -Force -ErrorAction SilentlyContinue
|
Remove-Item "$tasksPath\Microsoft\Windows\WaaSMedic" -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
Remove-Item "$tasksPath\Microsoft\Windows\WindowsUpdate" -Recurse -Force -ErrorAction SilentlyContinue
|
Remove-Item "$tasksPath\Microsoft\Windows\WindowsUpdate" -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
Remove-Item "$tasksPath\Microsoft\WindowsUpdate" -Recurse -Force -ErrorAction SilentlyContinue
|
Remove-Item "$tasksPath\Microsoft\WindowsUpdate" -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
& $Log "Scheduled task files deleted."
|
& $Log "Scheduled task files deleted."
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════
|
# ── 6. Remove ISO support folder ─────────────────────────────────────────
|
||||||
# 5. Remove ISO support folder (fresh-install only; not needed)
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════
|
|
||||||
if ($ISOContentsDir -and (Test-Path $ISOContentsDir)) {
|
if ($ISOContentsDir -and (Test-Path $ISOContentsDir)) {
|
||||||
& $Log "Removing ISO support\ folder..."
|
& $Log "Removing ISO support\ folder..."
|
||||||
Remove-Item -Path (Join-Path $ISOContentsDir "support") -Recurse -Force -ErrorAction SilentlyContinue
|
Remove-Item -Path (Join-Path $ISOContentsDir "support") -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
|||||||
240
functions/private/Invoke-WinUtilISOUSB.ps1
Normal file
240
functions/private/Invoke-WinUtilISOUSB.ps1
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
function Invoke-WinUtilISORefreshUSBDrives {
|
||||||
|
$combo = $sync["WPFWin11ISOUSBDriveComboBox"]
|
||||||
|
$removable = @(Get-Disk | Where-Object { $_.BusType -eq "USB" } | Sort-Object Number)
|
||||||
|
|
||||||
|
$combo.Items.Clear()
|
||||||
|
|
||||||
|
if ($removable.Count -eq 0) {
|
||||||
|
$combo.Items.Add("No USB drives detected")
|
||||||
|
$combo.SelectedIndex = 0
|
||||||
|
$sync["Win11ISOUSBDisks"] = @()
|
||||||
|
Write-Win11ISOLog "No USB drives detected."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($disk in $removable) {
|
||||||
|
$sizeGB = [math]::Round($disk.Size / 1GB, 1)
|
||||||
|
$combo.Items.Add("Disk $($disk.Number): $($disk.FriendlyName) [$sizeGB GB] - $($disk.PartitionStyle)")
|
||||||
|
}
|
||||||
|
$combo.SelectedIndex = 0
|
||||||
|
Write-Win11ISOLog "Found $($removable.Count) USB drive(s)."
|
||||||
|
$sync["Win11ISOUSBDisks"] = $removable
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-WinUtilISOWriteUSB {
|
||||||
|
$contentsDir = $sync["Win11ISOContentsDir"]
|
||||||
|
$usbDisks = $sync["Win11ISOUSBDisks"]
|
||||||
|
|
||||||
|
if (-not $contentsDir -or -not (Test-Path $contentsDir)) {
|
||||||
|
[System.Windows.MessageBox]::Show("No modified ISO content found. Please complete Steps 1-3 first.", "Not Ready", "OK", "Warning")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$combo = $sync["WPFWin11ISOUSBDriveComboBox"]
|
||||||
|
$selectedIndex = $combo.SelectedIndex
|
||||||
|
$selectedItemText = [string]$combo.SelectedItem
|
||||||
|
$usbDisks = @($usbDisks)
|
||||||
|
|
||||||
|
$targetDisk = $null
|
||||||
|
if ($selectedIndex -ge 0 -and $selectedIndex -lt $usbDisks.Count) {
|
||||||
|
$targetDisk = $usbDisks[$selectedIndex]
|
||||||
|
} elseif ($selectedItemText -match 'Disk\s+(\d+):') {
|
||||||
|
$selectedDiskNum = [int]$matches[1]
|
||||||
|
$targetDisk = $usbDisks | Where-Object { $_.Number -eq $selectedDiskNum } | Select-Object -First 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $targetDisk) {
|
||||||
|
[System.Windows.MessageBox]::Show("Please select a USB drive from the dropdown.", "No Drive Selected", "OK", "Warning")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$diskNum = $targetDisk.Number
|
||||||
|
$sizeGB = [math]::Round($targetDisk.Size / 1GB, 1)
|
||||||
|
|
||||||
|
$confirm = [System.Windows.MessageBox]::Show(
|
||||||
|
"ALL data on Disk $diskNum ($($targetDisk.FriendlyName), $sizeGB GB) will be PERMANENTLY ERASED.`n`nAre you sure you want to continue?",
|
||||||
|
"Confirm USB Erase", "YesNo", "Warning")
|
||||||
|
|
||||||
|
if ($confirm -ne "Yes") {
|
||||||
|
Write-Win11ISOLog "USB write cancelled by user."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $false
|
||||||
|
Write-Win11ISOLog "Starting USB write to Disk $diskNum..."
|
||||||
|
|
||||||
|
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
|
||||||
|
$runspace.ApartmentState = "STA"
|
||||||
|
$runspace.ThreadOptions = "ReuseThread"
|
||||||
|
$runspace.Open()
|
||||||
|
$runspace.SessionStateProxy.SetVariable("sync", $sync)
|
||||||
|
$runspace.SessionStateProxy.SetVariable("diskNum", $diskNum)
|
||||||
|
$runspace.SessionStateProxy.SetVariable("contentsDir", $contentsDir)
|
||||||
|
|
||||||
|
$script = [Management.Automation.PowerShell]::Create()
|
||||||
|
$script.Runspace = $runspace
|
||||||
|
$script.AddScript({
|
||||||
|
|
||||||
|
function Log($msg) {
|
||||||
|
$ts = (Get-Date).ToString("HH:mm:ss")
|
||||||
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
|
$sync["WPFWin11ISOStatusLog"].Text += "`n[$ts] $msg"
|
||||||
|
$sync["WPFWin11ISOStatusLog"].CaretIndex = $sync["WPFWin11ISOStatusLog"].Text.Length
|
||||||
|
$sync["WPFWin11ISOStatusLog"].ScrollToEnd()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function SetProgress($label, $pct) {
|
||||||
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
|
$sync.progressBarTextBlock.Text = $label
|
||||||
|
$sync.progressBarTextBlock.ToolTip = $label
|
||||||
|
$sync.ProgressBar.Value = [Math]::Max($pct, 5)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-FreeDriveLetter {
|
||||||
|
$used = (Get-PSDrive -PSProvider FileSystem -ErrorAction SilentlyContinue).Name
|
||||||
|
foreach ($c in [char[]](68..90)) {
|
||||||
|
if ($used -notcontains [string]$c) { return $c }
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
SetProgress "Formatting USB drive..." 10
|
||||||
|
|
||||||
|
# Phase 1: Clean disk via diskpart
|
||||||
|
$dpFile1 = Join-Path $env:TEMP "winutil_diskpart_$(Get-Random).txt"
|
||||||
|
"select disk $diskNum`nclean`nexit" | Set-Content -Path $dpFile1 -Encoding ASCII
|
||||||
|
Log "Running diskpart clean on Disk $diskNum..."
|
||||||
|
diskpart /s $dpFile1 2>&1 | Where-Object { $_ -match '\S' } | ForEach-Object { Log " diskpart: $_" }
|
||||||
|
Remove-Item $dpFile1 -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
# Phase 2: Initialize as GPT
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
Update-Disk -Number $diskNum -ErrorAction SilentlyContinue
|
||||||
|
$diskObj = Get-Disk -Number $diskNum -ErrorAction Stop
|
||||||
|
if ($diskObj.PartitionStyle -eq 'RAW') {
|
||||||
|
Initialize-Disk -Number $diskNum -PartitionStyle GPT -ErrorAction Stop
|
||||||
|
Log "Disk $diskNum initialized as GPT."
|
||||||
|
} else {
|
||||||
|
Set-Disk -Number $diskNum -PartitionStyle GPT -ErrorAction Stop
|
||||||
|
Log "Disk $diskNum converted to GPT (was $($diskObj.PartitionStyle))."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Phase 3: Create FAT32 partition via diskpart
|
||||||
|
$volLabel = "W11-" + (Get-Date).ToString('yyMMdd')
|
||||||
|
$dpFile2 = Join-Path $env:TEMP "winutil_diskpart2_$(Get-Random).txt"
|
||||||
|
$maxFat32PartitionMB = 32768
|
||||||
|
$diskSizeMB = [int][Math]::Floor((Get-Disk -Number $diskNum -ErrorAction Stop).Size / 1MB)
|
||||||
|
$createPartitionCommand = "create partition primary"
|
||||||
|
if ($diskSizeMB -gt $maxFat32PartitionMB) {
|
||||||
|
$createPartitionCommand = "create partition primary size=$maxFat32PartitionMB"
|
||||||
|
Log "Disk $diskNum is $diskSizeMB MB; creating FAT32 partition capped at $maxFat32PartitionMB MB (32 GB)."
|
||||||
|
}
|
||||||
|
|
||||||
|
@(
|
||||||
|
"select disk $diskNum"
|
||||||
|
$createPartitionCommand
|
||||||
|
"format quick fs=fat32 label=`"$volLabel`""
|
||||||
|
"exit"
|
||||||
|
) | Set-Content -Path $dpFile2 -Encoding ASCII
|
||||||
|
Log "Creating partitions on Disk $diskNum..."
|
||||||
|
diskpart /s $dpFile2 2>&1 | Where-Object { $_ -match '\S' } | ForEach-Object { Log " diskpart: $_" }
|
||||||
|
Remove-Item $dpFile2 -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
SetProgress "Assigning drive letters..." 30
|
||||||
|
Start-Sleep -Seconds 3
|
||||||
|
Update-Disk -Number $diskNum -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
$partitions = Get-Partition -DiskNumber $diskNum -ErrorAction Stop
|
||||||
|
Log "Partitions on Disk $diskNum after format: $($partitions.Count)"
|
||||||
|
foreach ($p in $partitions) {
|
||||||
|
Log " Partition $($p.PartitionNumber) Type=$($p.Type) Letter=$($p.DriveLetter) Size=$([math]::Round($p.Size/1MB))MB"
|
||||||
|
}
|
||||||
|
|
||||||
|
$winpePart = $partitions | Where-Object { $_.Type -eq "Basic" } | Select-Object -Last 1
|
||||||
|
if (-not $winpePart) {
|
||||||
|
throw "Could not find the WINPE (Basic) partition on Disk $diskNum after format."
|
||||||
|
}
|
||||||
|
|
||||||
|
try { Remove-PartitionAccessPath -DiskNumber $diskNum -PartitionNumber $winpePart.PartitionNumber -AccessPath "$($winpePart.DriveLetter):" -ErrorAction SilentlyContinue } catch {}
|
||||||
|
$usbLetter = Get-FreeDriveLetter
|
||||||
|
if (-not $usbLetter) { throw "No free drive letters (D-Z) available to assign to the USB data partition." }
|
||||||
|
Set-Partition -DiskNumber $diskNum -PartitionNumber $winpePart.PartitionNumber -NewDriveLetter $usbLetter
|
||||||
|
Log "Assigned drive letter $usbLetter to WINPE partition (Partition $($winpePart.PartitionNumber))."
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
|
||||||
|
$usbDrive = "${usbLetter}:"
|
||||||
|
if (-not (Test-Path $usbDrive)) { throw "Drive $usbDrive is not accessible after letter assignment." }
|
||||||
|
Log "USB data partition: $usbDrive"
|
||||||
|
|
||||||
|
$contentSizeBytes = (Get-ChildItem -LiteralPath $contentsDir -File -Recurse -Force -ErrorAction Stop | Measure-Object -Property Length -Sum).Sum
|
||||||
|
if (-not $contentSizeBytes) { $contentSizeBytes = 0 }
|
||||||
|
$usbVolume = Get-Volume -DriveLetter $usbLetter -ErrorAction Stop
|
||||||
|
$partitionCapacityBytes = [int64]$usbVolume.Size
|
||||||
|
$partitionFreeBytes = [int64]$usbVolume.SizeRemaining
|
||||||
|
|
||||||
|
$contentSizeGB = [math]::Round($contentSizeBytes / 1GB, 2)
|
||||||
|
$partitionCapacityGB = [math]::Round($partitionCapacityBytes / 1GB, 2)
|
||||||
|
$partitionFreeGB = [math]::Round($partitionFreeBytes / 1GB, 2)
|
||||||
|
|
||||||
|
Log "Source content size: $contentSizeGB GB. USB partition capacity: $partitionCapacityGB GB, free: $partitionFreeGB GB."
|
||||||
|
|
||||||
|
if ($contentSizeBytes -gt $partitionCapacityBytes) {
|
||||||
|
throw "ISO content ($contentSizeGB GB) is larger than the USB partition capacity ($partitionCapacityGB GB). Use a larger USB drive or reduce image size."
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($contentSizeBytes -gt $partitionFreeBytes) {
|
||||||
|
throw "Insufficient free space on USB partition. Required: $contentSizeGB GB, available: $partitionFreeGB GB."
|
||||||
|
}
|
||||||
|
|
||||||
|
SetProgress "Copying Windows 11 files to USB..." 45
|
||||||
|
|
||||||
|
# Copy files; split install.wim if > 4 GB (FAT32 limit)
|
||||||
|
$installWim = Join-Path $contentsDir "sources\install.wim"
|
||||||
|
if (Test-Path $installWim) {
|
||||||
|
$wimSizeMB = [math]::Round((Get-Item $installWim).Length / 1MB)
|
||||||
|
if ($wimSizeMB -gt 3800) {
|
||||||
|
Log "install.wim is $wimSizeMB MB - splitting for FAT32 compatibility... This will take several minutes."
|
||||||
|
$splitDest = Join-Path $usbDrive "sources\install.swm"
|
||||||
|
New-Item -ItemType Directory -Path (Split-Path $splitDest) -Force | Out-Null
|
||||||
|
Split-WindowsImage -ImagePath $installWim -SplitImagePath $splitDest -FileSize 3800 -CheckIntegrity
|
||||||
|
Log "install.wim split complete."
|
||||||
|
Log "Copying remaining files to USB..."
|
||||||
|
& robocopy $contentsDir $usbDrive /E /XF install.wim /NFL /NDL /NJH /NJS
|
||||||
|
} else {
|
||||||
|
& robocopy $contentsDir $usbDrive /E /NFL /NDL /NJH /NJS
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
& robocopy $contentsDir $usbDrive /E /NFL /NDL /NJH /NJS
|
||||||
|
}
|
||||||
|
|
||||||
|
SetProgress "Finalising USB drive..." 90
|
||||||
|
Log "Files copied to USB."
|
||||||
|
SetProgress "USB write complete" 100
|
||||||
|
Log "USB drive is ready for use."
|
||||||
|
|
||||||
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
|
[System.Windows.MessageBox]::Show(
|
||||||
|
"USB drive created successfully!`n`nYou can now boot from this drive to install Windows 11.",
|
||||||
|
"USB Ready", "OK", "Info")
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
Log "ERROR during USB write: $_"
|
||||||
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
|
[System.Windows.MessageBox]::Show("USB write failed:`n`n$_", "USB Write Error", "OK", "Error")
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
Start-Sleep -Milliseconds 800
|
||||||
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
|
$sync.progressBarTextBlock.Text = ""
|
||||||
|
$sync.progressBarTextBlock.ToolTip = ""
|
||||||
|
$sync.ProgressBar.Value = 0
|
||||||
|
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}) | Out-Null
|
||||||
|
|
||||||
|
$script.BeginInvoke() | Out-Null
|
||||||
|
}
|
||||||
@@ -540,6 +540,10 @@ $sync["FontScalingApplyButton"].Add_Click({
|
|||||||
|
|
||||||
# ── Win11ISO Tab button handlers ──────────────────────────────────────────────
|
# ── Win11ISO Tab button handlers ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
$sync["WPFTab5BT"].Add_Click({
|
||||||
|
$sync["Form"].Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ Invoke-WinUtilISOCheckExistingWork }) | Out-Null
|
||||||
|
})
|
||||||
|
|
||||||
$sync["WPFWin11ISOBrowseButton"].Add_Click({
|
$sync["WPFWin11ISOBrowseButton"].Add_Click({
|
||||||
Write-Debug "WPFWin11ISOBrowseButton clicked"
|
Write-Debug "WPFWin11ISOBrowseButton clicked"
|
||||||
Invoke-WinUtilISOBrowse
|
Invoke-WinUtilISOBrowse
|
||||||
|
|||||||
@@ -273,22 +273,43 @@
|
|||||||
<Style TargetType="ComboBox">
|
<Style TargetType="ComboBox">
|
||||||
<Setter Property="Foreground" Value="{DynamicResource ComboBoxForegroundColor}" />
|
<Setter Property="Foreground" Value="{DynamicResource ComboBoxForegroundColor}" />
|
||||||
<Setter Property="Background" Value="{DynamicResource ComboBoxBackgroundColor}" />
|
<Setter Property="Background" Value="{DynamicResource ComboBoxBackgroundColor}" />
|
||||||
|
<Setter Property="MinWidth" Value="{DynamicResource ButtonWidth}" />
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="ComboBox">
|
<ControlTemplate TargetType="ComboBox">
|
||||||
<Grid>
|
<Grid>
|
||||||
<ToggleButton x:Name="ToggleButton"
|
<Border x:Name="OuterBorder"
|
||||||
Background="{TemplateBinding Background}"
|
BorderBrush="{DynamicResource BorderColor}"
|
||||||
BorderBrush="{TemplateBinding Background}"
|
BorderThickness="1"
|
||||||
BorderThickness="0"
|
CornerRadius="{DynamicResource ButtonCornerRadius}"
|
||||||
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
|
Background="{TemplateBinding Background}">
|
||||||
ClickMode="Press">
|
<ToggleButton x:Name="ToggleButton"
|
||||||
<TextBlock Text="{TemplateBinding SelectionBoxItem}"
|
Background="Transparent"
|
||||||
Foreground="{TemplateBinding Foreground}"
|
BorderThickness="0"
|
||||||
Background="Transparent"
|
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2"
|
ClickMode="Press">
|
||||||
/>
|
<Grid>
|
||||||
</ToggleButton>
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
Text="{TemplateBinding SelectionBoxItem}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
Background="Transparent"
|
||||||
|
HorizontalAlignment="Left" VerticalAlignment="Center"
|
||||||
|
Margin="6,3,2,3"/>
|
||||||
|
<Path Grid.Column="1"
|
||||||
|
Data="M 0,0 L 8,0 L 4,5 Z"
|
||||||
|
Fill="{TemplateBinding Foreground}"
|
||||||
|
Width="8" Height="5"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Stretch="Uniform"
|
||||||
|
Margin="4,0,6,0"/>
|
||||||
|
</Grid>
|
||||||
|
</ToggleButton>
|
||||||
|
</Border>
|
||||||
<Popup x:Name="Popup"
|
<Popup x:Name="Popup"
|
||||||
IsOpen="{TemplateBinding IsDropDownOpen}"
|
IsOpen="{TemplateBinding IsDropDownOpen}"
|
||||||
Placement="Bottom"
|
Placement="Bottom"
|
||||||
@@ -297,11 +318,11 @@
|
|||||||
PopupAnimation="Slide">
|
PopupAnimation="Slide">
|
||||||
<Border x:Name="DropDownBorder"
|
<Border x:Name="DropDownBorder"
|
||||||
Background="{TemplateBinding Background}"
|
Background="{TemplateBinding Background}"
|
||||||
BorderBrush="{TemplateBinding Foreground}"
|
BorderBrush="{DynamicResource BorderColor}"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CornerRadius="4">
|
CornerRadius="4">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<ItemsPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2"/>
|
<ItemsPresenter HorizontalAlignment="Left" VerticalAlignment="Center" Margin="4,2"/>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Border>
|
</Border>
|
||||||
</Popup>
|
</Popup>
|
||||||
@@ -1340,21 +1361,17 @@
|
|||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem Header="Win11ISO" Visibility="Collapsed" Name="WPFTab5">
|
<TabItem Header="Win11ISO" Visibility="Collapsed" Name="WPFTab5">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Margin="{DynamicResource TabContentMargin}">
|
<Grid Name="Win11ISOPanel" Margin="{DynamicResource TabContentMargin}" Background="Transparent">
|
||||||
<Grid Background="Transparent" Name="Win11ISOPanel">
|
<Grid.RowDefinitions>
|
||||||
<Grid.RowDefinitions>
|
<RowDefinition Height="Auto"/> <!-- Steps 1-4 -->
|
||||||
<RowDefinition Height="Auto"/> <!-- Step 1: Select ISO -->
|
<RowDefinition Height="*"/> <!-- Log / Status -->
|
||||||
<RowDefinition Height="Auto"/> <!-- Step 2: Mount & Verify -->
|
</Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/> <!-- Step 3: Modify install.wim -->
|
|
||||||
<RowDefinition Height="Auto"/> <!-- Step 4: Output Options -->
|
|
||||||
<RowDefinition Height="Auto"/> <!-- Log / Status -->
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
<!-- Steps 1-4 -->
|
||||||
<!-- STEP 1 : Select Windows 11 ISO -->
|
<StackPanel Grid.Row="0">
|
||||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
||||||
<Border Grid.Row="0" Name="WPFWin11ISOSelectSection" Style="{StaticResource BorderStyle}">
|
<!-- ─── STEP 1 : Select Windows 11 ISO ─────────────── -->
|
||||||
<Grid Margin="5">
|
<Grid Name="WPFWin11ISOSelectSection" Margin="5" HorizontalAlignment="Left" MinWidth="{DynamicResource ButtonWidth}">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
@@ -1441,16 +1458,12 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
<!-- ─── STEP 2 : Mount & Verify ISO ──────────────────── -->
|
||||||
<!-- STEP 2 : Mount & Verify ISO -->
|
<Grid Name="WPFWin11ISOMountSection"
|
||||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
Margin="5"
|
||||||
<Border Grid.Row="1"
|
Visibility="Collapsed"
|
||||||
Name="WPFWin11ISOMountSection"
|
HorizontalAlignment="Left" MinWidth="{DynamicResource ButtonWidth}">
|
||||||
Style="{StaticResource BorderStyle}"
|
|
||||||
Visibility="Collapsed">
|
|
||||||
<Grid Margin="5">
|
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
@@ -1472,6 +1485,13 @@
|
|||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
Width="Auto" Padding="12,0"
|
Width="Auto" Padding="12,0"
|
||||||
Height="{DynamicResource ButtonHeight}"/>
|
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="Exports all drivers from this machine and injects them into install.wim and boot.wim. Recommended for systems with unsupported NVMe or network controllers."/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Verification results panel -->
|
<!-- Verification results panel -->
|
||||||
@@ -1500,22 +1520,17 @@
|
|||||||
FontSize="{DynamicResource FontSize}"
|
FontSize="{DynamicResource FontSize}"
|
||||||
Foreground="{DynamicResource MainForegroundColor}"
|
Foreground="{DynamicResource MainForegroundColor}"
|
||||||
Background="{DynamicResource MainBackgroundColor}"
|
Background="{DynamicResource MainBackgroundColor}"
|
||||||
BorderBrush="{DynamicResource BorderColor}"
|
HorizontalAlignment="Left"
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
Margin="0,0,0,0"/>
|
Margin="0,0,0,0"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
<!-- ─── STEP 3 : Modify install.wim ───────────────────── -->
|
||||||
<!-- STEP 3 : Modify install.wim -->
|
<StackPanel Name="WPFWin11ISOModifySection"
|
||||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
Margin="5"
|
||||||
<Border Grid.Row="2"
|
Visibility="Collapsed"
|
||||||
Name="WPFWin11ISOModifySection"
|
HorizontalAlignment="Left" MinWidth="{DynamicResource ButtonWidth}">
|
||||||
Style="{StaticResource BorderStyle}"
|
|
||||||
Visibility="Collapsed">
|
|
||||||
<StackPanel Margin="5">
|
|
||||||
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
|
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
|
||||||
Foreground="{DynamicResource MainForegroundColor}" Margin="0,0,0,8">
|
Foreground="{DynamicResource MainForegroundColor}" Margin="0,0,0,8">
|
||||||
Step 3 - Modify install.wim
|
Step 3 - Modify install.wim
|
||||||
@@ -1534,15 +1549,12 @@
|
|||||||
Width="Auto" Padding="12,0"
|
Width="Auto" Padding="12,0"
|
||||||
Height="{DynamicResource ButtonHeight}"/>
|
Height="{DynamicResource ButtonHeight}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
<!-- ─── STEP 4 : Output Options ───────────────────────── -->
|
||||||
<!-- STEP 4 : Output Options -->
|
<StackPanel Name="WPFWin11ISOOutputSection"
|
||||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
Margin="5"
|
||||||
<Border Grid.Row="3"
|
Visibility="Collapsed"
|
||||||
Name="WPFWin11ISOOutputSection"
|
HorizontalAlignment="Left" MinWidth="{DynamicResource ButtonWidth}">
|
||||||
Style="{StaticResource BorderStyle}">
|
|
||||||
<StackPanel Margin="5">
|
|
||||||
<!-- Header row: title + Clean & Reset button -->
|
<!-- Header row: title + Clean & Reset button -->
|
||||||
<Grid Margin="0,0,0,12">
|
<Grid Margin="0,0,0,12">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
@@ -1579,7 +1591,7 @@
|
|||||||
Height="{DynamicResource ButtonHeight}"/>
|
Height="{DynamicResource ButtonHeight}"/>
|
||||||
<Button Grid.Column="2"
|
<Button Grid.Column="2"
|
||||||
Name="WPFWin11ISOChooseUSBButton"
|
Name="WPFWin11ISOChooseUSBButton"
|
||||||
Content="Write Directly to a USB Drive (erases drive)"
|
Content="Write Directly to a USB Drive (ERASES DRIVE)"
|
||||||
Foreground="OrangeRed"
|
Foreground="OrangeRed"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
Width="Auto" Padding="12,0"
|
Width="Auto" Padding="12,0"
|
||||||
@@ -1613,7 +1625,7 @@
|
|||||||
Margin="0,0,6,0"/>
|
Margin="0,0,6,0"/>
|
||||||
<Button Grid.Column="1"
|
<Button Grid.Column="1"
|
||||||
Name="WPFWin11ISORefreshUSBButton"
|
Name="WPFWin11ISORefreshUSBButton"
|
||||||
Content="↻ Refresh"
|
Content="Refresh"
|
||||||
Width="Auto" Padding="8,0"
|
Width="Auto" Padding="8,0"
|
||||||
Height="{DynamicResource ButtonHeight}"/>
|
Height="{DynamicResource ButtonHeight}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -1626,34 +1638,37 @@
|
|||||||
Margin="0,0,0,10"/>
|
Margin="0,0,0,10"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
</StackPanel>
|
||||||
<!-- Status / Log Output -->
|
|
||||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
||||||
<Border Grid.Row="4" Style="{StaticResource BorderStyle}">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock FontSize="{DynamicResource FontSize}" FontWeight="Bold"
|
|
||||||
Foreground="{DynamicResource MainForegroundColor}" Margin="0,0,0,6">
|
|
||||||
Status Log
|
|
||||||
</TextBlock>
|
|
||||||
<TextBox Name="WPFWin11ISOStatusLog"
|
|
||||||
IsReadOnly="True"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
Height="140" Padding="6"
|
|
||||||
Background="{DynamicResource MainBackgroundColor}"
|
|
||||||
Foreground="{DynamicResource MainForegroundColor}"
|
|
||||||
BorderBrush="{DynamicResource BorderColor}"
|
|
||||||
BorderThickness="1"
|
|
||||||
Text="Ready. Please select a Windows 11 ISO to begin."/>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
|
<!-- 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>
|
</Grid>
|
||||||
</ScrollViewer>
|
|
||||||
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
Reference in New Issue
Block a user