Start-SplashScreen.ps1

<#PSScriptInfo
 
.VERSION 1.4
.GUID b00d1997-e5da-4af1-86f0-92120cfab2f3
.AUTHOR Florian Salzmann
.COMPANYNAME scloud.work
.COPYRIGHT 2024 Florian Salzmann. GPL-3.0 license.
.TAGS Windows SplashScreen PowerShell
.LICENSEURI https://github.com/FlorianSLZ/scloud/blob/main/LICENSE
.PROJECTURI https://github.com/FlorianSLZ/scloud/tree/main/scripts/Start-SplashScreen
.ICONURI https://scloud.work/wp-content/uploads/Start-SplashScreen.webp
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
    2024-08-09, 1.0: Original published version.
    2024-08-13, 1.1: New icon, always in front.
    2024-08-14, 1.2: Removed OSDCloud dependency.
    2024-08-14, 1.3: -UseBasicParsing added to Invoke-WebRequest.
    2024-08-14, 1.4: Removed "Topmost" to allow better troubleshooting.
 
#>
 

<#
 
.DESCRIPTION
Start-SplashScreen is a PowerShell script designed to execute a series of scripts with a user-friendly graphical interface. It provides a visual representation of the script execution process, including progress updates and a status indicator. The script also offers the ability to open the running PowerShell terminal window for troubleshooting or additional actions.
 
#>
 


param (
    [parameter(Mandatory = $true, HelpMessage = "Specify the processes by name and powershell-command or https-link. ")]
    [array]$Processes,
    
    [parameter(Mandatory = $false, HelpMessage = "Main message on the Splash Screen.")]
    [string]$MessageHeader = "Windows Preperation",
    
    [parameter(Mandatory = $false, HelpMessage = "Initla message where the script names will show on the Splash Screen (should appear less than a second).")]
    [string]$MessageText = "Initiate Installation",
    
    [parameter(Mandatory = $false, HelpMessage = "Initla status idendicator on the Splash Screen (should appear less than a second).")]
    [string]$MessageStatus = "...",

    [parameter(Mandatory = $false, HelpMessage = "Finishing message befor Splash Screen closes")]
    [string]$MessageFinished = "All processes finished. This window closes automatically. ", 

    [parameter(Mandatory = $false, HelpMessage = "Time until Splash Screen closes after finishing")]
    [int]$ClosingTimer = 5,

    [parameter(Mandatory = $false, HelpMessage = "Background color of the Splash Screen. Eg. #CCf4f4f4 (CC = 80% transparent) or #f4f4f4")]
    [string]$ColorBackground = "#f4f4f4", 

    [parameter(Mandatory = $false, HelpMessage = "Text color of the Splash Screen. Eg. #161616")]
    [string]$ColorText = "#161616"

)


Add-Type -AssemblyName PresentationFramework
[XML]$xaml = @"
<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="$MessageHeader"
  WindowStartupLocation="CenterScreen"
  WindowStyle="None"
  ShowInTaskbar="False"
  AllowsTransparency="True"
  Background="$ColorBackground"
  Foreground="$ColorText"
  WindowState="Maximized">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="*"/>
      <RowDefinition Height="75"/>
    </Grid.RowDefinitions>
    <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
      <TextBlock Name="TextMessageHeader" Text="$MessageHeader" FontSize="32" FontWeight="Bold" TextAlignment="Center" />
      <TextBlock Name="TextMessageBody" Text="$MessageText" FontSize="16" TextWrapping="Wrap" TextAlignment="Center" FontStyle="Italic" Margin="0,10,0,10" />
      <TextBlock Name="TextMessageStatus" Text="$MessageStatus" FontSize="18" FontWeight="Bold" TextAlignment="Center"/>
    </StackPanel>
    <Button Grid.Row="1" Name="ShowTerminal" Content="" HorizontalAlignment="Stretch" Background="Transparent" BorderThickness="0" />
  </Grid>
</Window>
"@


# Load XAML
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
$Window = [Windows.Markup.XamlReader]::Load($reader)

# Create a DispatcherTimer for the "Later" button action
$timer = New-Object System.Windows.Threading.DispatcherTimer
$timer.Interval = [TimeSpan]::FromMinutes(15)
$timer.Add_Tick({
    $timer.Stop()
    $Window.Show()
}) 


$messageScreenText = $Window.FindName("TextMessageBody")
$messageScreenStatus = $Window.FindName("TextMessageStatus")

$ShowTerminalButton = $Window.FindName("ShowTerminal")
$ShowTerminalButton.Add_Click({ Show-Console })


# Credits to - http://powershell.cz/2013/04/04/hide-and-show-console-window-from-gui/
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
 
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'


function Show-Console {
    $consolePtr = [Console.Window]::GetConsoleWindow()
    [Console.Window]::ShowWindow($consolePtr, 5)
}

function Hide-Console {
    $consolePtr = [Console.Window]::GetConsoleWindow()
    [Console.Window]::ShowWindow($consolePtr, 0)
}



# Show the window
Hide-Console
$Window.Show() | Out-Null

# Run the Dispatcher
#[System.Windows.Threading.Dispatcher]::Run()

$counter = 0 
$total = $Processes.Count
foreach ($script in $Processes) {
    $counter++
    $messageScreenText.Text = "$($script.Name)"
    $messageScreenStatus.Text = "($counter/$total)"

    
    $Window.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Background, [System.Action]{})


    # Check if the value is a URL (starts with "http")
    if ($script.Script -match "^https?://") {
        Write-Output "($counter/$total) - Running online script: $($script.Script)"
        $ScriptFile = Invoke-WebRequest $($script.Script) -UseBasicParsing
        $ScriptBlock = [Scriptblock]::Create($ScriptFile.Content) 
        Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList ($args + @('someargument'))
    } else {
      # Directly run the command (assuming it's a string)
      Write-Output "($counter/$total)- Running PowerShell command: $($script.Script)"
      Invoke-Expression $($script.Script)
    }
}


# Update the UI with the final message and countdown timer
$messageScreenText.Text = $MessageFinished

# Countdown loop
for ($i = $ClosingTimer; $i -gt 0; $i--) {
    $messageScreenStatus.Text = "$i Seconds"

    # Update the UI
    $Window.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Background, [System.Action]{})
    
    # Wait for 1 second
    Start-Sleep -Seconds 1
}

# Close the window after the countdown
$Window.Close()

Show-Console