Start and Restart Windows (UWP/WinUI) Applications on Windows Startup

A while ago Windows introduced a little-known feature that allows applications to automatically restart when Windows is restarted. However, rather than just look at this feature, which Windows app developer get for free, in this post we’re going to look at different options for starting and restarting a Windows application when Windows starts/restarts.

Launch on Windows Startup

The first thing we’re going to look at is how to configure a Windows (UWP/WinUI) application to automatically start when Windows starts. This is done by adding a StartupTask to the manifest of the application (i.e. the package.appxmanifest file).

<?xml version="1.0" encoding="utf-8"?>
<Package
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
  xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
  IgnorableNamespaces="uap mp">
	...
	<Applications>
		<Application Id="App"
		  Executable="$targetnametoken$.exe"
		  EntryPoint="LaunchOnWindowsStart.App">
			...
			<Extensions>
				<uap5:Extension Category="windows.startupTask">
					<uap5:StartupTask
					  TaskId="LaunchOnStartupTaskId"
					  DisplayName="My Launchable App" />
				</uap5:Extension>
			</Extensions>
		</Application>
	</Applications>
	...
</Package>

The points worth noting here are:

  • An additional namespace needs to be included for the StartupTask. In this case the namespace has been imported with a prefix of uap5.
  • TaskId – This is a string that you’ll use within the app in order to access the StartUpTask.
  • DisplayName – This is the text that will appear in the list of Windows startup tasks where a user can manually enable/disable startup tasks.

Including the StartupTask extension in the manifest file simply registers a startup task for the application with Windows. By default, the startup task will be disabled but can be enabled by the user either directly using the Windows startup task list, or when prompted by the application. Let’s look at these two options.

Toggling Startup Task Via Settings

Windows currently provides two ways to access the list of registered startup tasks. The first is via the Startup tab of Task Manager (Press Ctrl+Shift+Esc to launch Task Manager).

The other option is the Startup page within the Settings app.

In either location the user can toggle any startup task between Enabled and Disabled (or On/Off in the case of the settings app).

Just to clarify, if you add the StartupTask extension into the package.appxmanifest, a startup task for your application will appear in this list, showing the DisplayName and the publisher. The startup task will be disabled by default. From this list, the user can enable/disable your startup task. If the user enables the startup task for your application, it will launch the next time Windows starts up (or restarts).

Toggling Startup Task via the Application

A more common scenario is that you’ll want to provide an interface within the application itself for the user to toggle the behaviour when Windows starts. For example Spotify provides an option under Settings to customise the behaviour of the application when the user logs into the computer (i.e. Windows startup).

The process for toggling the state (i.e. enabled or disabled) of the startup task is to first, get a reference to the startup task, and then to either request the startup task be enabled, or to disable the task. Let’s see this in code.

In our application we’re going to have a very simple ToggleButton called LaunchOnStartupToggle and for the purpose of this post we’re going to manually set the IsChecked state (Please use data binding to a view model when implementing this in an actual app!!). When the application launches and navigates to the MainPage, we’re going to retrieve a reference to the startup task and update the IsChecked state on the ToggleButton based on the state of the startup task.

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    var startup = await StartupTask.GetAsync("LaunchOnStartupTaskId");
    UpdateToggleState(startup.State);
}
private void UpdateToggleState(StartupTaskState state)
{
    LaunchOnStartupToggle.IsEnabled = true;
    switch (state)
    {
        case StartupTaskState.Enabled:
            LaunchOnStartupToggle.IsChecked = true;
            break;
        case StartupTaskState.Disabled:
        case StartupTaskState.DisabledByUser:
            LaunchOnStartupToggle.IsChecked = false;
            break;
        default:
            LaunchOnStartupToggle.IsEnabled = false;
            break;
    }
}

Note that we’re also adjusting the IsEnabled state of the ToggleButton as there are some states where the computer policy will prevent the user overriding the state of the startup task.

Now, we need to handle when the ToggleButton changes state. For this, we’re simply going to handle the Click event on the ToggleButton (and yes, alternatively we could have handled the Checked and Unchecked events). A reference to the startup task can be retrieved using the StartupTask.GetAsync method, passing in the TaskId used in the package.appxmanifest.

private async void ToggleClick(object sender, RoutedEventArgs e)
{
    await ToggleLaunchOnStartup(LaunchOnStartupToggle.IsChecked??false);
}
private async Task ToggleLaunchOnStartup(bool enable)
{
    var startup = await StartupTask.GetAsync("LaunchOnStartupTaskId");
    switch (startup.State)
    {
        case StartupTaskState.Enabled when !enable:
            startup.Disable();
            break;
        case StartupTaskState.Disabled when enable:
            var updatedState = await startup.RequestEnableAsync();
            UpdateToggleState(updatedState);
            break;
        case StartupTaskState.DisabledByUser when enable:
            await new MessageDialog("Unable to change state of startup task via the application - enable via Startup tab on Task Manager (Ctrl+Shift+Esc)").ShowAsync();
            break;
        default:
            await new MessageDialog("Unable to change state of startup task").ShowAsync();
            break;
    }
}

To enable the startup task requires a call to RequestEnableAsync on the startup task reference. This will display a prompt for the user to choose whether to Enable or Disable the startup task for the app – note that the DisplayName set on the StartupTask in the package.appxmanifest is used in this dialog.

One important thing to note about this dialog – if the user opts to Disable the startup task, the state is changed to DisabledByUser and cannot be Enabled from within the application – calling RequestEnableAsync again will do nothing. Instead, the user should be directed to the startup tasks list in Settings or Task Manager.

Disabling the startup task from within the application is done by calling Disable on the startup task reference. Since the startup task has been disabled by the application, it can be enabled again by calling RequestEnableAsync again and allowing the user to select the Enable option.

Automatic Restart on Windows Restart

Most application don’t necessarily want to register a startup task and have the application launch every time Windows starts. However, what is convenient is that if the application is running when Windows has to restart, the application should be launched again (and ideally the application should be able to resume where the user left off). A lot of desktop applications already do this but this option wasn’t available to Windows (UWP/WinUI) applications until relatively recently.

Under Sign-in options in the Settings app, there is an option to Restart apps. This option is disabled by default, presumably because it’s being progressively rolled out to avoid disrupting users too much.

When the Restart apps option is switched on, any Windows applications that are running when Windows restarts, or if the user sings out and back in, will get relaunched.

As a Windows application developer you don’t need to do anything in order to take advantage of this. You don’t need to include a StartupTask (as previously described). The StartupTask is only required if you want your application to be launched every time Windows is started (irrespective of whether the application was running prior to Windows being restarted).

NOT WORKING!!!!

Ok, so by now if you’ve attempted to add a StartupTask, or played with the Restart apps option in Settings, you may well be getting frustrated because your application fails to launch – the splashscreen appears but then the application fails to launch.

This is because the default new project template you get when creating a new Windows (UWP/WinUI) application is significantly broken. Whilst it appears to include the basics required to run an application (i.e. the Launch event), it doesn’t include the code necessary to handle application activation. Application activation, triggered by things like a StartupTask, takes an alternative code path in a Windows application and does not call the OnLaunched method in the App class (why it doesn’t, I’ve never quite understood, since the application is indeed being launched *shrugs*).

Luckily the fix for this is relatively straight forward. You can either copy the code from the OnLaunched method override into an OnActivated method override, or you can move the OnLaunched logic into a separate method, HandleActivation, that can be called by both OnLaunched and OnActivated methods.

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    HandleActivation(e);
}
protected override void OnActivated(IActivatedEventArgs args)
{
    base.OnActivated(args);
    HandleActivation(args);
}
private void HandleActivation(IActivatedEventArgs e) {
    Frame rootFrame = Window.Current.Content as Frame;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == null)
    {
        // Create a Frame to act as the navigation context and navigate to the first page
        rootFrame = new Frame();

        rootFrame.NavigationFailed += OnNavigationFailed;

        if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            //TODO: Load state from previously suspended application
        }

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;
    }

    var launch = e as LaunchActivatedEventArgs;
    if (!(launch?.PrelaunchActivated??false))
    {
        if (rootFrame.Content == null)
        {
            // When the navigation stack isn't restored navigate to the first page,
            // configuring the new page by passing required information as a navigation
            // parameter
            rootFrame.Navigate(typeof(MainPage), launch?.Arguments);
        }
        // Ensure the current window is active
        Window.Current.Activate();
    }
}

Hopefully in this post you’ve seen how easily your application can register as a StartupTask. Don’t forget to handle the OnActivated method in your application.

1 thought on “Start and Restart Windows (UWP/WinUI) Applications on Windows Startup”

Leave a comment