GUI/CaptureFFU.ps1

#=======================================================================
# Clear Screen
#=======================================================================
#Clear-Host
#=======================================================================
# Block
#=======================================================================
Block-StandardUser
Block-WindowsVersionNe10
Block-PowerShellVersionLt5
#=======================================================================
# PowershellWindow Functions
# Hide the PowerShell Window
# https://community.spiceworks.com/topic/1710213-hide-a-powershell-console-window-when-running-a-script
#=======================================================================
$Script:showWindowAsync = Add-Type -MemberDefinition @"
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
"@
 -Name "Win32ShowWindowAsync" -Namespace Win32Functions -PassThru
function Show-Powershell() {
    $null = $showWindowAsync::ShowWindowAsync((Get-Process -Id $pid).MainWindowHandle, 10)
}
function Hide-Powershell() {
    $null = $showWindowAsync::ShowWindowAsync((Get-Process -Id $pid).MainWindowHandle, 2)
}
#Hide-Powershell
#=======================================================================
# MahApps.Metro
#=======================================================================
# Assign current script directory to a global variable
$Global:MyScriptDir = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Definition)

# Load presentationframework and Dlls for the MahApps.Metro theme
[System.Reflection.Assembly]::LoadWithPartialName("presentationframework") | Out-Null
[System.Reflection.Assembly]::LoadFrom("$Global:MyScriptDir\assembly\System.Windows.Interactivity.dll") | Out-Null
[System.Reflection.Assembly]::LoadFrom("$Global:MyScriptDir\assembly\MahApps.Metro.dll") | Out-Null
#=======================================================================
# Console Title
#=======================================================================
#$host.ui.RawUI.WindowTitle = "CaptureFFU"
#=======================================================================
# Test-InWinPE
#=======================================================================
function Test-InWinPE {
    return Test-Path -Path Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Control\MiniNT
}
#=======================================================================
# LoadForm
#=======================================================================
function LoadForm {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True, Position = 1)]
        [string]$XamlPath
    )
    
    # Import the XAML code
    [xml]$Global:xmlWPF = Get-Content -Path $XamlPath

    # Add WPF and Windows Forms assemblies
    try {
        Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase,system.windows.forms
    } 
    catch {
        throw "Failed to load Windows Presentation Framework assemblies."
    }

    #Create the XAML reader using a new XML node reader
    $Global:xamGUI = [Windows.Markup.XamlReader]::Load((new-object System.Xml.XmlNodeReader $xmlWPF))

    #Create hooks to each named object in the XAML
    $xmlWPF.SelectNodes("//*[@Name]") | ForEach {
        Set-Variable -Name ($_.Name) -Value $xamGUI.FindName($_.Name) -Scope Global
    }
}
#=======================================================================
# LoadForm
#=======================================================================
LoadForm -XamlPath (Join-Path $Global:MyScriptDir 'CaptureFFU.xaml')
#=======================================================================
# Variables
#=======================================================================
$Global:Manufacturer = Get-MyComputerManufacturer -Brief
$Global:Model = Get-MyComputerModel -Brief
$Global:SerialNumber = Get-MyBiosSerialNumber -Brief
#=======================================================================
# Title
#=======================================================================
#$TitleLabel.Content = 'CaptureFFU'
#=======================================================================
# CaptureDrives
#=======================================================================
# Create empty array of hard disk numbers
$Global:ArrayOfDiskNumbers = @()

#Get all the CaptureDrives
$Global:CaptureDrives = Get-Disk.fixed | Where-Object {$_.IsBoot -eq $false}

# Populate the ComboBox
$Global:CaptureDrives | foreach {
    $CaptureDriveComboBox.Items.Add($_.FriendlyName) | Out-Null
    $Global:ArrayOfDiskNumbers += $_.Number
}

$CaptureDriveDetails.Content = ''

#Select the first item
$CaptureDriveComboBox.SelectedIndex = 0
#=======================================================================
# Get-CaptureDriveDetails
#=======================================================================
function Get-CaptureDriveDetails {
    $CaptureDrive = Get-Disk.fixed | Where-Object { $_.Number -eq $Global:ArrayOfDiskNumbers[$CaptureDriveComboBox.SelectedIndex] }
    
    # Work out if the size should be in GB or TB
    if ([math]::Round(($CaptureDrive.Size/1TB),2) -lt 1) {
        $CaptureDriveSize = "$([math]::Round(($CaptureDrive.Size/1000000000),0)) GB"
    }
    else {
        $CaptureDriveSize = "$([math]::Round(($CaptureDrive.Size/1000000000000),2)) TB"
    }

    $Global:DiskNumber = $CaptureDrive.DiskNumber

$CaptureDriveDetails.Content = @"
BusType: $($CaptureDrive.BusType)
MediaType: $($CaptureDrive.MediaType)
Size: $CaptureDriveSize
DiskNumber: $Global:DiskNumber
NumberOfPartitions: $($CaptureDrive.NumberOfPartitions)
PartitionStyle: $($CaptureDrive.PartitionStyle)
"@


    Get-StorageDriveDetails
}
#=======================================================================
# Get-StorageDriveDetails
#=======================================================================
function Get-StorageDriveDetails {
    $Global:DestinationDrives = @()
    $Global:DestinationDrives = Get-Disk.storage | Where-Object {$_.DiskNumber -ne $Global:DiskNumber}

    $ImageFile = $null

    if (-NOT ($Global:DestinationDrives)) {
        $ImageFileComboBox.IsEnabled = "False"
    }
    else {
        foreach ($DestinationDrive in $Global:DestinationDrives) {
            if ($DestinationDrive.DriveLetter -gt 0) {
                $ImageFileName = "disk$Global:DiskNumber"
                $ImageFile = "$($DestinationDrive.DriveLetter):\BackupFFU\$Global:Manufacturer\$Global:Model\$($Global:SerialNumber)_$($ImageFileName).ffu"
                $ImageFileComboBox.Items.Add($ImageFile) | Out-Null
            }
        }
        $ImageFileComboBox.SelectedIndex = 0
    }
}

<# $DestinationDetails.Content = @"
DiskNumber: $($DestinationDrive.DiskNumber)
FileSystemLabel: $($DestinationDrive.FileSystemLabel)
FileSystem: $($DestinationDrive.FileSystem)
Size: $($DestinationDrive.Size)
SizeRemaining: $($DestinationDrive.SizeRemaining)
"@ #>


Get-CaptureDriveDetails
#=======================================================================
# CaptureDriveComboBox
#=======================================================================
$CaptureDriveComboBox.add_SelectionChanged({
    Get-CaptureDriveDetails
    $ImageFileComboBox.Items.Clear()
    Get-StorageDriveDetails
})
#=======================================================================
# GoButton
#=======================================================================
$GoButton.add_Click({

    $DismImageFile = $ImageFileComboBox.Text
    Write-Host -ForegroundColor Cyan "DismImageFile: $DismImageFile"
    $ParentDirectory = Split-Path $DismImageFile -Parent

    $DismCaptureDrive = "\\.\PhysicalDrive$($Global:DiskNumber)"
    Write-Host -ForegroundColor Cyan "DismCaptureDrive: $DismCaptureDrive"

    $DismName = "disk$($Global:DiskNumber)"
    Write-Host -ForegroundColor Cyan "DismName: $DismName"

    $DismDescription = "$Global:Manufacturer $Global:Model $Global:SerialNumber"
    Write-Host -ForegroundColor Cyan "DismDescription: $DismDescription"

    $DismCompress = 'Default'
    Write-Host -ForegroundColor Cyan "DismCompress: $DismCompress"
    
    Write-Host "DISM.exe /Capture-FFU /ImageFile=`"$DismImageFile`" /CaptureDrive=$DismCaptureDrive /Name:`"$DismName`" /Description:`"$DismDescription`" /Compress:$DismCompress"

    if ($null -eq $DismImageFile) {
        #Do nothing
    }
    else {
        $xamGUI.Close()
        Show-Powershell
        #=======================================================================
        # Verify WinPE
        #=======================================================================
        if (-NOT (Test-InWinPE)) {
            Write-Warning "CaptureFFU must be run in WinPE"
            PAUSE
            Break
        }
        #=======================================================================
        # Enable High Performance Power Plan
        #=======================================================================
        Get-OSDPower -Property High
        #=======================================================================
        if (!(Test-Path "$ParentDirectory")) {
            Try {New-Item -Path $ParentDirectory -ItemType Directory -Force -ErrorAction Stop}
            Catch {Write-Warning "Destination appears to be Read Only. Try another Destination Drive"; Break}
        }
        DISM.exe /Capture-FFU /ImageFile="$DismImageFile" /CaptureDrive=$DismCaptureDrive /Name:"$DismName" /Description:"$DismDescription" /Compress:$DismCompress
        #Get-WindowsImage -ImagePath $ImageFile
        #=======================================================================
    }
})
#=======================================================================
# ShowDialog
#=======================================================================
$xamGUI.ShowDialog() | Out-Null
#=======================================================================