Windows 10 Upgrade Splash Screen – Take 2

Recently I tweeted a picture of the custom Windows 10-style splash screen I’m using in an implementation of Windows as a Service with SCCM (aka in-place upgrade), and a couple of people asked for the code, so here it is!

A while ago a blogged about a custom splash screen I created to use during the Windows 10 upgrade process. Since then, I’ve seen some modifications of it out there, including that of Gary Blok, where he added the Windows Setup percent complete which I quite liked. So I made a few changes to the original code as follows:

  • Added a progress bar and percentage for the Windows Setup percent complete
  • Added a timer so the user knows how long the upgrade has been running
  • Prevent the monitors from going to sleep while the splash screen is displayed
  • Added a simple way to close the splash screen in a failure scenario by setting a task sequence variable
  • Re-wrote the WPF part into XAML code

Another change is that I call the script with ServiceUI.exe from the MDT toolkit instead of via the Invoke-PSScriptasUser.ps1 as this version needs to read task sequence variables so must run in the same context as the task sequence.

I haven’t added things like looping the text, or adding TS step names as I prefer not to do that, but check out Gary’s blog if you want to know how.

To use this version, download the files from my Github repo. Make sure you download the v2 edition. Grab the ServiceUI.exe from an MDT installation and add it at top-level (use the x64 version of ServiceUI.exe if you are deploying 64-bit OS). Package these files in a package in SCCM – no program needed.

To call the splash screen, add a Run Command Line step to your upgrade task sequence and call the main script via Service UI, referencing the package:

ServiceUI.exe -process:Explorer.exe %SYSTEMROOT%\System32\WindowsPowershell\v1.0\powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File "Show-OSUpgradeBackground.ps1"

To close the screen in a failure scenario, I add 3 steps as follows:

The first step kills the splash screen simply by setting the task sequence variable QuitSplashing to True. The splash screen code will check for this variable and initiate closure of the window when set to True.

The second step just runs a PowerShell script to wait 5 seconds for the splash screen to close

The last step restores the taskbar to the screen

For that step, run the following PowerShell code:

# 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

# Restore the taskbar
[Taskbar]::Show()

Create a Custom Splash Screen for a Windows 10 In-Place Upgrade

A while back I wrote a blog with some scripts that can be used to improve the user experience in a Windows 10 in-place upgrade. The solution included a simple splash screen that runs at the beginning of the upgrade to block the screen to the user and discourage interaction with the computer during the online phase of the upgrade. Since then, I made some improvements to the screen and styled it to look more like the built-in Windows update experience in Windows 10. Using this splash screen not only discourages computer interaction during the upgrade, but also creates a consistent user experience throughout the upgrade process, for a user-initiated upgrade.

The updated screen contains an array of text sentences that you can customise as you wish. Here is an example of what it could look like:

The splash screen is not completely foolproof in that it is still possible to use certain key combinations, like ctrl-alt-del and alt-tab etc, but the mouse cursor is hidden and mouse buttons will do nothing. The intention is simply to discourage the user from using the computer during the online phase. If the computer is locked, it will display the splash screen again when unlocked. If you wish to block user interaction completely, you might consider a more hardcore approach like this or this.

To use the splash screen, download all the files in my GitHub repository here (including the bin directory). Create a standard package in ConfigMgr containing the files (no program needed) and distribute. Then add a Run PowerShell Script step in the beginning of your in-place upgrade task sequence that looks like the following (reference the package you created):

ts

Once the splash screen has been displayed, the task sequence will move on to the next step – the screen will not block the task sequence.

How does it work?

The Invoke-PSScriptAsUser.ps1 simple calls the Show-OSUpgradeBackground.ps1 and runs it in the context of the currently logged-on user so that the splash screen will be visible to the user (task sequences run in SYSTEM context so this is necessary).

The Show-OSUpgradeBackground.ps1 determines your active screens, creates a runspace for each that calls PowerShell.exe and runs the Create-FullScreenBackground.ps1 for each screen.

The Create-FullScreenBackground.ps1  does the main work of displaying the splash screen. It will hide the task bar, hide the mouse cursor and display a full screen window in the Windows 10 update style. I’ve used the excellent MahApps toolkit to create the progress ring. The text displayed in the screen can be defined by placing short sentences in the $TextArray variable. The dispatcher timer will cycle through each of the these every 10 seconds (or whatever value you set) ending with a final sentence “Windows 10 Upgrade in Progress” which will stay on the screen until the computer is restarted into the next phase of the upgrade.

You can test the splash screen before deploying it simply by running the Show-OSUpgradeBackground.ps1 script.

Remember to deselect the option Show task sequence progress in the task sequence deployment to avoid having the task sequence UI show up on top of the window.