Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 35 additions & 8 deletions functions/private/Invoke-WinUtilISOUSB.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,24 @@ function Invoke-WinUtilISOWriteUSB {
try {
SetProgress "Formatting USB drive..." 10

# Phase 1: Clean disk via diskpart
# Phase 1: Clean disk via diskpart (retry once if the drive is not yet ready)
$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: $_" }
$dpCleanOut = diskpart /s $dpFile1 2>&1
$dpCleanOut | Where-Object { $_ -match '\S' } | ForEach-Object { Log " diskpart: $_" }
Remove-Item $dpFile1 -Force -ErrorAction SilentlyContinue

if (($dpCleanOut -join ' ') -match 'device is not ready') {
Log "Disk $diskNum was not ready; waiting 5 seconds and retrying clean..."
Start-Sleep -Seconds 5
Update-Disk -Number $diskNum -ErrorAction SilentlyContinue
$dpFile1b = Join-Path $env:TEMP "winutil_diskpart_$(Get-Random).txt"
"select disk $diskNum`nclean`nexit" | Set-Content -Path $dpFile1b -Encoding ASCII
diskpart /s $dpFile1b 2>&1 | Where-Object { $_ -match '\S' } | ForEach-Object { Log " diskpart: $_" }
Remove-Item $dpFile1b -Force -ErrorAction SilentlyContinue
}

# Phase 2: Initialize as GPT
Start-Sleep -Seconds 2
Update-Disk -Number $diskNum -ErrorAction SilentlyContinue
Expand All @@ -122,7 +133,8 @@ function Invoke-WinUtilISOWriteUSB {
Log "Disk $diskNum converted to GPT (was $($diskObj.PartitionStyle))."
}

# Phase 3: Create FAT32 partition via diskpart
# Phase 3: Create FAT32 partition via diskpart, then format with Format-Volume
# (diskpart's 'format' command can fail with "no volume selected" on fresh/never-formatted drives)
$volLabel = "W11-" + (Get-Date).ToString('yyMMdd')
$dpFile2 = Join-Path $env:TEMP "winutil_diskpart2_$(Get-Random).txt"
$maxFat32PartitionMB = 32768
Expand All @@ -136,29 +148,38 @@ function Invoke-WinUtilISOWriteUSB {
@(
"select disk $diskNum"
$createPartitionCommand
"select partition 1"
"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
SetProgress "Formatting USB partition..." 25
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)"
Log "Partitions on Disk $diskNum after creation: $($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."
throw "Could not find the Basic partition on Disk $diskNum after creation."
}

# Format using Format-Volume (reliable on fresh drives; diskpart format fails
# with 'no volume selected' when the partition has never been formatted before)
Log "Formatting Partition $($winpePart.PartitionNumber) as FAT32 (label: $volLabel)..."
Get-Partition -DiskNumber $diskNum -PartitionNumber $winpePart.PartitionNumber |
Format-Volume -FileSystem FAT32 -NewFileSystemLabel $volLabel -Force -Confirm:$false | Out-Null
Log "Partition $($winpePart.PartitionNumber) formatted as FAT32."

SetProgress "Assigning drive letters..." 30
Start-Sleep -Seconds 2
Update-Disk -Number $diskNum -ErrorAction SilentlyContinue

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." }
Expand All @@ -167,6 +188,12 @@ function Invoke-WinUtilISOWriteUSB {
Start-Sleep -Seconds 2

$usbDrive = "${usbLetter}:"
$retries = 0
while (-not (Test-Path $usbDrive) -and $retries -lt 6) {
$retries++
Log "Waiting for $usbDrive to become accessible (attempt $retries/6)..."
Start-Sleep -Seconds 2
}
if (-not (Test-Path $usbDrive)) { throw "Drive $usbDrive is not accessible after letter assignment." }
Log "USB data partition: $usbDrive"

Expand Down
26 changes: 0 additions & 26 deletions functions/private/Test-WinUtilInternetConnection.ps1

This file was deleted.

9 changes: 5 additions & 4 deletions scripts/main.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ $maxthreads = [int]$env:NUMBER_OF_PROCESSORS
$hashVars = New-object System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'sync',$sync,$Null
$debugVar = New-object System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'DebugPreference',$DebugPreference,$Null
$uiVar = New-object System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'PARAM_NOUI',$PARAM_NOUI,$Null
$offlineVar = New-object System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'PARAM_OFFLINE',$PARAM_OFFLINE,$Null
$InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()

# Add the variable to the session state
$InitialSessionState.Variables.Add($hashVars)
$InitialSessionState.Variables.Add($debugVar)
$InitialSessionState.Variables.Add($uiVar)
$InitialSessionState.Variables.Add($offlineVar)

# Get every private function and add them to the session state
$functions = Get-ChildItem function:\ | Where-Object { $_.Name -imatch 'winutil|WPF' }
Expand Down Expand Up @@ -350,11 +352,10 @@ $sync["Form"].Add_ContentRendered({
Write-Debug "Unable to retrieve information about the primary monitor."
}

# Check internet connectivity and disable install tab if offline
#$isOnline = Test-WinUtilInternetConnection
$isOnline = $true # Temporarily force online mode until we can resolve false negatives
if ($PARAM_OFFLINE) {
# Show offline banner
$sync.WPFOfflineBanner.Visibility = [System.Windows.Visibility]::Visible

if (-not $isOnline) {
# Disable the install tab
$sync.WPFTab1BT.IsEnabled = $false
$sync.WPFTab1BT.Opacity = 0.5
Expand Down
8 changes: 7 additions & 1 deletion scripts/start.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
param (
[string]$Config,
[switch]$Run,
[switch]$Noui
[switch]$Noui,
[switch]$Offline
)

if ($Config) {
Expand All @@ -27,6 +28,11 @@ if ($Noui) {
$PARAM_NOUI = $true
}

$PARAM_OFFLINE = $false
if ($Offline) {
$PARAM_OFFLINE = $true
}


if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Output "Winutil needs to be run as Administrator. Attempting to relaunch."
Expand Down
10 changes: 8 additions & 2 deletions xaml/inputXML.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -943,13 +943,19 @@
</Window.Resources>
<Grid Background="{DynamicResource MainBackgroundColor}" ShowGridLines="False" Name="WPFMainGrid" Width="Auto" Height="Auto" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Background="{DynamicResource MainBackgroundColor}">
<!-- Offline banner -->
<Border Name="WPFOfflineBanner" Grid.Row="0" Background="#8B0000" Visibility="Collapsed" Padding="6,4">
<TextBlock Text="&#x26A0; Offline Mode - No Internet Connection" Foreground="White" FontWeight="Bold"
HorizontalAlignment="Center" FontSize="13" Background="Transparent"/>
</Border>
<Grid Grid.Row="1" Background="{DynamicResource MainBackgroundColor}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <!-- Navigation buttons -->
<ColumnDefinition Width="*"/> <!-- Search bar and buttons -->
Expand Down Expand Up @@ -1192,7 +1198,7 @@
</Grid>
</Grid>

<TabControl Name="WPFTabNav" Background="Transparent" Width="Auto" Height="Auto" BorderBrush="Transparent" BorderThickness="0" Grid.Row="1" Grid.Column="0" Padding="-1">
<TabControl Name="WPFTabNav" Background="Transparent" Width="Auto" Height="Auto" BorderBrush="Transparent" BorderThickness="0" Grid.Row="2" Grid.Column="0" Padding="-1">
<TabItem Header="Install" Visibility="Collapsed" Name="WPFTab1">
<Grid Background="Transparent" >

Expand Down
Loading