mirror of
https://github.com/ChrisTitusTech/winutil
synced 2026-04-06 14:48:31 +00:00
Fix OSCDIMG output and cleanup comments
This commit is contained in:
@@ -1,13 +1,9 @@
|
||||
function Invoke-WinUtilISORefreshUSBDrives {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Populates the USB drive ComboBox with all currently attached removable drives.
|
||||
#>
|
||||
$combo = $sync["WPFWin11ISOUSBDriveComboBox"]
|
||||
$combo.Items.Clear()
|
||||
|
||||
$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
|
||||
@@ -16,38 +12,26 @@ function Invoke-WinUtilISORefreshUSBDrives {
|
||||
}
|
||||
|
||||
foreach ($disk in $removable) {
|
||||
$sizeGB = [math]::Round($disk.Size / 1GB, 1)
|
||||
$label = "Disk $($disk.Number): $($disk.FriendlyName) [$sizeGB GB] - $($disk.PartitionStyle)"
|
||||
$combo.Items.Add($label)
|
||||
$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)."
|
||||
|
||||
# Store disk objects for later use
|
||||
$sync["Win11ISOUSBDisks"] = $removable
|
||||
}
|
||||
|
||||
function Invoke-WinUtilISOWriteUSB {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Erases the selected USB drive and writes the modified Windows 11 ISO
|
||||
content as a bootable installation drive (using DISM / robocopy approach).
|
||||
#>
|
||||
$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")
|
||||
[System.Windows.MessageBox]::Show("No modified ISO content found. Please complete Steps 1-3 first.", "Not Ready", "OK", "Warning")
|
||||
return
|
||||
}
|
||||
|
||||
$selectedIndex = $sync["WPFWin11ISOUSBDriveComboBox"].SelectedIndex
|
||||
if ($selectedIndex -lt 0 -or -not $usbDisks -or $selectedIndex -ge $usbDisks.Count) {
|
||||
[System.Windows.MessageBox]::Show(
|
||||
"Please select a USB drive from the dropdown.",
|
||||
"No Drive Selected", "OK", "Warning")
|
||||
[System.Windows.MessageBox]::Show("Please select a USB drive from the dropdown.", "No Drive Selected", "OK", "Warning")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -71,9 +55,9 @@ function Invoke-WinUtilISOWriteUSB {
|
||||
$runspace.ApartmentState = "STA"
|
||||
$runspace.ThreadOptions = "ReuseThread"
|
||||
$runspace.Open()
|
||||
$runspace.SessionStateProxy.SetVariable("sync", $sync)
|
||||
$runspace.SessionStateProxy.SetVariable("diskNum", $diskNum)
|
||||
$runspace.SessionStateProxy.SetVariable("contentsDir", $contentsDir)
|
||||
$runspace.SessionStateProxy.SetVariable("sync", $sync)
|
||||
$runspace.SessionStateProxy.SetVariable("diskNum", $diskNum)
|
||||
$runspace.SessionStateProxy.SetVariable("contentsDir", $contentsDir)
|
||||
|
||||
$script = [Management.Automation.PowerShell]::Create()
|
||||
$script.Runspace = $runspace
|
||||
@@ -87,6 +71,7 @@ function Invoke-WinUtilISOWriteUSB {
|
||||
$sync["WPFWin11ISOStatusLog"].ScrollToEnd()
|
||||
})
|
||||
}
|
||||
|
||||
function SetProgress($label, $pct) {
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
$sync.progressBarTextBlock.Text = $label
|
||||
@@ -95,38 +80,25 @@ function Invoke-WinUtilISOWriteUSB {
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
# ── Helper: find a free drive letter (D-Z) ──────────────────────────
|
||||
function Get-FreeDriveLetter {
|
||||
$used = (Get-PSDrive -PSProvider FileSystem -ErrorAction SilentlyContinue).Name
|
||||
foreach ($c in [char[]](68..90)) { # D..Z
|
||||
if ($used -notcontains [string]$c) { return $c }
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
# ── Phase 1: Clean the disk via diskpart ────────────────────────────
|
||||
# Only run "clean" here. "convert gpt" in diskpart requires the disk to
|
||||
# already be MBR; after a clean the disk is RAW, so convert gpt fails on
|
||||
# many systems. We use Initialize-Disk (which accepts RAW disks) instead.
|
||||
$dpScript1 = @"
|
||||
select disk $diskNum
|
||||
clean
|
||||
exit
|
||||
"@
|
||||
# Phase 1: Clean disk via diskpart
|
||||
$dpFile1 = Join-Path $env:TEMP "winutil_diskpart_$(Get-Random).txt"
|
||||
$dpScript1 | Set-Content -Path $dpFile1 -Encoding ASCII
|
||||
"select disk $diskNum`nclean`nexit" | Set-Content -Path $dpFile1 -Encoding ASCII
|
||||
Log "Running diskpart clean on Disk $diskNum..."
|
||||
$dpOut1 = diskpart /s $dpFile1 2>&1
|
||||
diskpart /s $dpFile1 2>&1 | Where-Object { $_ -match '\S' } | ForEach-Object { Log " diskpart: $_" }
|
||||
Remove-Item $dpFile1 -Force -ErrorAction SilentlyContinue
|
||||
$dpOut1 | Where-Object { $_ -match '\S' } | ForEach-Object { Log " diskpart: $_" }
|
||||
|
||||
# ── Phase 2: Initialize as GPT via PowerShell ────────────────────────
|
||||
# After "clean", Windows may still see the disk as initialized (stale
|
||||
# metadata). Initialize-Disk only accepts RAW disks; Set-Disk handles
|
||||
# already-initialized (MBR/GPT) disks with no partitions. Try both.
|
||||
# Phase 2: Initialize as GPT
|
||||
Start-Sleep -Seconds 2
|
||||
Update-Disk -Number $diskNum -ErrorAction SilentlyContinue
|
||||
$diskObj = Get-Disk -Number $diskNum -ErrorAction Stop
|
||||
@@ -138,32 +110,19 @@ exit
|
||||
Log "Disk $diskNum converted to GPT (was $($diskObj.PartitionStyle))."
|
||||
}
|
||||
|
||||
# ── Phase 3: Create partitions via diskpart ──────────────────────────
|
||||
# "create partition efi" is not supported on removable media.
|
||||
# A single FAT32 primary partition is all that is needed for a UEFI-
|
||||
# bootable Windows install USB - the firmware locates \EFI\Boot\bootx64.efi
|
||||
# on any FAT32 volume regardless of GPT partition type.
|
||||
# FAT32 label limit is 11 chars; "W11-yyMMdd" = 10, fits without trimming.
|
||||
# Phase 3: Create FAT32 partition via diskpart
|
||||
$volLabel = "W11-" + (Get-Date).ToString('yyMMdd')
|
||||
$dpScript2 = @"
|
||||
select disk $diskNum
|
||||
create partition primary
|
||||
format quick fs=fat32 label="$volLabel"
|
||||
exit
|
||||
"@
|
||||
$dpFile2 = Join-Path $env:TEMP "winutil_diskpart2_$(Get-Random).txt"
|
||||
$dpScript2 | Set-Content -Path $dpFile2 -Encoding ASCII
|
||||
$dpFile2 = Join-Path $env:TEMP "winutil_diskpart2_$(Get-Random).txt"
|
||||
"select disk $diskNum`ncreate partition primary`nformat quick fs=fat32 label=`"$volLabel`"`nexit" |
|
||||
Set-Content -Path $dpFile2 -Encoding ASCII
|
||||
Log "Creating partitions on Disk $diskNum..."
|
||||
$dpOut2 = diskpart /s $dpFile2 2>&1
|
||||
diskpart /s $dpFile2 2>&1 | Where-Object { $_ -match '\S' } | ForEach-Object { Log " diskpart: $_" }
|
||||
Remove-Item $dpFile2 -Force -ErrorAction SilentlyContinue
|
||||
$dpOut2 | Where-Object { $_ -match '\S' } | ForEach-Object { Log " diskpart: $_" }
|
||||
|
||||
SetProgress "Assigning drive letters..." 30
|
||||
Start-Sleep -Seconds 3 # allow Windows to settle after partition creation
|
||||
Start-Sleep -Seconds 3
|
||||
Update-Disk -Number $diskNum -ErrorAction SilentlyContinue
|
||||
|
||||
# ── Explicitly assign drive letters via PowerShell ───────────────────
|
||||
# This is reliable regardless of registry state, unlike diskpart assign.
|
||||
$partitions = Get-Partition -DiskNumber $diskNum -ErrorAction Stop
|
||||
Log "Partitions on Disk $diskNum after format: $($partitions.Count)"
|
||||
foreach ($p in $partitions) {
|
||||
@@ -171,12 +130,10 @@ exit
|
||||
}
|
||||
|
||||
$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."
|
||||
}
|
||||
|
||||
# Remove stale letter first (noops if none), then assign a fresh one
|
||||
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." }
|
||||
@@ -185,30 +142,22 @@ exit
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
$usbDrive = "${usbLetter}:"
|
||||
if (-not (Test-Path $usbDrive)) {
|
||||
throw "Drive $usbDrive is not accessible after letter assignment."
|
||||
}
|
||||
if (-not (Test-Path $usbDrive)) { throw "Drive $usbDrive is not accessible after letter assignment." }
|
||||
Log "USB data partition: $usbDrive"
|
||||
SetProgress "Copying Windows 11 files to USB..." 45
|
||||
|
||||
# ── Copy files (split large install.wim if > 4 GB for FAT32) ──
|
||||
# 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) {
|
||||
# FAT32 limit – split with DISM
|
||||
Log "install.wim is $wimSizeMB MB - splitting for FAT32 compatibility...This will take several minutes."
|
||||
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
|
||||
Split-WindowsImage -ImagePath $installWim -SplitImagePath $splitDest -FileSize 3800 -CheckIntegrity
|
||||
Log "install.wim split complete."
|
||||
|
||||
# Copy everything else (exclude install.wim)
|
||||
Log "Copying remaining files to USB..."
|
||||
$robocopyArgs = @($contentsDir, $usbDrive, "/E", "/XF", "install.wim", "/NFL", "/NDL", "/NJH", "/NJS")
|
||||
& robocopy @robocopyArgs
|
||||
& robocopy $contentsDir $usbDrive /E /XF install.wim /NFL /NDL /NJH /NJS
|
||||
} else {
|
||||
& robocopy $contentsDir $usbDrive /E /NFL /NDL /NJH /NJS
|
||||
}
|
||||
@@ -218,7 +167,6 @@ exit
|
||||
|
||||
SetProgress "Finalising USB drive..." 90
|
||||
Log "Files copied to USB."
|
||||
|
||||
SetProgress "USB write complete" 100
|
||||
Log "USB drive is ready for use."
|
||||
|
||||
@@ -227,21 +175,17 @@ exit
|
||||
"USB drive created successfully!`n`nYou can now boot from this drive to install Windows 11.",
|
||||
"USB Ready", "OK", "Info")
|
||||
})
|
||||
}
|
||||
catch {
|
||||
} 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")
|
||||
[System.Windows.MessageBox]::Show("USB write failed:`n`n$_", "USB Write Error", "OK", "Error")
|
||||
})
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
Start-Sleep -Milliseconds 800
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
$sync.progressBarTextBlock.Text = ""
|
||||
$sync.progressBarTextBlock.Text = ""
|
||||
$sync.progressBarTextBlock.ToolTip = ""
|
||||
$sync.ProgressBar.Value = 0
|
||||
$sync.ProgressBar.Value = 0
|
||||
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $true
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user