Win10+ Setup Disk (Works with UEFI Secure Boot / BIOS / Install.wim over 4 GB)

Discussion in 'MDL Projects and Applications' started by rpo, Mar 18, 2019.

  1. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,509
    1,561
    60
  2. freddie-o

    freddie-o MDL Expert

    Jul 29, 2009
    1,613
    2,697
    60
    #442 freddie-o, Mar 23, 2025
    Last edited: Mar 23, 2025
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  3. Carlos Detweiller

    Carlos Detweiller Emperor of Ice-Cream
    Staff Member

    Dec 21, 2012
    7,087
    8,431
    240
    I guess one progress bar for the current file and the second one for the totals.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  4. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,509
    1,561
    60
    #444 rpo, Mar 23, 2025
    Last edited: Mar 24, 2025
    (OP)
    As @Carlos Detweiller said, the upper bar is for the global stats and the lower for the current file.
    The same form is used do display the screens.
    The -Window parameter on the powershell command line can be changed.
    The bar for the current file is not significant for small files because the bar are updated after 4Mbytes are processed.
    Here is my script :
    Code:
    <# : standard way of doing hybrid batch + powershell scripts
    @title Win10+ Setup Disk 3.4 &color 3E
    @powershell -noprofile -Window max -c "$param='%*';$ScriptPath='%~f0';iex((Get-Content('%~f0') -Raw))"&exit/b
    #>
    $OnlyUSBsticks="NO"
    #
    $host.ui.rawui.windowtitle = ($title=$host.ui.rawui.windowtitle -replace "^.*  ")
    #
    #
    #   Homepage: https://forums.mydigitallife.net/threads/win10-setup-disk-works-with-uefi-secure-boot-bios-install-wim-over-4-gb.79268/
    #   Credits: @rpo, @freddie-o, @BAU & @abbodi1406
    #
    Add-Type -AssemblyName Microsoft.VisualBasic
    Function MsgBox {
    # learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/msgbox-function
    [Microsoft.VisualBasic.Interaction]::MsgBox(
        $Args[0],"$($Args[1]),$($Args[2]),SystemModal",$title)}
    #
    #   This script must be executed with admin privilege
    #
    #   Test Administrator privileges
    If (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
    {
        $uac_error="Elevating UAC for Administrator Privileges failed"
        #   The following test is to avoid infinite looping if elevating UAC for Administrator Privileges failed
        If($param -eq $uac_error){
            $msg="$uac_error.`r`nRight click on the script and select ""Run as administrator""."
            MsgBox $msg    "OKOnly" "Critical" >$Null;exit}
    #   Restart the script to get Administrator privileges and exit
        Start-Process "$ScriptPath" $uac_error -Verb runAs; exit
    }
    #
    Add-Type -AssemblyName System.Windows.Forms # Load the class System.Windows.Forms
    #
    # Filebrowser dialog object
    $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{
        Multiselect = $false                 # Only one file can be chosen
        Filter = 'ISO images (*.iso)|*.iso' # Select only iso files
    }
    #
    Function Update_bcd ($partition){
    #    Set bootmenupolicy Legacy for $partition
    bcdedit /store "$partition\boot\bcd" /set '{default}' bootmenupolicy Legacy >$Null
    bcdedit /store "$partition\EFI\Microsoft\boot\bcd" /set '{default}' bootmenupolicy Legacy >$Null
    remove-item "$partition\boot\bcd.*" -force
    remove-item "$partition\EFI\Microsoft\boot\bcd.*" -force
    }
    # **************************************************
    $msg="Would you like to show only USB sticks and hide external hard disks?"
    $filter="Status='OK' and (InterfaceType='usb'and MediaType='Removable Media')"
    # YES : show only usb sticks
    # NO  : show usb sticks and external hard disks
    $OnlyUSBsticks=MsgBox $msg "YESNO" "Question"
    If($OnlyUSBsticks -eq "NO"){$filter="$filter or MediaType='External hard disk media'"}
    # **************************************************
    $delay=15
    While(!($Disks=Get-CimInstance -ClassName Win32_DiskDrive -Filter $filter)){
        $msg="No USB sticks were detected. Plug in your USB stick first and retry after $delay seconds or Cancel to exit."
        If((MsgBox $msg    "RetryCancel" "Question") -eq "Cancel"){exit}
        Start-Sleep $delay >$Null}
    #
    #winform dimensions
    $height=270
    $width=420
    $Form=New-Object System.Windows.Forms.Form # Create the screen form (window)
    $Form.TopMost = $True
    $Form.ShowIcon=$False
    $Form.ControlBox=$False
    # Set the title and size of the window:
    $Form.Text="$Title"
    $Form.Font='Consolas,10'
    $Form.Width=$width ; $Form.Height=$height
    $Form.StartPosition = "CenterScreen"
    $Form.KeyPreview = $True
    $Form.Add_KeyDown({if ($_.KeyCode -eq "Enter"){$OKButton.PerformClick()}})         # Enter = click OK button
    $Form.Add_KeyDown({if ($_.KeyCode -eq "Escape"){$CancelButton.PerformClick()}})    # Escape = click on Cancel button
    $Form.SizeGripStyle = "Hide"
    $form.ShowInTaskbar = $False
    $form.MaximizeBox = $False
    $form.MinimizeBox = $False
    [System.Windows.Forms.Application]::EnableVisualStyles();
    
    # Add a Source Windows ISO label on the form
    $SourceISO=New-Object System.Windows.Forms.Label
    $SourceISO.Location=New-Object System.Drawing.Point(20,30)
    $SourceISO.Size=New-Object System.Drawing.Size(200,20)
    $SourceISO.text="Source Windows ISO"
    $Form.Controls.Add($SourceISO)
    #
    # Add the ISO file name on the form
    $ISOFile=New-Object System.Windows.Forms.Label
    $ISOFile.Location=New-Object System.Drawing.Point(20,50)
    $ISOFile.Text=" "
    $ISOFile.Size=New-Object System.Drawing.Size(364,24)
    $ISOFile.Backcolor = "White"
    $ISOFile.BorderStyle = "FixedSingle"
    $Form.Controls.Add($ISOFile)
    #
    # Add a Target USB Disk label on the form
    $TargetUSB=New-Object System.Windows.Forms.Label
    $TargetUSB.Location=New-Object System.Drawing.Point(20,100)
    $TargetUSB.Text="Target USB Disk"
    $TargetUSB.Size=New-Object System.Drawing.Size(200,20)
    $Form.Controls.Add($TargetUSB)
    #
    # Create a USB Disk drop-down list and fill it
    $USBDiskList=New-Object System.Windows.Forms.ComboBox
    $USBDiskList.DropDownStyle=[System.Windows.Forms.ComboBoxStyle]::DropDownList
    $USBDiskList.Location=New-Object System.Drawing.Point(20,120)
    $USBDiskList.Size=New-Object System.Drawing.Size(363,22)
    $Form.Controls.Add($SelectUSBDiskList)
    #
    # Add a Select ISO button on the form
    $SelectISOButton=New-Object System.Windows.Forms.Button
    $SelectISOButton.Location=New-Object System.Drawing.Point(20,185)
    $SelectISOButton.Text="Select ISO"
    $SelectISOButton.Size=New-Object System.Drawing.Size(110,26)
    $Form.Controls.Add($SelectISOButton)
    #
    # Add a Create Setup Disk button on the form
    $CreateDiskButton=New-Object System.Windows.Forms.Button
    $CreateDiskButton.Location=New-Object System.Drawing.Point(140,185)
    $CreateDiskButton.Text="Create Setup Disk"
    $CreateDiskButton.Size=New-Object System.Drawing.Size(155,26)
    $Form.Controls.Add($CreateDiskButton)
    $CreateDiskButton.Enabled = $false
    $CreateDiskButton.DialogResult = "OK"
    #
    # Add a Cancel button on the form
    $CancelButton=New-Object System.Windows.Forms.Button
    $CancelButton.Location=New-Object System.Drawing.Point(305,185)
    $CancelButton.Text="Cancel"
    $CancelButton.Size=New-Object System.Drawing.Size(80,26)
    $Form.Controls.Add($CancelButton)
    $CancelButton.DialogResult = "Cancel"
    #
    $SelectISOButton.Add_Click({
    If ($FileBrowser.ShowDialog() -ne "Cancel"){ # if Cancel, just ignore
        $Global:ImagePath=$FileBrowser.filename    # return the file name
        $ISOFile.Text=Split-Path -Path $ImagePath -leaf # extract filename and extension (iso)
        if(($ISOFile.Text).length -gt 44){
            $ISOFile.Text=$ImagePath.PadRight(100).substring(0,43)+"..."}
        $CreateDiskButton.Enabled = $true
        $CreateDiskButton.Focus()}})
    #
    $USBDisks=@() # array with USB disk number
    Foreach ($Disk in $Disks){
        $FriendlyName=($Disk.Caption).PadRight(40).substring(0,35)
        $USBDisks+=$Disk.Index
        $USBDiskList.Items.Add(("{0,-30}{1,10:n2} GB" -f $FriendlyName,($Disk.Size/1GB))) >$Null
    <#
        Foreach ($Partition in (Get-Partition $Disk.Index)) {
            If(!$Partition.DriveLetter){$AccessPath="  "}Else{$AccessPath=$Partition.DriveLetter+":"}
            $USBDisks+=$Disk.Index
            $USBDiskList.Items.Add((" ({0,2}){1,-30}{2,10:n2} GB" -f $AccessPath,($Partition|
                Get-Volume).FileSystemLabel.PadRight(25).substring(0,25),($Partition.Size/1GB))) >$Null
        }
    #>
    }
    $form.Controls.Add($USBDiskList)
    $USBDiskList.SelectedIndex=0
    #
    $Groupbox=New-Object System.Windows.Forms.Groupbox # Add a group box on the form
    $Groupbox.Location=New-Object System.Drawing.Point(7,0)
    $Groupbox.Text=""
    $Groupbox.Size=New-Object System.Drawing.Size(390,225)
    $Form.Controls.Add($Groupbox)
    #
    # create label
    $label1 = New-Object system.Windows.Forms.Label
    $label1.Left=5
    $label1.Top= 10
    $label1.Width= $width - 20
    #adjusted height to accommodate progress 
    $label1.Height=150
    $label1.Font= "Verdana"
    #optional to show border
    #$label1.BorderStyle=1
    $msg=@"
    1.) Click "Select ISO" to select your source Windows ISO
    
    2.) Select your "Target USB disk" from the drop-down menu
    
    3.) Click "Create Setup Disk" to start creating your Windows setup disk
    "@
    MsgBox "$msg" "OKOnly" "Information" >$Null
    #
    if($form.ShowDialog() -eq "Cancel") {exit}
    #
    $Form.Controls.Remove($SourceISO)
    $Form.Controls.Remove($ISOFile)
    $Form.Controls.Remove($TargetUSB)
    $Form.Controls.Remove($SelectUSBDiskList)
    $Form.Controls.Remove($SelectISOButton)
    $Form.Controls.Remove($CreateDiskButton)
    $Form.Controls.Remove($CancelButton)
    $Form.Controls.Remove($USBDiskList)
    $Form.Controls.Remove($Groupbox)
    #
    # At this point the mounted USB disk and the iso image file path are defined
    #
    $USB=$USBDisks[$USBDiskList.SelectedIndex]
    #
    $msg=@"
    WARNING!`r`n
    Your USB disk will be converted to MBR scheme, repartitioned and reformatted.`r`n
    All partitions and data currently stored in the USB disk will be erased.`r`n
    Are you sure you want to continue?
    "@
    If((MsgBox $msg "YESNO" "Exclamation") -eq "NO"){exit}
    Clear-Host
    
    #add the label to the form
    $form.controls.add($label1)
    #
    #"Mounting and checking to see if the ISO image was mounted"
    #   Check if iso already mounted by getting driveletter
    If($ISO=(Get-DiskImage $ImagePath|Get-Volume).DriveLetter){$Mounted=$True}Else
    #    Mount iso and get drive letter
        {$Mounted=$False;If(!($ISO=(Mount-DiskImage $ImagePath|Get-Volume).DriveLetter)){Exit}}
    $ISO=$ISO+":"
    #
    Stop-Service ShellHWDetection -ErrorAction SilentlyContinue >$Null
    $ProgressPreference="SilentlyContinue"
    $label1.Text = "Cleaning the USB disk and converting it to MBR partition scheme"
    $form.Show()
    #
    "Select disk $USB`nclean`nconvert MBR`nexit"|diskpart >$Null
    If($LASTEXITCODE -ne 0){
        $msg="Diskpart operations failed with error code $LASTEXITCODE."
        $msg;pause }
    $label1.Text = $label1.Text+"`r`n`r`nCreating the FAT32 boot partition"
    $form.Refresh()
    $usbfat32=(New-Partition -DiskNumber $usb -Size 1GB -AssignDriveLetter|
        Format-Volume -FileSystem FAT32 -NewFileSystemLabel "BOOT").DriveLetter + ":"
    $label1.Text = $label1.Text+"`r`n`r`nCreating the NTFS setup partition & marking partition as active"
    $form.Refresh()
    $usbntfs=(New-Partition -DiskNumber $usb -UseMaximumSize -AssignDriveLetter -IsActive|
        Format-Volume -FileSystem NTFS -NewFileSystemLabel "SETUP").DriveLetter + ":"
    Start-Service ShellHWDetection -erroraction silentlycontinue >$Null
    # *********************************************************************
    $form.controls.Remove($label1)
    # 
    $label1 = New-Object system.Windows.Forms.Label
    $label1.Text = "not started"
    $label1.Left=5
    $label1.Top= 10
    $label1.Width= $width - 20
    $label1.Height=65
    $label1.Font= "Verdana"
    #$label1.BorderStyle=1
    $form.controls.add($label1)
    # 
    $label2 = New-Object system.Windows.Forms.Label
    $label2.Text = "not started"
    $label2.Left=5
    $label2.Top= 130
    $label2.Width= $width - 20
    $label2.Height=65
    $label2.Font= "Verdana"
    #$label2.BorderStyle=1
    $form.controls.add($label2)
    
    $progressBar1 = New-Object System.Windows.Forms.ProgressBar
    $progressBar1.Value = 0
    $progressBar1.Style="Continuous"
    #
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Width = $width - 30
    $System_Drawing_Size.Height = 20
    $progressBar1.Size = $System_Drawing_Size
    $progressBar1.Left = 5
    $progressBar1.Top = 70
    $form.Controls.Add($progressBar1)
    #
    $progressBar2 = New-Object System.Windows.Forms.ProgressBar
    $progressBar2.Name = 'progressBar2'
    $progressBar2.Value = 0
    $progressBar2.Style="Continuous"
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Width = $width - 30
    $System_Drawing_Size.Height = 20
    $progressBar2.Size = $System_Drawing_Size
    $progressBar2.Left = 5
    $progressBar2.Top = 200
    $form.Controls.Add($progressBar2)
    $form.Show()| out-null
    $form.Focus() | out-null
    #
    # *****************************************************************************
    #
    Function Copy_With_Progression {
    # suffix 1 : variables for global process
    # suffix 2 : variables for one file process
    [long]$allbytes = ($files | measure -Sum length).Sum
    [long]$total1 = 0 # bytes done
    $index = 0
    $filescount = $files.Count
    $sw1 = [Diagnostics.Stopwatch]::StartNew()
    $Buffer = New-Object Byte[] (4MB) # 4MB buffer
    ForEach ($file in $files) {
        $filefullname = $file.fullname     
        $index++
        # build destination path for this file (for example H:\sources\boot.wim)     
        $destfile = "$Dest_device$((Split-Path $filefullname -NoQualifier))" 
        $destdir= Split-Path $destfile -Parent
        # if it doesn't exist, create it
        if (!(Test-Path $destdir)){New-Item -ItemType Directory "$destdir" -Force >$Null}
        $SourceFile = [io.file]::OpenRead($filefullname)
        $DestinationFile = [io.file]::Create($destfile)
        $Filelength=$File.Length
        $sw2 = [Diagnostics.Stopwatch]::StartNew()
        [long]$total2 = [long]$count = 0
        do {
            # copy 4MB of src file to dst file
            $count = $SourceFile.Read($buffer, 0, $buffer.Length)
            $DestinationFile.Write($buffer, 0, $count)
            # this is just write-progress
            # uses stopwatch to determine how long is left
            $total2 += $count
            $total1 += $count
    #        Statistics for current file   
            if ($Filelength -gt 1){
                $pctcomp2 = $total2 * 100 / $FileLength}else{$pctcomp2 = 100}
            [int]$secselapsed2 = $sw2.Elapsed.TotalSeconds   
            if ($secselapsed2 -ne 0){
               [single]$xferrate = $total2 / $secselapsed2 / 1mb}else{[single]$xferrate = 0.0}
            if ($total2 -gt 0) {       
                [int]$secsleft2=$secselapsed2/$total2*$Filelength -$secselapsed2}else{[int]$secsleft2=0}                           
    #        Global statistics
            $pctcomp1 = $total1 * 100/ $allbytes
            [int]$secselapsed1 = $sw1.Elapsed.TotalSeconds   
            if ($total1 -gt 0) {       
                [int]$secsleft1=$secselapsed1/$total1*$Filelength -$secselapsed1}else{[int]$secsleft1=0}                   
            # Global
            $Form.Text="Copying $destfile"
            $progressBar1.Value = $pctcomp1
            $label1.text="{0,5} of $filescount files  - {1,5} left" -f $index,($filescount - $index) 
            $label1.text="$($label1.text){0,20:0} MB of {1,6:0} MB" -f ($total1/1MB),($allbytes/1MB)
            $label1.text="$($label1.text)`r`n`r`nPercent complete = {0,3:0}%      {1,4} minutes {2,2} seconds remaining" -f $pctcomp1,[math]::Truncate($secsleft1/60),($secsleft1%60)         
            # Current file
            $progressBar2.Value = $pctcomp2
            $label2.text="Copying file @ {0,6:n2} MB/s" -f $xferrate 
            $label2.text="$($label2.text){0,16:0} MB of {1,6:0} MB" -f ($total2/1MB),($filelength/1MB) 
            $label2.text="$($label2.text)`r`n`r`nPercent complete = {0,3:0}%      {1,4} minutes {2,2} seconds remaining" -f $pctcomp2,[math]::Truncate($secsleft2/60),($secsleft2%60) 
           
            $form.Refresh()
        } while ($count -gt 0)
        $sw2.Stop()
        $sw2.Reset()
        $SourceFile.Close()
        $DestinationFile.Close()
    }
    $sw1.Stop()
    $sw1.Reset()
    $Buffer=$Null
    #
    }# End Function Copy_With_Progression
    #
    #"`r`nCopying boot files to the fat32 boot partition $usbfat32"
    $files = Get-ChildItem $iso\boot, $iso\efi, 
        $iso\sources\boot.wim, $iso\bootmgr.*, $iso\bootmgr -Recurse -File -Force
    $Dest_device="$usbfat32"
    Copy_With_Progression   
    #
    Update_BCD $usbfat32
    #"`r`nRemoving the drive letter to hide the fat32 boot partition"
    Get-Volume ($usbfat32 -replace ".$")|Get-Partition|
        Remove-PartitionAccessPath -accesspath $usbfat32
    #
    #"`r`nCopying contents of the ISO to the NTFS setup partition $usbntfs"
    $files = Get-ChildItem $iso -Recurse -File -Force 
    $Dest_device="$usbntfs"
    Copy_With_Progression 
    #
    $form.Close()
    #
    Update_BCD $usbntfs
    bootsect /nt60 $usbntfs /force /mbr >$Null
    # Eject the mounted iso image if it was loaded by the script
    If(!$Mounted){DisMount-DiskImage $ImagePath >$Null}
    MsgBox "$Title was created successfully." "OKOnly" "Information" >$Null
    
     

    Update : my bad, the code is now ok
     
  5. Carlos Detweiller

    Carlos Detweiller Emperor of Ice-Cream
    Staff Member

    Dec 21, 2012
    7,087
    8,431
    240
    #445 Carlos Detweiller, Mar 28, 2025
    Last edited: Mar 29, 2025
    Temporarily closed for maintenance. Will be re-opened very soon.

    Edit: Reopened under new management. :)

    @rpo

    Edit: Oh, and while we are at it, would you like to have this topic moved to the "MDL Projects and Applications" forum? @rpo
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  6. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,509
    1,561
    60
    Why not, it appears that prominent people of the community approve your suggestion.

    PS : caveat concerning the gui draft : the gui may freeze (because Powershells support for multithreading is limited).
     
  7. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,509
    1,561
    60
    #447 rpo, Apr 10, 2025
    Last edited: Apr 22, 2025
    (OP)
    A candidate for next release :
    Code:
    <# : hybrid batch + powershell script
    @title Win10+ Setup Disk 3.3 &color 3E
    @powershell -noprofile -Window hidden -c "$param='%*';$ScriptPath='%~f0';iex((Get-Content('%~f0') -Raw))"&exit/b
    #>
    $OnlyUSBsticks="NO"
    #
    #   Homepage: https://forums.mydigitallife.net/threads/win10-setup-disk-works-with-uefi-secure-boot-bios-install-wim-over-4-gb.79268/
    #   Credits: @rpo, @freddie-o, @BAU & @abbodi1406
    #
    #******************************************************************************
    $Copy_with_progress= {
        Param ($iso,$usbfat32,$usbntfs,$title)
    $ProgressPreference='Continue'   
    #   
    $Host.ui.rawui.windowtitle = $Title   
    #
    Function Copy_With_Progress ($partition,$Files,$fs) { 
    [long]$allbytes = ($Files | measure -Sum length).Sum
    [long]$Total1 = 0 # bytes done
    $index = 0
    $FilesCount = $Files.Count
    $StopWatch1 = [System.Diagnostics.Stopwatch]::StartNew()
    $Buffer = New-Object Byte[] (4096 * 1024) # 4MB buffer
    #
    ForEach ($File in $Files) {
        $FileFullName = $File.FullName
        [long]$FileLength = $File.Length   
        $index++
        # build destination path for this file (for example H:\sources\boot.wim)     
        $DestFile = \"$partition$((Split-Path $filefullname -NoQualifier))\" 
        $DestDir = Split-Path $DestFile -Parent
        # if dir doesn't exist, create it
        if (!(Test-Path $DestDir)){New-Item -ItemType Directory \"$DestDir\" -Force >$Null}
        $SourceFile = [io.file]::OpenRead($FileFullName)
        $DestinationFile = [io.file]::Create($DestFile)
        $StopWatch2 = [System.Diagnostics.Stopwatch]::StartNew()
        [long]$Total2 = [long]$Count = 0
        do {
            # copy source file to destination file, 4MB at a time
            $Count = $SourceFile.Read($Buffer, 0, $Buffer.Length)
            $DestinationFile.Write($Buffer, 0, $Count)
            # this is just write-progress
            # uses stopwatch to determine how long is left
            $Total1 += $Count
            $Total2 += $Count
            #
            # Global statistics
            #
            $CompletionRate1 = $Total1 / $allbytes * 100
            [int]$MSElapsed = [int]$StopWatch1.ElapsedMilliseconds
            if (($Total1 -ne $allbytes) -and ($Total1 -ne 0)) {
                [int]$RemainingSeconds1 = $MSElapsed * ($allbytes / $Total1  - 1) / 1000                 
            } else {[int]$RemainingSeconds1 = 0}   
            $mbytes=$allbytes/1MB
            $ProgressParameters = @{
                Activity = \"$fs $partition partition\"
                Status = \"{0,3:0}% of  {1:n2} MBytes, $index of $Filescount files ($($Filescount - $index) left)\" -f $CompletionRate1,$mbytes
                PercentComplete = $CompletionRate1
                SecondsRemaining = $RemainingSeconds1}
            Write-Progress @ProgressParameters
            #
            # Statistics for current file
            #
            [int]$MSElapsed = [int]$StopWatch2.ElapsedMilliseconds
            if ($Count -ne 0) {
                $CompletionRate2 = $Total2 / $FileLength * 100
                [int]$RemainingSeconds2 = $MSElapsed * ($FileLength / $Total2  - 1) / 1000
                if ($MSElapsed -ge 1000) {
                    [single]$xferrate = $Total2 / $MSElapsed / 1mb * 1000}
                else{[single]$xferrate = 0.0}
            }else {
                $CompletionRate2 = 100
                [int]$RemainingSeconds2 = 0
            }
            $mbytes=$FileLength/1MB
            $ProgressParameters = @{
                Id = 1
                Activity = \"$($DestFile -Replace '^...')\"
                Status = \"{0,3:0}% of {1:n2} MBytes, copying file @ {2:n2} MB/s\" -f $CompletionRate2,$mbytes,$xferrate               
                PercentComplete = $CompletionRate2
                SecondsRemaining = $RemainingSeconds2}
            Write-Progress @ProgressParameters
        #
        } while ($Count -gt 0)
    $StopWatch2.Stop()
    $StopWatch2.Reset()
    $SourceFile.Close()
    $DestinationFile.Close()
    } # end for each file
    $StopWatch1.Stop()
    $StopWatch1.Reset()   
    } # End Function
    #
    $Files=Get-ChildItem $iso\boot, $iso\efi, ` 
        $iso\sources\boot.wim, $iso\bootmgr.*, $iso\bootmgr -Recurse -File -Force
    Copy_With_Progress $usbfat32 $Files 'FAT32'
    #
    $Files=Get-ChildItem $iso -Recurse -File -Force
    Copy_With_Progress $usbntfs $Files 'NTFS'
    #
    } # end of scriptblock
    #******************************************************************************
    $host.ui.rawui.windowtitle = ($title=$host.ui.rawui.windowtitle -replace "^.*  ")
    Add-Type -AssemblyName System.Windows.Forms # Load the class System.Windows.Forms
    [System.Windows.Forms.Application]::EnableVisualStyles()
    #
    $Form = New-Object System.Windows.Forms.Form -Property @{ # Create the screen form (window)
        TopMost = $True
        ShowIcon = $False
        ControlBox = $False
        ForeColor = "White"
        BackColor = "Black"
        Text = "$Title"
        Font = 'Consolas,10'
        Width = 420
        Height = 270
        StartPosition = "CenterScreen"
        SizeGripStyle = "Hide"
        ShowInTaskbar = $False
        MaximizeBox = $False
        MinimizeBox = $False
        KeyPreview = $True
        #Add_KeyDown({if ($_.KeyCode -eq "Escape"){$CancelButton.PerformClick()}}) 
        }   
    #
    # Add a OK button on the form
    $OKButton = New-Object System.Windows.Forms.Button  -Property @{
        Location = New-Object System.Drawing.Point(25,185)
        Size = New-Object System.Drawing.Size(80,26)
        DialogResult = "OK"
        }
    $Form.Controls.Add($OKButton)
    #   
    # Add a Cancel button on the form
    $CancelButton = New-Object System.Windows.Forms.Button  -Property @{
        Location = New-Object System.Drawing.Point(300,185)
        Size = New-Object System.Drawing.Size(80,26)
        DialogResult = "Cancel"
        }
    #
    # create label
    $Label0 = New-Object system.Windows.Forms.Label  -Property @{
        Location = New-Object System.Drawing.Point(20,10)
        Size = New-Object System.Drawing.Size(365,25)
        Font = "Arial,12"
        ForeColor = "Red"
        BackColor = "White"
        }
    #
    $Label1 = New-Object system.Windows.Forms.Label  -Property @{
        Location = New-Object System.Drawing.Point(60,70)
        Size = New-Object System.Drawing.Size(380,150)   
        }
    $Form.Controls.Add($Label1)
    #   
    #
    #   This script must be executed with admin privilege
    #
    #   Test Administrator privileges
    $uac_error="Elevating UAC for Administrator Privileges failed"
    $Label1.Text = @"
    $uac_error!`r`n`r`nRight click on the script.
    Select "Run as administrator".
    "@
    If (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
    {   
        #   The following test is to avoid infinite looping if elevating UAC for Administrator Privileges failed 
        If($param -eq $uac_error){
            $Form.ForeColor = "Red"
            $Form.BackColor = "White"
            $Label1.Location = New-Object System.Drawing.Point(20,50)
            $Label1.Size = New-Object System.Drawing.Size(380,120)
            $OKButton.Location = New-Object System.Drawing.Point(170,185)
            $OKButton.Text = "Exit"
            $Form.Controls.Add($OKButton)
            [void]$Form.Showdialog()       
            exit}
    #   Restart the script to get Administrator privileges and exit
        Start-Process "$ScriptPath" $uac_error -Verb runAs; exit
    }
    #
    # Filebrowser dialog object
    $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{
        Multiselect = $false                 # Only one file can be chosen
        Filter = 'ISO images (*.iso)|*.iso' # Select only iso files
    }
    #
    Function Update_bcd ($partition){
    #    Set bootmenupolicy Legacy for $partition
    bcdedit /store "$partition\boot\bcd" /set '{default}' bootmenupolicy Legacy >$Null
    bcdedit /store "$partition\EFI\Microsoft\boot\bcd" /set '{default}' bootmenupolicy Legacy >$Null
    remove-item "$partition\boot\bcd.*" -force
    remove-item "$partition\EFI\Microsoft\boot\bcd.*" -force
    }
    #
    $Label1.Location = New-Object System.Drawing.Point(40,60)
    $Label1.Size = New-Object System.Drawing.Size(380,100)
    $Label1.Text = @"
    Would you like to show only USB sticks
    and hide external hard disks?
    "@
    $filter = "Status='OK' and (InterfaceType='usb'and MediaType='Removable Media')"
    # YES : show only usb sticks
    # NO  : show usb sticks and external hard disks
    $Form.Controls.Add($CancelButton)
    $CancelButton.Text = "No"
    $OKButton.Text = "Yes"
    $OnlyUSBsticks=$Form.ShowDialog()
    If($OnlyUSBsticks -eq "Cancel"){$filter = "$filter or MediaType='External hard disk media'"}
    #
    $Label0.Text = "No USB device were detected."   
    $Label1.Text = @"
    Plug in your USB device first.`n
    Retry or Cancel.
    "@
    $OKButton.Text = "Retry"
    $CancelButton.Text = "Cancel" 
    While(!($Disks=Get-CimInstance -ClassName Win32_DiskDrive -Filter $filter)){
        $Form.Controls.Add($Label0)       
        If($Form.ShowDialog() -eq "Cancel"){exit}
        $Form.Controls.Remove($Label0)   
        }
    #   
    $Label1.Location = New-Object System.Drawing.Point(10,20)
    $Label1.Size = New-Object System.Drawing.Size(380,150)
    $Label1.Text = @"
    1. Click "Select ISO" to select your source Windows ISO`n
    2. Select your "Target USB disk" from the drop-down menu`n
    3. Click "Create Setup Disk" to start creating your Windows setup disk
    "@
    $OKButton.Location = New-Object System.Drawing.Point(25,185)
    $OKButton.Text = "Next"
    $CancelButton.Text = "Abort"
    If($Form.Showdialog() -eq "Cancel"){exit}
    #
    # Add a Source Windows ISO label on the form
    $Label1.Location = New-Object System.Drawing.Point(20,30)
    $Label1.Size = New-Object System.Drawing.Size(200,20)
    $Label1.Text = "Source Windows ISO"
    #
    # Add the ISO file name on the form
    $ISOFile = New-Object System.Windows.Forms.Label -Property @{ 
        Location = New-Object System.Drawing.Point(20,50)
        Size = New-Object System.Drawing.Size(364,24)
        Backcolor = "White"
        ForeColor = "Black"
        BorderStyle = "FixedSingle"
        }
    #
    # Add a Target USB Disk label on the form
    $TargetUSB = New-Object System.Windows.Forms.Label -Property @{
        Location = New-Object System.Drawing.Point(20,100)
        Text = "Target USB Disk"
        Size = New-Object System.Drawing.Size(200,20)
        }
    #
    # Create a USB Disk drop-down list and fill it
    $USBDiskList = New-Object System.Windows.Forms.ComboBox -Property @{
        DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
        Location = New-Object System.Drawing.Point(20,120)
        Size = New-Object System.Drawing.Size(363,22)
        }
    #
    # Add a Select ISO button on the form
    $SelectISOButton = New-Object System.Windows.Forms.Button -Property @{
        Location = New-Object System.Drawing.Point(20,185)
        Text = "Select ISO"
        Size = New-Object System.Drawing.Size(110,26)
        }
    #
    # Add a Create Setup Disk button on the form
    $OKButton.Location = New-Object System.Drawing.Point(140,185)
    $OKButton.Size = New-Object System.Drawing.Size(155,26)
    $OKButton.Text=""
    $OKButton.Enabled = $false
    #
    # Add a Cancel button on the form
    $CancelButton.Text = "Cancel"   
    #
    $SelectISOButton.Add_Click({
    If ($FileBrowser.ShowDialog() -ne "Cancel"){ # if Cancel, just ignore
        $Global:ImagePath=$FileBrowser.filename    # return the file name
        $ISOFile.Text = Split-Path -Path $ImagePath -leaf # extract filename and extension (iso)
        if(($ISOFile.Text).length -gt 44){
            $ISOFile.Text = $ImagePath.PadRight(100).substring(0,43)+"..."}
        $OKButton.Text = "Create Setup Disk"   
        $OKButton.Enabled = $true
        $OKButton.Focus()}})
    #
    $USBDisks=@() # array with USB disk number
    Foreach ($Disk in $Disks){
        $FriendlyName = ($Disk.Caption).PadRight(40).substring(0,35)
        $USBDisks+=$Disk.Index
        $USBDiskList.Items.Add(("{0,-30}{1,10:n2} GB" -f $FriendlyName,($Disk.Size/1GB))) >$Null
    <#
        Foreach ($Partition in (Get-Partition $Disk.Index)) {
            If(!$Partition.DriveLetter){$AccessPath="  "}Else{$AccessPath=$Partition.DriveLetter+":"}
            $USBDisks+=$Disk.Index
            $USBDiskList.Items.Add((" ({0,2}){1,-30}{2,10:n2} GB" -f $AccessPath,($Partition|
                Get-Volume).FileSystemLabel.PadRight(25).substring(0,25),($Partition.Size/1GB))) >$Null
        }
    #>
    }
    #
    $GroupBox = New-Object System.Windows.Forms.GroupBox # Add a group box on the form
    $GroupBox.Location = New-Object System.Drawing.Point(7,0)
    $GroupBox.Size = New-Object System.Drawing.Size(390,225)   
    $Form.Controls.AddRange(@($ISOFile, $TargetUSB, $SelectISOButton, $USBDiskList, $GroupBox))
    #
    $USBDiskList.SelectedIndex=0
    #
    if($Form.ShowDialog() -eq "Cancel") {exit}
    #
    # At this point the mounted USB disk and the iso image file path are defined
    #
    $USB=$USBDisks[$USBDiskList.SelectedIndex]
    #
    $Form.Controls.Clear()
    $Form.Controls.AddRange(@($OKButton, $CancelButton, $Label1, $Label0))
    $OKButton.Location = New-Object System.Drawing.Point(25,185)
    $OKButton.Text = "Yes"
    $CancelButton.Text = "No"
    $OKButton.Size = New-Object System.Drawing.Size(80,26)
    $Label1.Location = New-Object System.Drawing.Point(20,50)
    $Label1.Size = New-Object System.Drawing.Size(380,180)
    $Label0.Text = "WARNING!`n"
    $Label1.Text = @"
    Your USB device will be converted to MBR scheme, repartitioned and reformatted.`n
    All partitions and data currently stored in the USB device will be erased.`n
    Are you sure you want to continue?
    "@
    if($Form.ShowDialog() -eq "Cancel") {exit}
    #
    $Label1.Location = New-Object System.Drawing.Point(10,20)
    $Label1.Size = New-Object System.Drawing.Size(380,160)
    $Label1.Text = "Mounting and checking to see if the ISO image was mounted`n"
    $Form.Controls.Clear()
    $Form.Controls.Add($Label1)
    $Form.Show()
    $Form.Refresh()
    #
    #   Check if iso already mounted by getting driveletter
    If($ISO = (Get-DiskImage $ImagePath|Get-Volume).DriveLetter){$Mounted = $True}Else
    #    Mount iso and get drive letter
        {$Mounted = $False;If(!($ISO = (Mount-DiskImage $ImagePath|Get-Volume).DriveLetter)){Exit}}
    $ISO=$ISO+":"
    #
    Stop-Service ShellHWDetection -ErrorAction SilentlyContinue >$Null
    $ProgressPreference="SilentlyContinue"
    #
    $Label1.Text = $Label1.Text+"`nCleaning the USB disk and converting it to MBR partition scheme`n"
    $Form.Refresh()
    "Select disk $USB`nclean`nconvert MBR`nexit"|diskpart >$Null
    If($LASTEXITCODE -ne 0){
        $Form.Hide()
        $Label1.Location=New-Object System.Drawing.Point(20,80)
        $Label1.Size=New-Object System.Drawing.Size(380,100)
        $Label1.Text = "Diskpart operations failed with error code $LASTEXITCODE."
        $OKButton.Location=New-Object System.Drawing.Point(170,185)
        $OKButton.Text = "Exit"
        $Form.Controls.Add($OKButton)
        [void]$Form.Showdialog()       
        exit}
    #   
    $Label1.Text = $Label1.Text+"`nCreating the FAT32 boot partition`n"
    $Form.Refresh() 
    $usbfat32 = (New-Partition -DiskNumber $usb -Size 1GB -AssignDriveLetter|
        Format-Volume -FileSystem FAT32 -NewFileSystemLabel "BOOT").DriveLetter + ":"
    $Files = Get-ChildItem $iso\boot, $iso\efi, `
        $iso\sources\boot.wim, $iso\bootmgr.*, $iso\bootmgr -Recurse -File -Force
    $FilesSize = ($Files | measure -Sum Length).Sum/1GB   
    $msg = @"
    Fat32 1 GB partition too small. $("{0,1:n2}" -f $FilesSize) GB needed"
    "@   
    If ($FilesSize -gt 1GB){
        $Form.Hide()
        $Label1.Text = $msg
        $Label1.ForeColor = "Red"
        $Label1.BackColor = "White"
        $Label1.Location=New-Object System.Drawing.Point(20,80)
        $Label1.AutoSize = $True
        $OKButton.Text = "Exit"
        $OKButton.Location=New-Object System.Drawing.Point(170,185)
        $Form.Controls.Add($OKButton)
        [void]$Form.Showdialog()
        exit
    }
    $Label1.Text = $Label1.Text+"`nCreating the NTFS setup partition and marking partition active"
    $Form.Refresh()    
    $usbntfs = (New-Partition -DiskNumber $usb -UseMaximumSize -AssignDriveLetter -IsActive|
        Format-Volume -FileSystem NTFS -NewFileSystemLabel "SETUP").DriveLetter + ":"
    $Files = Get-ChildItem $iso -Recurse -File -Force 
    $FilesSize = ($Files | measure -Sum Length).Sum/1GB
    $PartitionSize = (Get-Volume ($usbntfs -Replace ".$")).Size/1GB   
    $msg = @"
    NTFS partition $("{0,1:n2}" -f $PartitionSize) too small. $("{0,1:n2}" -f $FilesSize) GB needed"       
    "@
    If($FilesSize -gt $PartitionSize){
        $Form.Hide()
        $Label1.Text = $msg
        $Label1.ForeColor = "Red"
        $Label1.BackColor = "White"
        $Label1.Location=New-Object System.Drawing.Point(10,80)
        $Label1.AutoSize = $True
        $OKButton.Text = "Exit"
        $OKButton.Location=New-Object System.Drawing.Point(170,185)
        $Form.Controls.Add($OKButton)
        [void]$Form.Showdialog()
        exit
    }   
    #
    $Form.Hide()
    Start-Service ShellHWDetection -erroraction silentlycontinue >$Null
    #
    #******************************************************************************
    Try{
        Pwsh -c "$PsVersionTable" -ErrorAction Stop >$Null
        $ps = "pwsh"}
    Catch{$ps = "powershell"}   
    #"`r`nStarting copy process and waiting for completion`r`n"
    $Process = Start-Process $ps -ArgumentList `
        "-Command & {$Copy_with_progress} '$iso' '$usbfat32' '$usbntfs' '$title'" -Wait -PassThru
    #******************************************************************************
    #
    $Label1.Location=New-Object System.Drawing.Point(20,80)
    $Label1.Size=New-Object System.Drawing.Size(380,100)
    $OKButton.Location=New-Object System.Drawing.Point(170,185)
    $Form.Controls.Add($OKButton)   
    If($process.ExitCode -ne 0){   
        $Label1.Text = "Copying files to the USB device failed."
        $OKButton.Text = "Exit"
        [void]$Form.Showdialog()
        exit}
    #
    $OKButton.Location = New-Object System.Drawing.Point(25,185)
    $OKButton.Text = "Next"
    $CancelButton.Text = "Abort"
    $Form.Controls.Add($CancelButton)   
    $Label1.Text = "All folders and files copyed."
    If($Form.Showdialog() -eq "Cancel"){exit}
    #
    $Form.Controls.Remove($OKButton)
    $Form.Controls.Remove($CancelButton)
    $Label1.Location = New-Object System.Drawing.Point(10,20)
    $Label1.Size = New-Object System.Drawing.Size(380,160)
    $Label1.Text = "Updating BCD`n"
    $Form.Show()
    $Form.Refresh()
    Update_BCD $usbfat32
    Update_BCD $usbntfs
    #
    $Label1.Text = $Label1.Text+"`nRemoving the drive letter to hide the FAT32 boot partition`n"
    $Form.Refresh()
    Get-Volume ($usbfat32 -replace ".$")|Get-Partition|
        Remove-PartitionAccessPath -accesspath $usbfat32
    #
    $Label1.Text = $Label1.Text+"`nEject the mounted ISO image if it was loaded by the script"
    $Form.Refresh()
    # Eject the mounted iso image if it was loaded by the script
    If(!$Mounted){DisMount-DiskImage $ImagePath >$Null}
    #
    bootsect /nt60 $usbntfs /force /mbr >$Nul
    #
    $Form.Hide()
    $Form.Controls.Add($OKButton)
    $OKButton.Location=New-Object System.Drawing.Point(170,185)   
    $Label1.Location=New-Object System.Drawing.Point(20,80)
    $Label1.AutoSize = $True
    $Label1.Text = "$Title was created successfully."
    $OKButton.Text = "Exit"
    [void]$Form.ShowDialog()
    
    

    Progression bar output with Powershell 5.1 :
    upload_2025-4-10_15-11-46.png

    Progression bar output with pwsh 7 :
    upload_2025-4-10_15-13-41.png
     
  8. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,509
    1,561
    60
    #448 rpo, Apr 21, 2025
    Last edited: Apr 22, 2025
    (OP)
    The OP was updated The download includes versions 3.2 and 3.3, they differ only by cosmetic changes; the created disk has the same content for the two versions.

    Update :
    Re-uploaded today (April, 22th) to correct a bug while performing a test to ensure that no created partition of the USB device is too small (concerns only version 3.3 since version 3.2 doesn't perform this test).
     
  9. haris_mdlf69

    haris_mdlf69 MDL Addicted

    Oct 23, 2018
    614
    1,078
    30
  10. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,509
    1,561
    60
    #450 rpo, May 7, 2025
    Last edited: May 8, 2025
    (OP)
    Re-upload due to a small UI update.

    Reminder : if you NEVER want to use usb connected external hdd/ssd, edit the script :
    1. Search the string $OnlyUSBsticks="NO" and replace by $OnlyUSBsticks="YES"
    2. Search the statement :
    Code:
    If($Form.ShowDialog() -eq "Cancel"){$OnlyUSBsticks="No"}Else{$OnlyUSBsticks="Yes"}
    
    and delete it.
     
  11. haris_mdlf69

    haris_mdlf69 MDL Addicted

    Oct 23, 2018
    614
    1,078
    30
    #451 haris_mdlf69, Friday at 19:21
    Last edited: Friday at 19:27
    Hey @rpo, just wondering—would it be possible to add support for a source Windows folder in a future release as well? It’d be super helpful for those of us who usually work with extracted installation files instead of full ISOs. Thanks for considering it!
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  12. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,509
    1,561
    60
    This was asked many years ago. The short anwer was "no", but i could reconsider this demand.
    Instead of searching a iso file, it could be possible to search for a install.wim or install.esd file. Is this acceptable?
     
  13. haris_mdlf69

    haris_mdlf69 MDL Addicted

    Oct 23, 2018
    614
    1,078
    30
    It's acceptable and should work just fine for my needs.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  14. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,509
    1,561
    60
    #454 rpo, Sunday at 16:47
    Last edited: Monday at 10:30
    (OP)
    New version adding the possibility to select source windows folder; select install.wim/esd in the extracted dolders.
    Code:
    <# : hybrid batch + powershell script
    @title Win10+ Setup Disk 3.4 &color 3E
    @powershell -noprofile -Window hidden -c "$param='%*';$ScriptPath='%~f0';iex((Get-Content('%~f0') -Raw))"&exit/b
    #>
    $OnlyUSBsticks="NO"
    #
    #   Homepage: https://forums.mydigitallife.net/threads/win10-setup-disk-works-with-uefi-secure-boot-bios-install-wim-over-4-gb.79268/
    #   Credits: @rpo, @freddie-o, @BAU & @abbodi1406
    #
    #******************************************************************************
    $Copy_with_progress= {
        Param ($iso,$usbfat32,$usbntfs,$title)
    #   
    $Host.ui.rawui.windowtitle = $Title   
    #
    Function Copy_With_Progress ($partition,$Files,$fs) { 
    [long]$allbytes = ($Files | measure -Sum length).Sum
    [long]$Total1 = 0 # bytes done
    $index = 0
    $FilesCount = $Files.Count
    $StopWatch1 = [System.Diagnostics.Stopwatch]::StartNew()
    $Buffer = New-Object Byte[] (4096 * 1024) # 4MB buffer
    #
    ForEach ($File in $Files) {
        $FileFullName = $File.FullName
        [long]$FileLength = $File.Length   
        $index++
        # build destination path for this file (for example H:\sources\boot.wim)     
        $DestFile = $partition+$FileFullName.Substring($iso.length)
        $DestDir = Split-Path $DestFile -Parent
        # if dir doesn't exist, create it
        if (!(Test-Path $DestDir)){New-Item -ItemType Directory \"$DestDir\" -Force >$Null}
        $SourceFile = [io.file]::OpenRead($FileFullName)
        $DestinationFile = [io.file]::Create($DestFile)
        $StopWatch2 = [System.Diagnostics.Stopwatch]::StartNew()
        [long]$Total2 = [long]$Count = 0
        do {
            # copy source file to destination file, 4MB at a time
            $Count = $SourceFile.Read($Buffer, 0, $Buffer.Length)
            $DestinationFile.Write($Buffer, 0, $Count)
            # this is just write-progress
            # uses stopwatch to determine how long is left
            $Total1 += $Count
            $Total2 += $Count
            #
            # Global statistics
            #
            $CompletionRate1 = $Total1 / $allbytes * 100
            [int]$MSElapsed = [int]$StopWatch1.ElapsedMilliseconds
            if (($Total1 -ne $allbytes) -and ($Total1 -ne 0)) {
                [int]$RemainingSeconds1 = $MSElapsed * ($allbytes / $Total1  - 1) / 1000                 
            } else {[int]$RemainingSeconds1 = 0}   
            $mbytes=$allbytes/1MB
            $ProgressParameters = @{
                Activity = \"$fs $partition partition\"
                Status = \"{0,3:0}% of  {1:n2} MBytes, $index of $Filescount files ($($Filescount - $index) left)\" -f $CompletionRate1,$mbytes
                PercentComplete = $CompletionRate1
                SecondsRemaining = $RemainingSeconds1}
            Write-Progress @ProgressParameters
            #
            # Statistics for current file
            #
            [int]$MSElapsed = [int]$StopWatch2.ElapsedMilliseconds
            if ($Count -ne 0) {
                $CompletionRate2 = $Total2 / $FileLength * 100
                [int]$RemainingSeconds2 = $MSElapsed * ($FileLength / $Total2  - 1) / 1000
                if ($MSElapsed -ge 1000) {
                    [single]$xferrate = $Total2 / $MSElapsed / 1mb * 1000}
                else{[single]$xferrate = 0.0}
            }else {
                $CompletionRate2 = 100
                [int]$RemainingSeconds2 = 0
            }
            $mbytes=$FileLength/1MB
            $ProgressParameters = @{
                Id = 1
                Activity = \"$($DestFile -Replace '^...')\"
                Status = \"{0,3:0}% of {1:n2} MBytes, copying file @ {2:n2} MB/s\" -f $CompletionRate2,$mbytes,$xferrate               
                PercentComplete = $CompletionRate2
                SecondsRemaining = $RemainingSeconds2}
            Write-Progress @ProgressParameters
        #
        } while ($Count -gt 0)
    $StopWatch2.Stop()
    $StopWatch2.Reset()
    $SourceFile.Close()
    $DestinationFile.Close()
    } # end for each file
    $StopWatch1.Stop()
    $StopWatch1.Reset()   
    } # End Function
    #
    $Files=Get-ChildItem $iso\boot, $iso\efi, ` 
        $iso\sources\boot.wim, $iso\bootmgr.*, $iso\bootmgr -Recurse -File -Force
    Copy_With_Progress $usbfat32 $Files 'FAT32'
    #
    $Files=Get-ChildItem $iso -Recurse -File -Force
    Copy_With_Progress $usbntfs $Files 'NTFS'
    #
    } # end of scriptblock
    #******************************************************************************
    $host.ui.rawui.windowtitle = ($title=$host.ui.rawui.windowtitle -replace "^.*  ")
    Add-Type -AssemblyName System.Windows.Forms # Load the class System.Windows.Forms
    [System.Windows.Forms.Application]::EnableVisualStyles()
    #
    $Form = New-Object System.Windows.Forms.Form -Property @{ # Create the screen form (window)
        TopMost = $True
        ShowIcon = $False
        ControlBox = $False
        ForeColor = "White"
        BackColor = "Black"
        Text = "$Title"
        Font = 'Consolas,10'
        Width = 420
        Height = 270
        StartPosition = "CenterScreen"
        SizeGripStyle = "Hide"
        ShowInTaskbar = $False
        MaximizeBox = $False
        MinimizeBox = $False
        KeyPreview = $True}   
    #
    # Add a OK button on the form
    $OKButton = New-Object System.Windows.Forms.Button  -Property @{
        Location = New-Object System.Drawing.Point(25,185)
        Size = New-Object System.Drawing.Size(80,26)
        DialogResult = "OK"
        }
    $Form.Controls.Add($OKButton)
    #   
    # Add a Cancel button on the form
    $CancelButton = New-Object System.Windows.Forms.Button  -Property @{
        Location = New-Object System.Drawing.Point(300,185)
        Size = New-Object System.Drawing.Size(80,26)
        DialogResult = "Cancel"}
    #
    # create label
    $Label0 = New-Object system.Windows.Forms.Label  -Property @{
        Location = New-Object System.Drawing.Point(20,10)
        Size = New-Object System.Drawing.Size(365,25)
        Font = "Arial,12"
        ForeColor = "Red"
        BackColor = "White"}
    #
    $Label1 = New-Object system.Windows.Forms.Label  -Property @{
        Location = New-Object System.Drawing.Point(60,70)
        Size = New-Object System.Drawing.Size(380,150)}
    #
    #   How to
    #
    $HowTo= New-Object system.Windows.Forms.Form  -Property @{
        TopMost = $True
        ShowIcon = $False
        ControlBox = $False
        ShowInTaskbar = $False
        Width = 420
        Height = 270
        Font = "Arial,12"}
    #
    $HowToText = New-Object system.Windows.Forms.RichTextBox  -Property @{
        Location = New-Object System.Drawing.Point(10,30)
        Dock = "Fill"
        BackColor = 'Blue'
        ForeColor = 'White'   
        Anchor = 'top, left'}
    $HowTo.Controls.Add($HowToText)
    #
    #    Display information
    #
    $Information= New-Object system.Windows.Forms.Form  -Property @{
        TopMost = $True
        ShowIcon = $False
        ControlBox = $False
        ShowInTaskbar = $False
        StartPosition = 'CenterScreen'
        Width = 420
        Height = 270
        Font = "Arial,12"}
    #
    $InformationText = New-Object system.Windows.Forms.RichTextBox  -Property @{
        Location = New-Object System.Drawing.Point(10,30)
        Dock = "Fill"
        BackColor = 'Blue'
        ForeColor = 'White'   
        Anchor = 'top, left'}
    $Information.Controls.Add($InformationText)
    #
    #
    #   This script must be executed with admin privilege
    #
    #   Test Administrator privileges
    $uac_error="Elevating UAC for Administrator Privileges failed"
    $Label1.Text = @"
    $uac_error!`r`n`r`nRight click on the script.
    Select "Run as administrator".
    "@
    If (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
    {   
        #   The following test is to avoid infinite looping if elevating UAC for Administrator Privileges failed 
        If($param -eq $uac_error){
            $Form.ForeColor = "Red"
            $Form.BackColor = "White"
            $Label1.Location = New-Object System.Drawing.Point(20,50)
            $Label1.Size = New-Object System.Drawing.Size(380,120)
            $OKButton.Location = New-Object System.Drawing.Point(170,185)
            $OKButton.Text = "Exit"
            $Form.Controls.AddRange(@($OKButton, $Label1))
            [void]$Form.Showdialog()       
            exit}
    #   Restart the script to get Administrator privileges and exit
        Start-Process "$ScriptPath" $uac_error -Verb runAs; exit
    }
    #
    # Filebrowser dialog object
    $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{
        Title="Select ISO image or intall.wim/esd in extracted source folder"
        Multiselect = $false                 # Only one file can be chosen
        Filter = 'ISO images (*.iso;*install.wim;*install.esd)|*.iso;*install.wim;*install.esd' # Select only iso files
    }
    # 
    Function Update_bcd ($partition){
    #    Set bootmenupolicy Legacy for $partition
    bcdedit /store "$partition\boot\bcd" /set '{default}' bootmenupolicy Legacy >$Null
    bcdedit /store "$partition\EFI\Microsoft\boot\bcd" /set '{default}' bootmenupolicy Legacy >$Null
    remove-item "$partition\boot\bcd.*" -force
    remove-item "$partition\EFI\Microsoft\boot\bcd.*" -force
    }
    #
    $Form.Controls.Clear()
    $Label1.Location = New-Object System.Drawing.Point(40,60)
    $Label1.Size = New-Object System.Drawing.Size(380,100)
    $Label1.Text = @"
    Would you like to show only USB sticks
    and hide external hdd/ssd disks?
    "@
    $filter = "Status='OK' and (InterfaceType='usb'and MediaType='Removable Media')"
    # YES : show only usb sticks
    # NO  : show usb sticks and external hard disks
    $Form.Controls.AddRange(@($OKButton, $Label1, $CancelButton))
    $CancelButton.Text = "No"
    $OKButton.Text = "Yes"
    If($Form.ShowDialog() -eq "Cancel"){$OnlyUSBsticks="No"}Else{$OnlyUSBsticks="Yes"}
    If($OnlyUSBsticks -eq "No")
        {$filter = "$filter or MediaType='External hard disk media'"}
    #
    $Form.Controls.Clear()
    $Label0.Text = "No USB device were detected."   
    $Label1.Text = @"
    Plug in your USB device first.`n
    Retry or Cancel.
    "@
    $OKButton.Text = "Retry"
    $CancelButton.Text = "Cancel" 
    While(!($Disks=Get-CimInstance -ClassName Win32_DiskDrive -Filter $filter)){
        $Form.Controls.AddRange(@($OKButton, $CancelButton, $Label0, $Label1))   
        If($Form.ShowDialog() -eq "Cancel"){exit}}   
    #
    # Add a Source Windows ISO label on the form
    $Label1.Location = New-Object System.Drawing.Point(20,25)
    $Label1.Size = New-Object System.Drawing.Size(400,20)
    $Label1.Text = "Windows source (ISO or extracted source folder)"
    #
    # Add the ISO file name on the form
    $ISOFile = New-Object System.Windows.Forms.TextBox -Property @{ 
        Location = New-Object System.Drawing.Point(20,45)
        Size = New-Object System.Drawing.Size(364,24)
        Backcolor = "White"
        ForeColor = "Black"
        ReadOnly = $True
        BorderStyle = "FixedSingle"}
    #
    # Add a Target USB Disk label on the form
    $TargetUSB = New-Object System.Windows.Forms.Label -Property @{
        Location = New-Object System.Drawing.Point(20,100)
        Text = "Target USB Disk"
        Size = New-Object System.Drawing.Size(200,20)}
    #
    # Create a USB Disk drop-down list and fill it
    $USBDiskList = New-Object System.Windows.Forms.ComboBox -Property @{
        DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
        Location = New-Object System.Drawing.Point(20,120)
        Size = New-Object System.Drawing.Size(363,22)}
    #
    # Add a Select ISO button on the form
    $SelectISOButton = New-Object System.Windows.Forms.Button -Property @{
        Location = New-Object System.Drawing.Point(20,185)
        Text = "Source"
        Size = New-Object System.Drawing.Size(110,26)}
    #
    # Add a Create Setup Disk button on the form
    $OKButton.Location = New-Object System.Drawing.Point(140,185)
    $OKButton.Size = New-Object System.Drawing.Size(155,26)
    $OKButton.Text=""
    $OKButton.Enabled = $false
    #
    # Add a Cancel button on the form
    $CancelButton.Text = "Cancel"   
    #
    $SelectISOButton.Add_Click({
    If ($FileBrowser.ShowDialog() -ne "Cancel"){ # if Cancel, just ignore
        $Global:ImagePath=$FileBrowser.filename    # return the file name
        If($ImagePath.Split(".") -eq "Iso"){
            $Global:dvd = $True
            $ISOFile.Text = Split-Path -Path $ImagePath -leaf # filename (.iso)
        }Else{
            $Global:dvd = $False
            $Global:ImagePath=Split-Path $ImagePath -Parent|Split-Path -Parent
            $ISOFile.Text = $ImagePath
        }   
        if(($ISOFile.Text).length -gt 44){
            $ISOFile.Text = $ImagePath.PadRight(100).substring(0,43)+"..."}
        $OKButton.Text = "Create Setup Disk"   
        $OKButton.Enabled = $true
        $OKButton.Focus()}})
    #
    $USBDisks=@() # array with USB disk number
    Foreach ($Disk in $Disks){
        $FriendlyName = ($Disk.Caption).PadRight(40).substring(0,35)
        $USBDisks+=$Disk.Index
        $USBDiskList.Items.Add(("{0,-30}{1,10:n2} GB" -f $FriendlyName,($Disk.Size/1GB))) >$Null
    <#
        Foreach ($Partition in (Get-Partition $Disk.Index)) {
            If(!$Partition.DriveLetter){$AccessPath="  "}Else{$AccessPath=$Partition.DriveLetter+":"}
            $USBDisks+=$Disk.Index
            $USBDiskList.Items.Add((" ({0,2}){1,-30}{2,10:n2} GB" -f $AccessPath,($Partition|
                Get-Volume).FileSystemLabel.PadRight(25).substring(0,25),($Partition.Size/1GB))) >$Null
        }
    #>
    }
    #
    $GroupBox = New-Object System.Windows.Forms.GroupBox # Add a group box on the form
    $GroupBox.Location = New-Object System.Drawing.Point(7,0)
    $GroupBox.Size = New-Object System.Drawing.Size(390,225)
    #
    $Form.Controls.Clear()   
    $Form.Controls.AddRange(@($Label1, $OKbutton, $CancelButton, $ISOFile))
    $Form.Controls.AddRange(@($TargetUSB, $SelectISOButton, $USBDiskList, $GroupBox))
    #
    $USBDiskList.SelectedIndex=0
    #
    $HowToText.Text = @"
    `nPlugin your usb device
    `n`nClick "Select ISO" or install.wim/esd in extracted folders
    `n`nSelect your "Target USB disk" from the drop-down menu
    `n`nClick "Create Setup Disk" to start creating your Windows setup disk
    "@   
    $HowTo.Show()
    $HowTo.Refresh()
    $Result= $Form.ShowDialog() 
    $HowTo.Dispose()
    $HowTo.Close()
    If($Result -eq "Cancel") {exit}
    #
    # At this point the mounted USB disk and the iso image file path are defined
    #
    $USB=$USBDisks[$USBDiskList.SelectedIndex]
    #
    $Form.Controls.Clear()
    $Form.Controls.AddRange(@($OKButton, $CancelButton, $Label1, $Label0))
    $OKButton.Location = New-Object System.Drawing.Point(25,185)
    $OKButton.Text = "Yes"
    $CancelButton.Text = "No"
    $OKButton.Size = New-Object System.Drawing.Size(80,26)
    $Label1.Location = New-Object System.Drawing.Point(20,50)
    $Label1.Size = New-Object System.Drawing.Size(380,180)
    $Label0.Text = "WARNING!`n"
    $Label1.Text = @"
    Your USB device will be converted to MBR scheme, repartitioned and reformatted.`n
    All partitions and data currently stored in the USB device will be erased.`n
    Are you sure you want to continue?
    "@
    if($Form.ShowDialog() -eq "Cancel") {exit}
    #
    $Information.Show()
    If($dvd){
    $InformationText.AppendText("`nMounting and checking to see if the ISO image was mounted`n")
    $Information.Refresh()
    #
    #   Check if iso already mounted by getting driveletter
    If($ISO = (Get-DiskImage $ImagePath|Get-Volume).DriveLetter){$Mounted = $True}Else
    #    Mount iso and get drive letter
        {$Mounted = $False;If(!($ISO = (Mount-DiskImage $ImagePath|Get-Volume).DriveLetter)){Exit}}
    $ISO=$ISO+":"
    }Else{
    $ISO = $ImagePath
    }
    #
    Stop-Service ShellHWDetection -ErrorAction SilentlyContinue >$Null
    $ProgressPreference="SilentlyContinue"
    #
    $InformationText.AppendText("`nCleaning the USB disk and converting it to MBR partition scheme`n")
    $Information.Refresh()
    "Select disk $USB`nclean`nconvert MBR`nexit"|diskpart >$Null
    If($LASTEXITCODE -ne 0){
        $Information.Hide()
        $Form.Controls.Clear()
        $Form.Controls.AddRange(@($OKButton, $Label1))
        $Label1.Location=New-Object System.Drawing.Point(20,80)
        $Label1.Size=New-Object System.Drawing.Size(380,100)
        $Label1.Text = "Diskpart operations failed with error code $LASTEXITCODE."
        $OKButton.Location=New-Object System.Drawing.Point(170,185)
        $OKButton.Text = "Exit"
        $Form.Controls.Add($OKButton)
        [void]$Form.Showdialog()       
        exit}
    # 
    $InformationText.AppendText("`nCreating the FAT32 boot partition`n")
    $Information.Refresh() 
    $usbfat32 = (New-Partition -DiskNumber $usb -Size 1GB -AssignDriveLetter|
        Format-Volume -FileSystem FAT32 -NewFileSystemLabel "BOOT").DriveLetter + ":"
    $Files = Get-ChildItem $iso\boot, $iso\efi, `
        $iso\sources\boot.wim, $iso\bootmgr.*, $iso\bootmgr -Recurse -File -Force
    $FilesSize = ($Files | measure -Sum Length).Sum/1GB   
    $msg = @"
    Fat32 1 GB partition too small. $("{0,1:n2}" -f $FilesSize) GB needed"
    "@   
    If ($FilesSize -gt 1GB){
        $Information.Hide()
        $Form.Controls.Clear()
        $Label1.Text = $msg
        $Label1.ForeColor = "Red"
        $Label1.BackColor = "White"
        $Label1.Location=New-Object System.Drawing.Point(20,80)
        $Label1.AutoSize = $True
        $OKButton.Text = "Exit"
        $OKButton.Location=New-Object System.Drawing.Point(170,185)
        $Form.Controls.AddRange(@($OKButton, $Label1))
        [void]$Form.Showdialog()
        exit
    }
    $InformationText.AppendText("`nCreating the NTFS setup partition and marking partition active")
    $Information.Refresh()    
    $usbntfs = (New-Partition -DiskNumber $usb -UseMaximumSize -AssignDriveLetter -IsActive|
        Format-Volume -FileSystem NTFS -NewFileSystemLabel "SETUP").DriveLetter + ":"
    $Files = Get-ChildItem $iso -Recurse -File -Force 
    $FilesSize = ($Files | measure -Sum Length).Sum/1GB
    $PartitionSize = (Get-Volume ($usbntfs -Replace ".$")).Size/1GB   
    $msg = @"
    NTFS partition $("{0,1:n2}" -f $PartitionSize) too small. $("{0,1:n2}" -f $FilesSize) GB needed"       
    "@
    If($FilesSize -gt $PartitionSize){
        $Information.Hide()
        $Form.Controls.Clear()
        $Label1.Text = $msg
        $Label1.ForeColor = "Red"
        $Label1.BackColor = "White"
        $Label1.Location=New-Object System.Drawing.Point(10,80)
        $Label1.AutoSize = $True
        $OKButton.Text = "Exit"
        $OKButton.Location=New-Object System.Drawing.Point(170,185)
        $Form.Controls.AddRange(@($OKButton, $Label1))
        [void]$Form.Showdialog()
        exit
    }   
    #
    $InformationText.Text = ""
    $Information.Hide()
    Start-Service ShellHWDetection -erroraction silentlycontinue >$Null
    #
    #******************************************************************************
    Try{
        Pwsh -c "$PsVersionTable" -ErrorAction Stop >$Null
        $ps = "pwsh"}
    Catch{$ps = "powershell"}   
    #"`r`nStarting copy process and waiting for completion`r`n"
    $Process = Start-Process $ps -ArgumentList `
        "-Command & {$Copy_with_progress} '$iso' '$usbfat32' '$usbntfs' '$title'" -Wait -PassThru
    #******************************************************************************
    #
    $Form.Controls.Clear()
    $Label1.Location=New-Object System.Drawing.Point(20,80)
    $Label1.Size=New-Object System.Drawing.Size(380,100)
    $OKButton.Location=New-Object System.Drawing.Point(170,185)
    $Form.Controls.AddRange(@($OKButton, $Label1))   
    If($process.ExitCode -ne 0){   
        $Label1.Text = "Copying files to the USB device failed."
        $OKButton.Text = "Exit"
        [void]$Form.Showdialog()
        exit}
    #
    $Form.Controls.Clear
    $OKButton.Location = New-Object System.Drawing.Point(25,185)
    $OKButton.Text = "Next"
    $CancelButton.Text = "Abort"   
    $Label1.Text = "All folders and files copyed."
    $Form.Controls.AddRange(@($OKButton, $Label1, $CancelButton))
    If($Form.Showdialog() -eq "Cancel"){exit}
    #
    $InformationText.AppendText("`nUpdating BCD`n")
    $Information.Show()
    $Information.Refresh()
    Update_BCD $usbfat32
    Update_BCD $usbntfs
    #
    $InformationText.AppendText("`nRemoving the drive letter to hide the FAT32 boot partition`n")
    $Information.Refresh()
    Get-Volume ($usbfat32 -replace ".$")|Get-Partition|
        Remove-PartitionAccessPath -accesspath $usbfat32
    #
    If(DVD){
    $InformationText.AppendText("`nEject the mounted ISO image if it was loaded by the script")
    $Information.Refresh()
    # Eject the mounted iso image if it was loaded by the script
    If(!$Mounted){DisMount-DiskImage $ImagePath >$Null}
    }
    #
    bootsect /nt60 $usbntfs /force /mbr >$Null
    #
    $Information.Dispose()
    $Information.Close()
    $Form.Controls.Clear()
    $Form.Controls.AddRange(@($OKButton, $Label1))
    $OKButton.Location=New-Object System.Drawing.Point(170,185)   
    $Label1.Location=New-Object System.Drawing.Point(20,80)
    $Label1.AutoSize = $True
    $Label1.Text = "$Title was created successfully."
    $OKButton.Text = "Exit"
    [void]$Form.ShowDialog()
    
    
     
  15. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,509
    1,561
    60
    Experimental version introducing progress bars

    upload_2025-5-13_12-11-58.png
    Code:
    <# : hybrid batch + powershell script
    @title Win10+ Setup Disk 3.5 &color 3E
    @powershell -noprofile -Window hidden -c "$param='%*';$ScriptPath='%~f0';iex((Get-Content('%~f0') -Raw))"&exit/b
    #>
    $OnlyUSBsticks="NO"
    #
    #   Homepage: https://forums.mydigitallife.net/threads/win10-setup-disk-works-with-uefi-secure-boot-bios-install-wim-over-4-gb.79268/
    #   Credits: @rpo, @freddie-o, @BAU & @abbodi1406
    #
    #******************************************************************************
    $host.ui.rawui.windowtitle = ($title=$host.ui.rawui.windowtitle -replace "^.*  ")
    Add-Type -AssemblyName System.Windows.Forms # Load the class System.Windows.Forms
    [System.Windows.Forms.Application]::EnableVisualStyles()
    $Width = 420; $Height = 270
    #
    #******************************************************************************
    Function Copy_Progression ($Files,$Partition,$fs) {
    #
    # suffix 1 : variables for global process
    # suffix 2 : variables for one file process
    [long]$allbytes = ($Files | measure -Sum length).Sum
    [long]$Total1 = 0 # bytes done
    $index = 0
    $FilesCount = $Files.Count
    $StopWatch1 = [Diagnostics.Stopwatch]::StartNew()
    $Buffer = New-Object Byte[] (4MB) # 4MB buffer
    #
    ForEach ($File in $Files) {
        $FileFullName = $File.fullname
        [long]$FileLength = $File.Length
        $index++
        # build destination path for this file (for example H:\sources\boot.wim)    
        $DestFile = $partition+$FileFullName.Substring($iso.length)
        $DestDir= Split-Path $DestFile -Parent  
        # if it doesn't exist, create it
        if (!(Test-Path $DestDir)){New-Item -ItemType Directory "$DestDir" -Force >$Null}
        $SourceFile = [io.file]::OpenRead($FileFullName)
        $DestinationFile = [io.file]::Create($DestFile)
        $FileLength=$File.Length
        $Sync.FileName="$($DestFile -Replace '^...')"
        $StopWatch2 = [Diagnostics.Stopwatch]::StartNew()
        [long]$Total2 = [long]$Count = 0
        $LastStopWatch = 0
        do {
            # copy 4MB of src file to dst file
            $Count = $SourceFile.Read($buffer, 0, $buffer.Length)
            $DestinationFile.Write($buffer, 0, $Count)
            # this is just write-progress
            # uses stopwatch to determine how long is left
            $Total2 += $Count
            $Total1 += $Count
            #
            # Global statistics
            #
            $CompletionRate1 = $Total1 / $allbytes * 100
            [int]$MSElapsed = [int]$StopWatch1.ElapsedMilliseconds
            if (($Total1 -ne $allbytes) -and ($Total1 -ne 0)) {
                [int]$RemainingSeconds1 = $MSElapsed * ($allbytes / $Total1  - 1) / 1000                
            } else {[int]$RemainingSeconds1 = 0}  
            $Label1="$Partition $FS {0,5} of {1,5} files  - {2,5} left" -f $index,$FilesCount,($FilesCount - $index)
            $Label1="$Label1{0,10:0} MB of {1,6:0} MB" -f ($Total1/1MB),($allbytes/1MB)
            $Label1="$Label1`r`n`r`nPercent complete = {0,3:0}%      {1,4} minutes {2,2} seconds remaining" -f $CompletionRate1,[math]::Truncate($RemainingSeconds1/60),($RemainingSeconds1%60)                
            $Sync.Label1=$Label1
            $Sync.CompletionRate1 = $CompletionRate1
            #
            # Statistics for current file
            #
            [int]$MSElapsed = [int]$StopWatch2.ElapsedMilliseconds
            if ($Count -ne 0) {
                $CompletionRate2 = $Total2 / $FileLength * 100
                [int]$RemainingSeconds2 = $MSElapsed * ($FileLength / $Total2  - 1) / 1000
                if ($MSElapsed -ge 1000) {
                    [single]$xferrate = $Total2 / $MSElapsed / 1mb * 1000}
                else{[single]$xferrate = 0.0}
            }else {
                $CompletionRate2 = 100
                [int]$RemainingSeconds2 = 0
            }
            $Label2="Copying file @ {0,6:n2} MB/s" -f $xferrate
            $Label2="$Label2{0,16:0} MB of {1,6:0} MB" -f ($Total2/1MB),($filelength/1MB)
            $Label2="$Label2`r`n`r`nPercent complete = {0,3:0}%      {1,4} minutes {2,2} seconds remaining" -f $CompletionRate2,[math]::Truncate($RemainingSeconds2/60),($RemainingSeconds2%60)
            If (($CompletionRate2 -eq 100) -or (($MSElapsed - $LastStopWatch) -gt 1000)){
                $LastStopWatch = $MSElapsed
            }
            $Sync.Label2=$Label2
            $Sync.CompletionRate2 = $CompletionRate2
            $Sync.Flag = 1
        } while ($Count -gt 0)
        $StopWatch2.Stop()
        $StopWatch2.Reset()
        $SourceFile.Close()
        $DestinationFile.Close()
    }
    If($fs -eq "NTFS"){$Sync.Flag = -1}
    $StopWatch1.Stop()
    $StopWatch1.Reset()
    $Buffer=$Null
    #
    }# End Function Copy_With_Progression
    #******************************************************************************
    #
    $Form = New-Object System.Windows.Forms.Form -Property @{ # Create the screen form (window)
        TopMost = $True
        ShowIcon = $False
        ControlBox = $False
        ForeColor = "White"
        BackColor = "Black"
        Text = "$Title"
        Font = 'Consolas,10'
        Width = $Width;Height = $Height
        StartPosition = "CenterScreen"
        SizeGripStyle = "Hide"
        ShowInTaskbar = $False
        MaximizeBox = $False
        MinimizeBox = $False
        KeyPreview = $True}  
    #
    # Add a OK button on the form
    $OKButton = New-Object System.Windows.Forms.Button  -Property @{
        Location = New-Object System.Drawing.Point(25,185)
        Size = New-Object System.Drawing.Size(80,26)
        DialogResult = "OK"
        }
    $Form.Controls.Add($OKButton)
    #  
    # Add a Cancel button on the form
    $CancelButton = New-Object System.Windows.Forms.Button  -Property @{
        Location = New-Object System.Drawing.Point(300,185)
        Size = New-Object System.Drawing.Size(80,26)
        DialogResult = "Cancel"}
    #
    # create label
    $Label0 = New-Object system.Windows.Forms.Label  -Property @{
        Location = New-Object System.Drawing.Point(20,10)
        Size = New-Object System.Drawing.Size(365,25)
        Font = "Arial,12"
        ForeColor = "Red"
        BackColor = "White"}
    #
    $Label1 = New-Object system.Windows.Forms.Label  -Property @{
        Location = New-Object System.Drawing.Point(60,70)
        Size = New-Object System.Drawing.Size(380,150)}
    #
    #   How to
    #
    $HowTo= New-Object system.Windows.Forms.Form  -Property @{
        TopMost = $True
        ShowIcon = $False
        ControlBox = $False
        ShowInTaskbar = $False
        Width = $Width;Height = $Height
        Font = "Arial,12"}
    #
    $HowToText = New-Object system.Windows.Forms.RichTextBox  -Property @{
        Location = New-Object System.Drawing.Point(10,30)
        Dock = "Fill"
        BackColor = 'Blue'
        ForeColor = 'White'  
        Anchor = 'top, left'}
    $HowTo.Controls.Add($HowToText)
    #
    #    Display information
    #
    $Information= New-Object system.Windows.Forms.Form  -Property @{
        TopMost = $True
        ShowIcon = $False
        ControlBox = $False
        ShowInTaskbar = $False
        StartPosition = 'CenterScreen'
        Width = $Width;Height = $Height
        BackColor = 'Blue'
        ForeColor = 'White'
        Font = "Arial,12"}
    #
    $InformationText = New-Object system.Windows.Forms.RichTextBox  -Property @{
        Location = New-Object System.Drawing.Point(10,30)
        Dock = "Fill"
        BackColor = 'Blue'
        ForeColor = 'White'
        Anchor = 'top, left'}
    $Information.Controls.Add($InformationText)
    #
    #
    #   This script must be executed with admin privilege
    #
    #   Test Administrator privileges
    $uac_error="Elevating UAC for Administrator Privileges failed"
    $Label1.Text = @"
    $uac_error!`r`n`r`nRight click on the script.
    Select "Run as administrator".
    "@
    If (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
    {  
        #   The following test is to avoid infinite looping if elevating UAC for Administrator Privileges failed
        If($param -eq $uac_error){
            $Form.ForeColor = "Red"
            $Form.BackColor = "White"
            $Label1.Location = New-Object System.Drawing.Point(20,50)
            $Label1.Size = New-Object System.Drawing.Size(380,120)
            $OKButton.Location = New-Object System.Drawing.Point(170,185)
            $OKButton.Text = "Exit"
            $Form.Controls.AddRange(@($OKButton, $Label1))
            [void]$Form.Showdialog()      
            exit}
    #   Restart the script to get Administrator privileges and exit
        Start-Process "$ScriptPath" $uac_error -Verb runAs; exit
    }
    #
    # Filebrowser dialog object
    $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{
        Title="Select ISO image or intall.wim/esd in extracted source folder"
        Multiselect = $false                 # Only one file can be chosen
        Filter = 'ISO images (*.iso;*install.wim;*install.esd)|*.iso;*install.wim;*install.esd' # Select only iso files
    }
    #
    Function Update_bcd ($partition){
    #    Set bootmenupolicy Legacy for $partition
    bcdedit /store "$partition\boot\bcd" /set '{default}' bootmenupolicy Legacy >$Null
    bcdedit /store "$partition\EFI\Microsoft\boot\bcd" /set '{default}' bootmenupolicy Legacy >$Null
    remove-item "$partition\boot\bcd.*" -force
    remove-item "$partition\EFI\Microsoft\boot\bcd.*" -force
    }
    #
    $Form.Controls.Clear()
    $Label1.Location = New-Object System.Drawing.Point(40,60)
    $Label1.Size = New-Object System.Drawing.Size(380,100)
    $Label1.Text = @"
    Would you like to show only USB sticks
    and hide external hdd/ssd disks?
    "@
    $filter = "Status='OK' and (InterfaceType='usb'and MediaType='Removable Media')"
    # YES : show only usb sticks
    # NO  : show usb sticks and external hard disks
    $Form.Controls.AddRange(@($OKButton, $Label1, $CancelButton))
    $CancelButton.Text = "No"
    $OKButton.Text = "Yes"
    If($Form.ShowDialog() -eq "Cancel"){$OnlyUSBsticks="No"}Else{$OnlyUSBsticks="Yes"}
    If($OnlyUSBsticks -eq "No")
        {$filter = "$filter or MediaType='External hard disk media'"}
    #
    $Form.Controls.Clear()
    $Label0.Text = "No USB device were detected."  
    $Label1.Text = @"
    Plug in your USB device first.`n
    Retry or Cancel.
    "@
    $OKButton.Text = "Retry"
    $CancelButton.Text = "Cancel"
    While(!($Disks=Get-CimInstance -ClassName Win32_DiskDrive -Filter $filter)){
        $Form.Controls.AddRange(@($OKButton, $CancelButton, $Label0, $Label1))  
        If($Form.ShowDialog() -eq "Cancel"){exit}}  
    #
    # Add a Source Windows ISO label on the form
    $Label1.Location = New-Object System.Drawing.Point(20,25)
    $Label1.Size = New-Object System.Drawing.Size(400,20)
    $Label1.Text = "Windows source (ISO or extracted source folder)"
    #
    # Add the ISO file name on the form
    $ISOFile = New-Object System.Windows.Forms.TextBox -Property @{
        Location = New-Object System.Drawing.Point(20,45)
        Size = New-Object System.Drawing.Size(364,24)
        Backcolor = "White"
        ForeColor = "Black"
        ReadOnly = $True
        BorderStyle = "FixedSingle"}
    #
    # Add a Target USB Disk label on the form
    $TargetUSB = New-Object System.Windows.Forms.Label -Property @{
        Location = New-Object System.Drawing.Point(20,100)
        Text = "Target USB Disk"
        Size = New-Object System.Drawing.Size(200,20)}
    #
    # Create a USB Disk drop-down list and fill it
    $USBDiskList = New-Object System.Windows.Forms.ComboBox -Property @{
        DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
        Location = New-Object System.Drawing.Point(20,120)
        Size = New-Object System.Drawing.Size(363,22)}
    #
    # Add a Select ISO button on the form
    $SelectISOButton = New-Object System.Windows.Forms.Button -Property @{
        Location = New-Object System.Drawing.Point(20,185)
        Text = "Source"
        Size = New-Object System.Drawing.Size(110,26)}
    #
    # Add a Create Setup Disk button on the form
    $OKButton.Location = New-Object System.Drawing.Point(140,185)
    $OKButton.Size = New-Object System.Drawing.Size(155,26)
    $OKButton.Text=""
    $OKButton.Enabled = $false
    #
    # Add a Cancel button on the form
    $CancelButton.Text = "Cancel"  
    #
    $SelectISOButton.Add_Click({
    If ($FileBrowser.ShowDialog() -ne "Cancel"){ # if Cancel, just ignore
        $Global:ImagePath=$FileBrowser.filename    # return the file name
        If($ImagePath.Split(".") -eq "Iso"){
            $Global:dvd = $True
            $ISOFile.Text = Split-Path -Path $ImagePath -leaf # filename (.iso)
        }Else{
            $Global:dvd = $False
            $Global:ImagePath=Split-Path $ImagePath -Parent|Split-Path -Parent
            $ISOFile.Text = $ImagePath
        }  
        if(($ISOFile.Text).length -gt 44){
            $ISOFile.Text = $ImagePath.PadRight(100).substring(0,43)+"..."}
        $OKButton.Text = "Create Setup Disk"  
        $OKButton.Enabled = $true
        $OKButton.Focus()}})
    #
    $USBDisks=@() # array with USB disk number
    Foreach ($Disk in $Disks){
        $FriendlyName = ($Disk.Caption).PadRight(40).substring(0,35)
        $USBDisks+=$Disk.Index
        $USBDiskList.Items.Add(("{0,-30}{1,10:n2} GB" -f $FriendlyName,($Disk.Size/1GB))) >$Null
    <#
        Foreach ($Partition in (Get-Partition $Disk.Index)) {
            If(!$Partition.DriveLetter){$AccessPath="  "}Else{$AccessPath=$Partition.DriveLetter+":"}
            $USBDisks+=$Disk.Index
            $USBDiskList.Items.Add((" ({0,2}){1,-30}{2,10:n2} GB" -f $AccessPath,($Partition|
                Get-Volume).FileSystemLabel.PadRight(25).substring(0,25),($Partition.Size/1GB))) >$Null
        }
    #>
    }
    #
    $GroupBox = New-Object System.Windows.Forms.GroupBox # Add a group box on the form
    $GroupBox.Location = New-Object System.Drawing.Point(7,0)
    $GroupBox.Size = New-Object System.Drawing.Size(390,225)
    #
    $Form.Controls.Clear()  
    $Form.Controls.AddRange(@($Label1, $OKbutton, $CancelButton, $ISOFile))
    $Form.Controls.AddRange(@($TargetUSB, $SelectISOButton, $USBDiskList, $GroupBox))
    #
    $USBDiskList.SelectedIndex=0
    #
    $HowToText.Text = @"
    `nPlugin your usb device
    `n`nClick "Select ISO" or install.wim/esd in extracted folders
    `n`nSelect your "Target USB disk" from the drop-down menu
    `n`nClick "Create Setup Disk" to start creating your Windows setup disk
    "@  
    $HowTo.Show()
    $HowTo.Refresh()
    $Result= $Form.ShowDialog()
    $HowTo.Dispose()
    $HowTo.Close()
    If($Result -eq "Cancel") {exit}
    #
    # At this point the mounted USB disk and the iso image file path are defined
    #
    $USB=$USBDisks[$USBDiskList.SelectedIndex]
    #
    $Form.Controls.Clear()
    $Form.Controls.AddRange(@($OKButton, $CancelButton, $Label1, $Label0))
    $OKButton.Location = New-Object System.Drawing.Point(25,185)
    $OKButton.Text = "Yes"
    $CancelButton.Text = "No"
    $OKButton.Size = New-Object System.Drawing.Size(80,26)
    $Label1.Location = New-Object System.Drawing.Point(20,50)
    $Label1.Size = New-Object System.Drawing.Size(380,180)
    $Label0.Text = "WARNING!`n"
    $Label1.Text = @"
    Your USB device will be converted to MBR scheme, repartitioned and reformatted.`n
    All partitions and data currently stored in the USB device will be erased.`n
    Are you sure you want to continue?
    "@
    if($Form.ShowDialog() -eq "Cancel") {exit}
    #
    $Information.Show()
    If($dvd){
    $InformationText.AppendText("`nMounting and checking to see if the ISO image was mounted`n")
    $Information.Refresh()
    #
    #   Check if iso already mounted by getting driveletter
    If($ISO = (Get-DiskImage $ImagePath|Get-Volume).DriveLetter){$Mounted = $True}Else
    #    Mount iso and get drive letter
        {$Mounted = $False;If(!($ISO = (Mount-DiskImage $ImagePath|Get-Volume).DriveLetter)){Exit}}
    $ISO=$ISO+":"
    }Else{
    $ISO = $ImagePath
    }
    #
    Stop-Service ShellHWDetection -ErrorAction SilentlyContinue >$Null
    $ProgressPreference="SilentlyContinue"
    #
    $InformationText.AppendText("`nCleaning the USB disk and converting it to MBR partition scheme`n")
    $Information.Refresh()
    "Select disk $USB`nclean`nconvert MBR`nexit"|diskpart >$Null
    If($LASTEXITCODE -ne 0){
        $Information.Hide()
        $Form.Controls.Clear()
        $Form.Controls.AddRange(@($OKButton, $Label1))
        $Label1.Location=New-Object System.Drawing.Point(20,80)
        $Label1.Size=New-Object System.Drawing.Size(380,100)
        $Label1.Text = "Diskpart operations failed with error code $LASTEXITCODE."
        $OKButton.Location=New-Object System.Drawing.Point(170,185)
        $OKButton.Text = "Exit"
        $Form.Controls.Add($OKButton)
        [void]$Form.Showdialog()      
        exit}
    #
    $InformationText.AppendText("`nCreating the FAT32 boot partition`n")
    $Information.Refresh()
    $usbfat32 = (New-Partition -DiskNumber $usb -Size 1GB -AssignDriveLetter|
        Format-Volume -FileSystem FAT32 -NewFileSystemLabel "BOOT").DriveLetter + ":"
    $Files32 = Get-ChildItem $iso\boot, $iso\efi, `
        $iso\sources\boot.wim, $iso\bootmgr.*, $iso\bootmgr -Recurse -File -Force
    $FilesSize = ($Files32 | measure -Sum Length).Sum/1GB  
    $msg = @"
    Fat32 1 GB partition too small. $("{0,1:n2}" -f $FilesSize) GB needed"
    "@  
    If ($FilesSize -gt 1GB){
        $Information.Hide()
        $Form.Controls.Clear()
        $Label1.Text = $msg
        $Label1.ForeColor = "Red"
        $Label1.BackColor = "White"
        $Label1.Location=New-Object System.Drawing.Point(20,80)
        $Label1.AutoSize = $True
        $OKButton.Text = "Exit"
        $OKButton.Location=New-Object System.Drawing.Point(170,185)
        $Form.Controls.AddRange(@($OKButton, $Label1))
        [void]$Form.Showdialog()
        exit
    }
    $InformationText.AppendText("`nCreating the NTFS setup partition and marking partition active")
    $Information.Refresh()    
    $usbntfs = (New-Partition -DiskNumber $usb -UseMaximumSize -AssignDriveLetter -IsActive|
        Format-Volume -FileSystem NTFS -NewFileSystemLabel "SETUP").DriveLetter + ":"
    $FilesNTFS = Get-ChildItem $iso -Recurse -File -Force
    $FilesSize = ($FilesNTFS | measure -Sum Length).Sum/1GB
    $PartitionSize = (Get-Volume ($usbntfs -Replace ".$")).Size/1GB  
    $msg = @"
    NTFS partition $("{0,1:n2}" -f $PartitionSize) too small. $("{0,1:n2}" -f $FilesSize) GB needed"      
    "@
    If($FilesSize -gt $PartitionSize){
        $Information.Hide()
        $Form.Controls.Clear()
        $Label1.Text = $msg
        $Label1.ForeColor = "Red"
        $Label1.BackColor = "White"
        $Label1.Location=New-Object System.Drawing.Point(10,80)
        $Label1.AutoSize = $True
        $OKButton.Text = "Exit"
        $OKButton.Location=New-Object System.Drawing.Point(170,185)
        $Form.Controls.AddRange(@($OKButton, $Label1))
        [void]$Form.Showdialog()
        exit
    }  
    #
    $InformationText.Text = ""
    $Information.Hide()
    Start-Service ShellHWDetection -erroraction silentlycontinue >$Null
    #
    #******************************************************************************
    #
    $Sync = [hashtable]::Synchronized(@{})
    $Sync.Host = $host
    $Sync.Flag = 0
    #
    $runspace = [runspacefactory]::CreateRunspace()
    $runspace.Open()
    $runspace.SessionStateProxy.SetVariable('Sync',$Sync)
    $powershell = [powershell]::Create()
    $powershell.Runspace = $runspace
    $powershell.AddScript({
    #
        $Width = 420; $Height = 270
        $Form = New-Object System.Windows.Forms.Form -Property @{ # Create the screen form (window)
        TopMost = $True
        ShowIcon = $False
        ControlBox = $True
        Text = " "
        Font = 'Consolas,10'
        Width = $Width; Height = $Height
        StartPosition = "CenterScreen"
        SizeGripStyle = "Hide"
        ShowInTaskbar = $False
        MaximizeBox = $False
        MinimizeBox = $False
        ForeColor = "White"
        BackColor = "Blue"}  
    #
        $form.Add_Closing({param($sender,$e)
            $Sync.Flag =-2
            [environment]::exit(1)})
        $Label1 = New-Object system.Windows.Forms.Label -Property @{
        Location = New-Object System.Drawing.Point(5, 30)
        Size = New-Object System.Drawing.Size(($Width-20),65)
        Font= "Verdana"}
        #
        $ProgressBar1 = New-Object System.Windows.Forms.ProgressBar -Property @{
        Value = 0
        Minimum = 0
        Maximum = 100
        Style="Continuous"
        Location = New-Object System.Drawing.Point(5, 90)
        Size = New-Object System.Drawing.Size(($Width-25),20)}
    #
        $Label2 = New-Object system.Windows.Forms.Label -Property @{
        Location = New-Object System.Drawing.Point(0, 130)
        Size = New-Object System.Drawing.Size(($Width-20),65)
        Font= "Verdana"}
    #
        $ProgressBar2 = New-Object System.Windows.Forms.ProgressBar -Property @{
        Value = 0
        Minimum = 0
        Maximum = 100
        Style="Continuous"
        Location = New-Object System.Drawing.Point(5, 190)
        Size = New-Object System.Drawing.Size(($Width-25),20)}
        $Form.Show()
        $Form.Controls.AddRange(@($Label1, $Label2, $ProgressBar1, $ProgressBar2))
    #  
        While ($Sync.Flag -eq 0){Start-Sleep -Milliseconds 100|Out-Null}
        While ($Sync.Flag -gt 0) {      
        $Form.Text = $Sync.FileName
        $Label1.Text = $Sync.Label1
        $Label2.Text = $Sync.Label2
        $ProgressBar1.Value  =     $Sync.CompletionRate1  
        $ProgressBar2.Value  =     $Sync.CompletionRate2
    
        $Form.Refresh()
        [System.Windows.Forms.Application]::DoEvents()
    #        $Sync.host.ui.WriteVerboseLine($Sync.label1)
        $Null = (Start-Sleep -MilliSeconds 100)
        }Else{$form.Close();$Form.Dispose()}
    })|Out-Null
    $handle = $powershell.BeginInvoke()
    #
    Copy_Progression $Files32 $usbfat32 "FAT32"
    Copy_Progression $Filesntfs $usbntfs "NTFS"
    $Sync.Flag = -1
    #******************************************************************************
    #
    $Form.Controls.Clear()
    $OKButton.Location = New-Object System.Drawing.Point(25,185)
    $OKButton.Text = "Next"
    $CancelButton.Location = New-Object System.Drawing.Point(300,185)
    $CancelButton.Text = "Abort"  
    $Label1.Text = "All folders and files copyed."
    $Label1.Location=New-Object System.Drawing.Point(30,80)
    $Label1.Size = New-Object System.Drawing.Size(380,20)
    $Form.Controls.AddRange(@($OKButton, $Label1, $CancelButton))
    If($Form.Showdialog() -eq "Cancel"){exit}
    #
    $InformationText.AppendText("`nUpdating BCD`n")
    $Information.Show()
    $Information.Refresh()
    Update_BCD $usbfat32
    Update_BCD $usbntfs
    #
    $InformationText.AppendText("`nRemoving the drive letter to hide the FAT32 boot partition`n")
    $Information.Refresh()
    Get-Volume ($usbfat32 -replace ".$")|Get-Partition|
        Remove-PartitionAccessPath -accesspath $usbfat32
    #
    If(DVD){
    $InformationText.AppendText("`nEject the mounted ISO image if it was loaded by the script")
    $Information.Refresh()
    # Eject the mounted iso image if it was loaded by the script
    If(!$Mounted){DisMount-DiskImage $ImagePath >$Null}
    }
    #
    bootsect /nt60 $usbntfs /force /mbr >$Null
    #
    $Information.Dispose()
    $Information.Close()
    $Form.Controls.Clear()
    $Form.Controls.AddRange(@($OKButton, $Label1))
    $OKButton.Location=New-Object System.Drawing.Point(170,185)  
    $Label1.Location=New-Object System.Drawing.Point(20,80)
    $Label1.AutoSize = $True
    $Label1.Text = "$Title was created successfully."
    $OKButton.Text = "Exit"
    [void]$Form.ShowDialog()