Resources/SplashScreen/Create-FullScreenBackground.ps1
<#
GARY BLOK - Modified script from: SMSAgent https://smsagent.blog/2018/08/21/create-a-custom-splash-screen-for-a-windows-10-in-place-upgrade/ Creates a full screen 'background' styled for a Windows 10 upgrade, and hides the task bar Called by the "Show-OSUpgradeBackground" script Change Log: 23.02.19 - Added Function to read OSDCloud Transcript fields to update Splash Screen #> Param($DeviceName) Start-Transcript x:\OSDCloud\Logs\Splash.log Function Get-LogLastHeading { if ($env:SystemDrive -eq 'X:') { if (Test-Path "C:\OSDCloud\Logs"){$LogsPath = "C:\OSDCloud\Logs"} else {$LogsPath = "X:\OSDCloud\Logs"} $OSDLogFile = Get-ChildItem -Path "$LogsPath\*.log" | Where-Object {$_.Name -match "Deploy-OSDCloud.log"} } $RAWContent = Get-Content $OSDLogFile -ReadCount 1 $StartPoint = $RAWContent | Select-String -SimpleMatch "Transcript started, output" | Select-Object -Property LineNumber $LastRow = $RAWContent | Select-Object -Last 1 $SectionHeadersNumbers = $RAWContent | Select-String -SimpleMatch "====" | Select-Object -Property LineNumber if ($StartPoint){ if ($SectionHeadersNumbers){ $LastHeadersNumbers = $SectionHeadersNumbers | Select-Object -Last 1 $RAWContent[$LastHeadersNumbers.LineNumber + 1] } else { if (($LastRow -notmatch "True") -or ($LastRow -notmatch "global") -and ($LastRow.Length -gt 18)){ $LastRowText = $LastRow.Substring(18) $LastRowtext } else { Write-Output "Working on your OS Installation" } } } } function Get-NetworkStat { #Get-Adapter doesn't work in WinPE properly #$ActiveAdapter = Get-NetAdapter | Where-Object {$_.Status -eq "Up"} | Select-Object -First 1 #[int]$LinkSpeed = $ActiveAdapter.TransmitLinkSpeed $Sample = Get-Counter "\Network Interface(*)\Bytes Total/sec" #needs Windows\System32\pdh.dll added to Boot Media. $SampleValue = ($Sample.CounterSamples | Select-Object -Property CookedValue).CookedValue | Sort-Object -Descending | Select-Object -First 1 #$PercentUtil = [math]::round($SampleValue/($LinkSpeed / 8) * 100) $MBps = [math]::round($SampleValue / 1000000) #Write-Output "MB/s: $MBps | %Util: $PercentUtil" Write-Output "MB/s: $MBps" } if (!($Global:ModuleBase = (Get-Module -Name OSD).ModuleBase)){Import-Module -Name OSD} if ($Global:ModuleBase = (Get-Module -Name OSD).ModuleBase){ $Global:MahAppsPath = "$Global:ModuleBase\Projects\assembly\MahApps.Metro.dll" $Global:InteractivityPath = "$Global:ModuleBase\Projects\assembly\System.Windows.Interactivity.dll" } # Add required assemblies Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase,System.Windows.Forms,System.Drawing Add-Type -AssemblyName System.DirectoryServices.AccountManagement #Add-Type -Path "$PSSCriptRoot\bin\MahApps.Metro.dll" Write-Host "Adding Type MahApps: $Global:MahAppsPath" Add-Type -Path $Global:MahAppsPath #Add-Type -Path "$PSSCriptRoot\bin\System.Windows.Interactivity.dll" Write-Host "Adding Type Interactivity: $Global:InteractivityPath" Add-Type -Path $Global:InteractivityPath # Find screen by DeviceName $Screens = [System.Windows.Forms.Screen]::AllScreens $Screen = $Screens | Where {$_.DeviceName -eq $DeviceName} # Add custom type to hide the taskbar # Thanks to https://stackoverflow.com/questions/25499393/make-my-wpf-application-full-screen-cover-taskbar-and-title-bar-of-window $Source = @" using System; using System.Runtime.InteropServices; public class Taskbar { [DllImport("user32.dll")] private static extern int FindWindow(string className, string windowText); [DllImport("user32.dll")] private static extern int ShowWindow(int hwnd, int command); private const int SW_HIDE = 0; private const int SW_SHOW = 1; protected static int Handle { get { return FindWindow("Shell_TrayWnd", ""); } } private Taskbar() { // hide ctor } public static void Show() { ShowWindow(Handle, SW_SHOW); } public static void Hide() { ShowWindow(Handle, SW_HIDE); } } "@ Add-Type -ReferencedAssemblies 'System', 'System.Runtime.InteropServices' -TypeDefinition $Source -Language CSharp # Find the user identity from the domain if possible Try { $PrincipalContext = [System.DirectoryServices.AccountManagement.PrincipalContext]::new([System.DirectoryServices.AccountManagement.ContextType]::Domain, [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()) $GivenName = ([System.DirectoryServices.AccountManagement.Principal]::FindByIdentity($PrincipalContext,[System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName,[Environment]::UserName)).GivenName $PrincipalContext.Dispose() } Catch {} # Create a WPF window $Window = New-Object System.Windows.Window $window.Background = "#012a47" $Window.WindowStyle = [System.Windows.WindowStyle]::None $Window.ResizeMode = [System.Windows.ResizeMode]::NoResize $Window.Foreground = [System.Windows.Media.Brushes]::White $window.Topmost = $True # Get the bounds of the primary screen $Bounds = $Screen.Bounds # Assemble a grid $Grid = New-object System.Windows.Controls.Grid $Grid.Width = "NaN" $Grid.Height = "NaN" $Grid.HorizontalAlignment = "Stretch" $Grid.VerticalAlignment = "Stretch" # Add a column $Column = New-Object System.Windows.Controls.ColumnDefinition $Grid.ColumnDefinitions.Add($Column) # Add rows $Row = New-Object System.Windows.Controls.RowDefinition $Row.Height = "1*" $Grid.RowDefinitions.Add($Row) $Row = New-Object System.Windows.Controls.RowDefinition $Row.Height = [System.Windows.GridLength]::Auto $Grid.RowDefinitions.Add($Row) $Row = New-Object System.Windows.Controls.RowDefinition $Row.Height = [System.Windows.GridLength]::Auto $Grid.RowDefinitions.Add($Row) $Row = New-Object System.Windows.Controls.RowDefinition $Row.Height = "1*" $Grid.RowDefinitions.Add($Row) # Add a progress ring $ProgressRing = [MahApps.Metro.Controls.ProgressRing]::new() $ProgressRing.Opacity = 0 $ProgressRing.IsActive = $false $ProgressRing.Margin = "0,0,0,60" $Grid.AddChild($ProgressRing) $ProgressRing.SetValue([System.Windows.Controls.Grid]::RowProperty,1) # Add a textblock $TextBlock = New-Object System.Windows.Controls.TextBlock If ($GivenName) { $TextBlock.Text = "Hi $GivenName" } Else { $TextBlock.Text = "Hey Team Member" } $TextBlock.TextWrapping = [System.Windows.TextWrapping]::Wrap $TextBlock.MaxWidth = $Bounds.Width $TextBlock.Margin = "0,0,0,120" $TextBlock.FontSize = 50 $TextBlock.FontWeight = [System.Windows.FontWeights]::Light $TextBlock.VerticalAlignment = "Top" $TextBlock.HorizontalAlignment = "Center" $TextBlock.Opacity = 0 $Grid.AddChild($TextBlock) $TextBlock.SetValue([System.Windows.Controls.Grid]::RowProperty,2) # Add a textblock $TextBlock2 = New-Object System.Windows.Controls.TextBlock $TextBlock2.Margin = "0,0,0,60" $TextBlock2.Text = "Don't turn off your PC" $TextBlock2.TextWrapping = [System.Windows.TextWrapping]::Wrap $TextBlock2.MaxWidth = $Bounds.Width $TextBlock2.FontSize = 25 $TextBlock2.FontWeight = [System.Windows.FontWeights]::Light $TextBlock2.VerticalAlignment = "Bottom" $TextBlock2.HorizontalAlignment = "Center" $TextBlock2.Opacity = 0 $Grid.AddChild($TextBlock2) $TextBlock2.SetValue([System.Windows.Controls.Grid]::RowProperty,3) # Add a textblock (@gwblok Change) $TextBlock3 = New-Object System.Windows.Controls.TextBlock $TextBlock3.Margin = "0,0,0,120" $TextBlock3.Text = "Task Sequence Step Should be Here" $TextBlock3.TextWrapping = [System.Windows.TextWrapping]::Wrap $TextBlock3.MaxWidth = $Bounds.Width $TextBlock3.FontSize = 15 $TextBlock3.FontWeight = [System.Windows.FontWeights]::Light $TextBlock3.VerticalAlignment = "Bottom" $TextBlock3.HorizontalAlignment = "Center" $TextBlock3.Opacity = 0 $Grid.AddChild($TextBlock3) $TextBlock3.SetValue([System.Windows.Controls.Grid]::RowProperty,4) # Add a textblock (@gwblok Change) $TextBlock4 = New-Object System.Windows.Controls.TextBlock $TextBlock4.Margin = "0,0,60,60" $TextBlock4.Text = "Downloading..." $TextBlock4.TextWrapping = [System.Windows.TextWrapping]::Wrap $TextBlock4.MaxWidth = $Bounds.Width $TextBlock4.FontSize = 20 $TextBlock4.FontWeight = [System.Windows.FontWeights]::Light $TextBlock4.VerticalAlignment = "Bottom" $TextBlock4.HorizontalAlignment = "Right" $TextBlock4.Opacity = 0 $TextBlock4.Visibility = 'Hidden' $Grid.AddChild($TextBlock4) $TextBlock4.SetValue([System.Windows.Controls.Grid]::RowProperty,5) # Add to window $Window.AddChild($Grid) $FadeinAnimation = [System.Windows.Media.Animation.DoubleAnimation]::new(0,1,[System.Windows.Duration]::new([Timespan]::FromSeconds(3))) $FadeOutAnimation = [System.Windows.Media.Animation.DoubleAnimation]::new(1,0,[System.Windows.Duration]::new([Timespan]::FromSeconds(3))) $ColourBrighterAnimation = [System.Windows.Media.Animation.ColorAnimation]::new("#012a47","#1271b5",[System.Windows.Duration]::new([Timespan]::FromSeconds(5))) $ColourDarkerAnimation = [System.Windows.Media.Animation.ColorAnimation]::new("#1271b5","#012a47",[System.Windows.Duration]::new([Timespan]::FromSeconds(5))) #Looks for SPLASH.JSON files in OSDCloud\Config $Global:SplashScreenJSON = Get-PSDrive -PSProvider FileSystem | Where-Object {$_.Name -ne 'C'} | ForEach-Object { Get-ChildItem "$($_.Root)OSDCloud\Config\" -Include "SPLASH.JSON" -File -Recurse -Force -ErrorAction Ignore } if ($Global:SplashScreenJSON) { Write-Host -ForegroundColor Gray 'OSDCloud Config StartNet Scripts' $Global:SplashScreenJSON = $Global:SplashScreenJSON | Sort-Object -Property FullName $SplashJson = $Global:SplashScreenJSON | Select-Object -Last 1 Write-Host -ForegroundColor Gray "Using $($SplashJson.FullName)" $TextArrayJSON = get-content -Path $SplashJson.FullName | ConvertFrom-Json $TextArray = $TextArrayJSON.TextArray Write-Output $TextArray } # An array of sentences to display, in order. Leave the first one blank as the 0 index gets skipped. if (!($TextArray)){ Write-Host "No Text Array found in JSON, using Defaults" $TextArray = @( "This Line never actually displays" if ($OSVersion -and $OSBuild){"We're installing $OSVersion $OSBuild for you!"} else {"We're installing Windows for you!"} "It may take 60 - 120 minutes" "Your PC will restart several times" "OSDCloud - Good Choice!" "Yes, it's still going!" ) Write-Output $TextArray } $script:i = 0 # Start a dispatcher timer. This is used to control when the sentences are changed. $TimerCode = { $ProgressRing.IsActive = $True # The IF statement number should equal the number of sentences in the TextArray $NumberofElements = $TextArray.Count -1 If ($script:i -lt $NumberofElements) { $FadeoutAnimation.Add_Completed({ $TextBlock.Opacity = 0 $TextBlock.Text = $TextArray[$script:i] $TextBlock.BeginAnimation([System.Windows.Controls.TextBlock]::OpacityProperty,$FadeinAnimation) }) $TextBlock.BeginAnimation([System.Windows.Controls.TextBlock]::OpacityProperty,$FadeoutAnimation) } # The final sentence to display ongoing ElseIf ($script:i -eq $NumberofElements) { $script:i = 0 $FadeoutAnimation.Add_Completed({ $TextBlock.Opacity = 0 $TextBlock.Text = "We're installing Windows on this PC" $TextBlock.BeginAnimation([System.Windows.Controls.TextBlock]::OpacityProperty,$FadeinAnimation) }) $TextBlock.BeginAnimation([System.Windows.Controls.TextBlock]::OpacityProperty,$FadeoutAnimation) } Else { # Restore the taskbar [Taskbar]::Show() # Restore the mouse cursor [System.Windows.Forms.Cursor]::Show() $DispatcherTimer.Stop() $DispatcherTimerTS.Stop() exit } $ColourBrighterAnimation.Add_Completed({ $Window.Background.BeginAnimation([System.Windows.Media.SolidColorBrush]::ColorProperty,$ColourDarkerAnimation) }) $Window.Background.BeginAnimation([System.Windows.Media.SolidColorBrush]::ColorProperty,$ColourBrighterAnimation) $Script:i++ } #Main Text Timer $DispatcherTimer = New-Object -TypeName System.Windows.Threading.DispatcherTimer $DispatcherTimer.Interval = [TimeSpan]::FromSeconds(10) $DispatcherTimer.Add_Tick($TimerCode) #Step Name Timer Controls #Runs at every 1 second to try to make sure it catches all of the step names. $TimerCodeTS = { $TestInfo = Get-LogLastHeading #$TestInfo = $tsenv.Value('_SMSTSCurrentActionName') $TextBlock3.Text = $TestInfo } $DispatcherTimerTS = New-Object -TypeName System.Windows.Threading.DispatcherTimer $DispatcherTimerTS.Interval = [TimeSpan]::FromMilliseconds(1000) $DispatcherTimerTS.Add_Tick($TimerCodeTS) #Timer for Upgrade % - Should be inactivate until activated in the Main Text Timer when it reaches the upgrade step. $TimerCodeUpgrade = { $CurlProcess = Get-Process -name Curl -ErrorAction SilentlyContinue if ($CurlProcess){ $TextBlock4.Visibility = 'Visible' $TextBlock4.Text = "Downloading Process: $(Get-NetworkStat) " } else { $TextBlock4.Visibility = 'Hidden' $TextBlock4.Text = "" } <# $TestInfoUpgrade = Get-ItemPropertyValue -Path "HKLM:\SYSTEM\Setup\MoSetup\Volatile" -Name "SetupProgress" if ($TestInfoUpgrade) {$TextBlock4.Text = "Windows Setup Engine: $($TestInfoUpgrade) %"} else {$TextBlock4.Text = "Windows Setup Engine: Initializing"} #> } $DispatcherTimerUpgrade = New-Object -TypeName System.Windows.Threading.DispatcherTimer $DispatcherTimerUpgrade.Interval = [TimeSpan]::FromSeconds(5) $DispatcherTimerUpgrade.Add_Tick($TimerCodeUpgrade) # Event: Window loaded $Window.Add_Loaded({ # Activate the window to bring it to the fore $This.Activate() # Fill the screen $Bounds = $screen.Bounds $Window.Left = $Bounds.Left $Window.Top = $Bounds.Top $Window.Height = $Bounds.Height $Window.Width = $Bounds.Width # Hide the taskbar [TaskBar]::Hide() # Hide the mouse cursor [System.Windows.Forms.Cursor]::Hide() # Begin animations $TextBlock.BeginAnimation([System.Windows.Controls.TextBlock]::OpacityProperty,$FadeinAnimation) $TextBlock2.BeginAnimation([System.Windows.Controls.TextBlock]::OpacityProperty,$FadeinAnimation) $TextBlock3.BeginAnimation([System.Windows.Controls.TextBlock]::OpacityProperty,$FadeinAnimation) $TextBlock4.BeginAnimation([System.Windows.Controls.TextBlock]::OpacityProperty,$FadeinAnimation) $ProgressRing.BeginAnimation([System.Windows.Controls.TextBlock]::OpacityProperty,$FadeinAnimation) $ColourBrighterAnimation.Add_Completed({ $Window.Background.BeginAnimation([System.Windows.Media.SolidColorBrush]::ColorProperty,$ColourDarkerAnimation) }) $Window.Background.BeginAnimation([System.Windows.Media.SolidColorBrush]::ColorProperty,$ColourBrighterAnimation) }) # Event: Window closing $Window.Add_Closing({ # Restore the taskbar [Taskbar]::Show() # Restore the mouse cursor [System.Windows.Forms.Cursor]::Show() $DispatcherTimer.Stop() $DispatcherTimerTS.Stop() }) # Event: Allows to close the window on right-click (uncomment for testing) <# $Window.Add_MouseRightButtonDown({ $This.Close() }) #> # Display the window $DispatcherTimer.Start() $DispatcherTimerTS.Start() $DispatcherTimerUpgrade.Start() $Window.ShowDialog() |