@@ -446,6 +446,53 @@ function Invoke-WinUtilISOModify {
446446 $script.BeginInvoke () | Out-Null
447447}
448448
449+ function Invoke-WinUtilISOCheckExistingWork {
450+ <#
451+ . SYNOPSIS
452+ Called when the Win11ISO tab is opened. Checks for a pre-existing
453+ WinUtil_Win11ISO temp directory and, if found, restores the working-
454+ directory state so the user can proceed directly to Step 4 (output
455+ options) without repeating the modification.
456+ #>
457+
458+ # If state is already loaded (e.g. user just switched tabs mid-session)
459+ # do nothing so we don't overwrite in-progress work.
460+ if ($sync [" Win11ISOContentsDir" ] -and (Test-Path $sync [" Win11ISOContentsDir" ])) {
461+ return
462+ }
463+
464+ $existingWorkDir = Get-Item - Path (Join-Path $env: TEMP " WinUtil_Win11ISO*" ) - ErrorAction SilentlyContinue |
465+ Where-Object { $_.PSIsContainer } |
466+ Sort-Object LastWriteTime - Descending |
467+ Select-Object - First 1
468+
469+ if (-not $existingWorkDir ) { return }
470+
471+ $isoContents = Join-Path $existingWorkDir.FullName " iso_contents"
472+ if (-not (Test-Path $isoContents )) { return }
473+
474+ # Restore state
475+ $sync [" Win11ISOWorkDir" ] = $existingWorkDir.FullName
476+ $sync [" Win11ISOContentsDir" ] = $isoContents
477+
478+ # Show Step 4 and collapse steps 1-3 (modification already happened)
479+ $sync [" WPFWin11ISOSelectSection" ].Visibility = " Collapsed"
480+ $sync [" WPFWin11ISOMountSection" ].Visibility = " Collapsed"
481+ $sync [" WPFWin11ISOModifySection" ].Visibility = " Collapsed"
482+ $sync [" WPFWin11ISOOutputSection" ].Visibility = " Visible"
483+
484+ # Notify via the status log
485+ $dirName = $existingWorkDir.Name
486+ $modified = $existingWorkDir.LastWriteTime.ToString (" yyyy-MM-dd HH:mm" )
487+ Write-Win11ISOLog " Existing working directory found: $ ( $existingWorkDir.FullName ) "
488+ Write-Win11ISOLog " Last modified: $modified — Skipping Steps 1-3 and resuming at Step 4."
489+ Write-Win11ISOLog " Click 'Clean & Reset' if you want to start over with a new ISO."
490+
491+ [System.Windows.MessageBox ]::Show(
492+ " A previous WinUtil ISO working directory was found:`n`n $ ( $existingWorkDir.FullName ) `n`n (Last modified: $modified )`n`n Step 4 (output options) has been restored so you can save the already-modified image.`n`n Click 'Clean & Reset' in Step 4 if you want to start over." ,
493+ " Existing Work Found" , " OK" , " Info" )
494+ }
495+
449496function Invoke-WinUtilISOCleanAndReset {
450497 <#
451498 . SYNOPSIS
@@ -600,7 +647,7 @@ function Invoke-WinUtilISOExport {
600647 }
601648
602649 if ($proc.ExitCode -eq 0 ) {
603- Set-WinUtilProgressBar - Label " ISO exported ✔ " - Percent 100
650+ Set-WinUtilProgressBar - Label " ISO exported" - Percent 100
604651 Write-Win11ISOLog " ISO exported successfully: $outputISO "
605652 [System.Windows.MessageBox ]::Show(
606653 " ISO exported successfully!`n`n $outputISO " ,
@@ -622,197 +669,3 @@ function Invoke-WinUtilISOExport {
622669 }
623670}
624671
625- function Invoke-WinUtilISORefreshUSBDrives {
626- <#
627- . SYNOPSIS
628- Populates the USB drive ComboBox with all currently attached removable drives.
629- #>
630- $combo = $sync [" WPFWin11ISOUSBDriveComboBox" ]
631- $combo.Items.Clear ()
632-
633- $removable = Get-Disk | Where-Object { $_.BusType -eq " USB" } | Sort-Object Number
634-
635- if ($removable.Count -eq 0 ) {
636- $combo.Items.Add (" No USB drives detected" )
637- $combo.SelectedIndex = 0
638- Write-Win11ISOLog " No USB drives detected."
639- return
640- }
641-
642- foreach ($disk in $removable ) {
643- $sizeGB = [math ]::Round($disk.Size / 1 GB , 1 )
644- $label = " Disk $ ( $disk.Number ) : $ ( $disk.FriendlyName ) [$sizeGB GB] — $ ( $disk.PartitionStyle ) "
645- $combo.Items.Add ($label )
646- }
647- $combo.SelectedIndex = 0
648- Write-Win11ISOLog " Found $ ( $removable.Count ) USB drive(s)."
649-
650- # Store disk objects for later use
651- $sync [" Win11ISOUSBDisks" ] = $removable
652- }
653-
654- function Invoke-WinUtilISOWriteUSB {
655- <#
656- . SYNOPSIS
657- Erases the selected USB drive and writes the modified Windows 11 ISO
658- content as a bootable installation drive (using DISM / robocopy approach).
659- #>
660- $contentsDir = $sync [" Win11ISOContentsDir" ]
661- $usbDisks = $sync [" Win11ISOUSBDisks" ]
662-
663- if (-not $contentsDir -or -not (Test-Path $contentsDir )) {
664- [System.Windows.MessageBox ]::Show(
665- " No modified ISO content found. Please complete Steps 1–3 first." ,
666- " Not Ready" , " OK" , " Warning" )
667- return
668- }
669-
670- $selectedIndex = $sync [" WPFWin11ISOUSBDriveComboBox" ].SelectedIndex
671- if ($selectedIndex -lt 0 -or -not $usbDisks -or $selectedIndex -ge $usbDisks.Count ) {
672- [System.Windows.MessageBox ]::Show(
673- " Please select a USB drive from the dropdown." ,
674- " No Drive Selected" , " OK" , " Warning" )
675- return
676- }
677-
678- $targetDisk = $usbDisks [$selectedIndex ]
679- $diskNum = $targetDisk.Number
680- $sizeGB = [math ]::Round($targetDisk.Size / 1 GB , 1 )
681-
682- $confirm = [System.Windows.MessageBox ]::Show(
683- " ALL data on Disk $diskNum ($ ( $targetDisk.FriendlyName ) , $sizeGB GB) will be PERMANENTLY ERASED.`n`n Are you sure you want to continue?" ,
684- " Confirm USB Erase" , " YesNo" , " Warning" )
685-
686- if ($confirm -ne " Yes" ) {
687- Write-Win11ISOLog " USB write cancelled by user."
688- return
689- }
690-
691- $sync [" WPFWin11ISOWriteUSBButton" ].IsEnabled = $false
692- Write-Win11ISOLog " Starting USB write to Disk $diskNum ..."
693-
694- $runspace = [Management.Automation.Runspaces.RunspaceFactory ]::CreateRunspace()
695- $runspace.ApartmentState = " STA"
696- $runspace.ThreadOptions = " ReuseThread"
697- $runspace.Open ()
698- $runspace.SessionStateProxy.SetVariable (" sync" , $sync )
699- $runspace.SessionStateProxy.SetVariable (" diskNum" , $diskNum )
700- $runspace.SessionStateProxy.SetVariable (" contentsDir" , $contentsDir )
701-
702- $script = [Management.Automation.PowerShell ]::Create()
703- $script.Runspace = $runspace
704- $script.AddScript ({
705-
706- function Log ($msg ) {
707- $ts = (Get-Date ).ToString(" HH:mm:ss" )
708- $sync [" WPFWin11ISOStatusLog" ].Dispatcher.Invoke([action ]{
709- $sync [" WPFWin11ISOStatusLog" ].Text += " `n [$ts ] $msg "
710- $sync [" WPFWin11ISOStatusLog" ].CaretIndex = $sync [" WPFWin11ISOStatusLog" ].Text.Length
711- $sync [" WPFWin11ISOStatusLog" ].ScrollToEnd()
712- })
713- }
714- function SetProgress ($label , $pct ) {
715- $sync [" WPFWin11ISOStatusLog" ].Dispatcher.Invoke([action ]{
716- $sync.progressBarTextBlock.Text = $label
717- $sync.progressBarTextBlock.ToolTip = $label
718- $sync.ProgressBar.Value = [Math ]::Max($pct , 5 )
719- })
720- }
721-
722- try {
723- SetProgress " Formatting USB drive..." 10
724-
725- # ── Diskpart script: clean, GPT, create ESP + data partitions ──
726- $dpScript = @"
727- select disk $diskNum
728- clean
729- convert gpt
730- create partition efi size=512
731- format quick fs=fat32 label="SYSTEM"
732- assign
733- create partition primary
734- format quick fs=fat32 label="WINPE"
735- assign
736- exit
737- "@
738- $dpFile = Join-Path $env: TEMP " winutil_diskpart_$ ( Get-Random ) .txt"
739- $dpScript | Set-Content - Path $dpFile - Encoding ASCII
740- Log " Running diskpart on Disk $diskNum ..."
741- diskpart / s $dpFile | Out-Null
742- Remove-Item $dpFile - Force
743-
744- SetProgress " Identifying USB partitions..." 30
745- Start-Sleep - Seconds 3 # let Windows assign drive letters
746-
747- # Find newly assigned drive letter for the data partition
748- $usbVol = Get-Partition - DiskNumber $diskNum |
749- Where-Object { $_.Type -eq " Basic" } |
750- Get-Volume |
751- Where-Object { $_.FileSystemLabel -eq " WINPE" } |
752- Select-Object - First 1
753-
754- if (-not $usbVol ) {
755- throw " Could not locate the formatted USB data partition. Drive letter may not have been assigned automatically."
756- }
757-
758- $usbDrive = " $ ( $usbVol.DriveLetter ) :"
759- Log " USB data partition: $usbDrive "
760- SetProgress " Copying Windows 11 files to USB..." 45
761-
762- # ── Copy files (split large install.wim if > 4 GB for FAT32) ──
763- $installWim = Join-Path $contentsDir " sources\install.wim"
764- if (Test-Path $installWim ) {
765- $wimSizeMB = [math ]::Round((Get-Item $installWim ).Length / 1 MB )
766- if ($wimSizeMB -gt 3800 ) {
767- # FAT32 limit – split with DISM
768- Log " install.wim is $wimSizeMB MB – splitting for FAT32 compatibility..."
769- $splitDest = Join-Path $usbDrive " sources\install.swm"
770- New-Item - ItemType Directory - Path (Split-Path $splitDest ) - Force | Out-Null
771- Split-WindowsImage - ImagePath $installWim `
772- - SplitImagePath $splitDest `
773- - FileSize 3800 - CheckIntegrity | Out-Null
774- Log " install.wim split complete."
775-
776- # Copy everything else (exclude install.wim)
777- $robocopyArgs = @ ($contentsDir , $usbDrive , " /E" , " /XF" , " install.wim" , " /NFL" , " /NDL" , " /NJH" , " /NJS" )
778- & robocopy @robocopyArgs | Out-Null
779- } else {
780- & robocopy $contentsDir $usbDrive / E / NFL / NDL / NJH / NJS | Out-Null
781- }
782- } else {
783- & robocopy $contentsDir $usbDrive / E / NFL / NDL / NJH / NJS | Out-Null
784- }
785-
786- SetProgress " Finalising USB drive..." 90
787- Log " Files copied to USB."
788-
789- SetProgress " USB write complete ✔" 100
790- Log " USB drive is ready for use."
791-
792- $sync [" WPFWin11ISOStatusLog" ].Dispatcher.Invoke([action ]{
793- [System.Windows.MessageBox ]::Show(
794- " USB drive created successfully!`n`n You can now boot from this drive to install Windows 11." ,
795- " USB Ready" , " OK" , " Info" )
796- })
797- }
798- catch {
799- Log " ERROR during USB write: $_ "
800- $sync [" WPFWin11ISOStatusLog" ].Dispatcher.Invoke([action ]{
801- [System.Windows.MessageBox ]::Show(
802- " USB write failed:`n`n $_ " ,
803- " USB Write Error" , " OK" , " Error" )
804- })
805- }
806- finally {
807- Start-Sleep - Milliseconds 800
808- $sync [" WPFWin11ISOStatusLog" ].Dispatcher.Invoke([action ]{
809- $sync.progressBarTextBlock.Text = " "
810- $sync.progressBarTextBlock.ToolTip = " "
811- $sync.ProgressBar.Value = 0
812- $sync [" WPFWin11ISOWriteUSBButton" ].IsEnabled = $true
813- })
814- }
815- }) | Out-Null
816-
817- $script.BeginInvoke () | Out-Null
818- }
0 commit comments