MSIX Labs for Developers
MSIX Labs for Developers is a hands-on labs experience to help developers learn how to modernize their desktop apps by taking advantage of MSIX. These labs have been carefully designed to implement one feature per exercise, intentionally delivering byte sized information, and eventually building up your toolset so you can give your desktop app a true makeover!
The labs start with the desktop app ‘MyEmployees’, which you will learn to package using the MSIX packaging format, and in consecutive exercises implement Windows 10 features in the packaged app. The labs will not walk through step by step by adding lines of code. They are sample based, so will add features and help you understand the mechanics behind what made the feature work.
Related training material
If you are new to MSIX or unfamiliar with the concepts, we recommend watching the training videos prior to performing the labs. The MSIX overview video talks through the core concepts of MSIX.
- MSIX Training Overview
- MSIX Training for Developers
- MSIX Training Evolving Enhancing Desktop Apps v2
Pre-requisites
Ensure that your device has these prerequisites installed before you begin the labs:
- Windows 10, version 1903 (build 18362) or a later version
- Visual Studio 2019 or Visual Studio 2017 v15.5+ (since these versions contain the MSIX Packaging Project). Make sure you install the following workloads and optional features with Visual Studio:
- .NET Desktop development
- Universal Windows Platform development
- Windows 10 SDK
- .NET Core 3 SDK (install the latest version) (You can also do this by running the command ‘winget install Microsoft.dotnet’ from your terminal.)
- For exercise 12, ensure you have the .NET 6 SDK installed as well
- GitHub Desktop Client (You can also do this by running the command ‘winget install GitHub.GitHubDesktop’ from your terminal.)
- Activate Developer Mode on your device to be able to run the labs samples.
Setup your lab environment
- Clone the MSIX-Labs repository to your computer. You can do this either from the repository home page from your browser or by using the GitHub Desktop Client.
- Open File Explorer and navigate to the folder path MSIX-Labs\DeveloperLabs\MyEmployees in your cloned MSIX-Labs repository. Pin the current location to Quick Access by selecting the ‘Pin to Quick access’ button in the ribbon. Right click on MyEmployees.sln and select ‘Microsoft Visual Studio 2019’ from the ‘Open with’ submenu.
- To navigate between branches, you can use the Team Explorer in Visual Studio or use GitHub Desktop. We will be using GitHub Desktop.
Labs structure
The branch dev-labs-myemployees is the first branch in the series and contains the basic Desktop app MyEmployees. The labs contain 11 exercises, each exercise adds a new feature to the MyEmployees app. The branches named ‘dev-labs-exercise-*‘ contain the code at the end of each exercise with the feature added.
For example, the branch dev-labs-exercise-1-appupdate contains the update feature implemented in exercise 1, the branch dev-labs-exercise-2-backgroundtask contains the background task feature implemented in exercise 2 on top of the update feature and so on.
The master branch contains all the features and you can checkout the master branch to see what the MyEmployees app looks like with all the modern features added. Additionally, the table below lists the corresponding branch for each feature and contains its GitHub hyperlink.
Repository Branch Content | Repository Branch Name and Link |
---|---|
Basic Desktop app: MyEmployees | dev-labs-myemployees |
Exercise 1: App update | dev-labs-exercise-1-appupdate |
Exercise 1.5: Embedded .Appinstaller | dev-labs-embedded-appinstaller |
Exercise 2: Background Task | dev-labs-exercise-2-backgroundtask |
Exercise 2.5: COM Background Task | dev-labs-ComBackgroundUpdate |
Exercise 3: Toast Notification | dev-labs-exercise-3-toast |
Exercise 4: Background Transfer | dev-labs-exercise-4-bgtransfer |
Exercise 5: Picker | dev-labs-exercise-5-picker |
Exercise 6: Launcher | dev-labs-exercise-6-launcher |
Exercise 7: Share | dev-labs-exercise-7-share |
Exercise 8: Optional Package | dev-labs-exercise-8-optionalpackage |
Exercise 9: App Service | dev-labs-exercise-9-appservice |
Exercise 10: App Extension | dev-labs-exercise-10-appextension |
Exercise 11: WinRT Component | dev-labs-exercise-11-winrtcomponent |
Exercise 12: Windows App SDK Add-On | dev-labs-WinAppSDK |
MyEmployees with all modern features added | master |
MyEmployees app setup
MyEmployees is a basic Desktop app that displays employee records and their details. To run the basic app sample:
-
Checkout branch ‘dev-labs-myemployees’. From your GitHub Desktop Client, click on the ‘Current branch’ dropdown menu, and select the branch ‘dev-labs-myemployees’.
-
From the pinned ‘MyEmployees’ folder in File Explorer, open MyEmployees.sln using Visual Studio 2019.
Note: In the Visual Studio Solution Explorer pane, the project ‘MyEmployees’ is a Win32 app. The project ‘MyEmployees.Package’ is a Windows Application Packaging Project that enables the app to be packaged as an MSIX and be modernized by taking advantage of Windows 10 features. For more information, refer to the MSIX documentation - Package a desktop or UWP app in Visual Studio.
-
Expand ‘MyEmployees.Package’ from the Solution Explorer. Right-click on ‘Package.appxmanifest’ and select ‘View Designer’ from the drop-down menu. Select the ‘Packaging’ tab within the designer view.
Note: The ‘Package.appxmanifest’ file is the MyEmployees app’s manifest file formatted as XML. The fields shown in the ‘Packaging’ tab describes the package when it is deployed.
-
To build the solution, in the Standard Toolbar Options, select Configuration - Debug, Platform - x64 and Project - MyEmployees.Package. Click ‘Build Solution’ from the main menu Build dropdown.
-
To run the application you can choose either of the following options:
-
Start debugging by clicking on ‘Local Machine’ or select F5.
-
Package the app and then install it on your local machine.
-
Right click on the MyEmployees.Package project -> Publish -> Create App Packages. A window titled ‘Create App Packages’ will pop up.
-
In the ‘Select distribution method’ settings, select the ‘Sideloading’ radio button, uncheck ‘Enable automatic updates’. Click on ‘Next’.
-
In the ‘Select signing method’ settings, select the ‘Yes, select a certificate’ radio button, and click the ‘Create’ option. Fill in the ‘Publisher Common Name’ as ‘Contoso’ and create a password if you wish. You can also choose a Timestamp server if you wish. Click on ‘Next’.
If you get an error that says ‘Certificate not found’, just remove the current certificate and try step 3.
-
In the ‘Select and configure packages’ settings, you can choose to either generate an app bundle or a package. For the purpose of these labs, let’s create a package. Choose ‘Never’ in the dropdown menu for ‘Generate app bundle’. Select x64 checkbox for ‘Select the packages to create and the solution configuration mappings’. Click on ‘Create’.
-
In the ‘Finished creating package’ pop-up, click on the ‘Output location’ and open the package ‘MyEmployees.Package_1.0.0.0_x64_Debug_Test’. Double click on the .msix package to install the app.
Note: If the certificate you used to sign the app is not trusted by your machine, you will not be able to install the package.
To install the certificate on your local machine, right click on the .msix package and select ‘Properties’. In the ‘Digital Signatures’ tab, select the certificate and click on ‘Details’. In the ‘Digital Signature Details’ window, click on ‘View Certificate’, and then ‘Install Certificate’. In the ‘Certificate Import Wizard’, check the ‘Store Location’ as ‘Local Machine’. Click on ‘Next’. Select the ‘Place all certificates in the following store’ radio button and browse to ‘Trusted People’. Click on ‘Next’, and then ‘Finish’.
Now that you’ve installed the certificate, try installing the app.
-
Launch the app.
-
-
-
Open Task Manager from the Windows Start Menu and click on ‘More Details’. The ‘Processes’ tab shows the running app processes and their device resource usage. Notice the ‘MyEmployees’ app details here.
In the ‘Details’ tab in Task Manager, notice the Package name next to MyEmployees.exe. Try changing the ‘Package display name’ in the MyEmployees Package.appxmanifest file and see if it gets reflected in the Task Manager!
-
Whether you chose to debug or package the app and install the MSIX on your local machine, the app gets installed. Click on the Windows start menu and search for MyEmployees, right click on the MyEmployees app and select ‘App settings’. Notice the app details like Publisher and Version that were specified in the appxmanifest file. The version for the basic MyEmployees app should be 1.0.0.0.
-
To stop running the application, simply exit it.
If you were debugging, you can also stop debugging by pressing Shift+F5 or clicking the red square ‘Stop Debugging’ option on the Application Insights Toolbar.
-
Cleanup:
To uninstall the app, click on the Windows start menu and search for MyEmployees, right click on the MyEmployees app and select ‘Uninstall’.
Discard any changes made to this local branch if you are just trying out the features. From GitHub Desktop Client, from the Branch menu, select ‘Discard all changes’.
-
Check out the video at the end of each exercise to see how to run the sample and how the feature works and looks like.
Exercise 1: App Update
What does this feature do?
The app update feature checks whether there is a newer version of the app available at the specified location in the code by comparing version data, offers options to update the app now or later, and handles cancellation of the update in case of failure.
What is the magic sauce here?
The Scenario.cs file contains the functions for all feature scenarios added in these exercises. The function InitiateAppUpdate() is responsible for checking for the update and initiating the process.
/// <summary>
/// Initiates the scenario app update
/// </summary>
public static void InitiateAppUpdate()
{
bool checkForUpdate = AppUpdate.CheckforUpdate();
if (checkForUpdate)
{
AppUpdateHelper(updateAvailable, updateNowbutton_text, updateLaterbutton_text);
}
else
{
MessageBox.Show(upToDate, appName, MessageBoxButtons.OK);
}
}
The function AddPackageAsync is responsible for updating the application and relaunching it. This update is performed when the latest available version is larger than the app version.
deploymentOperation = packageManager.AddPackageAsync(packageUri, null, DeploymentOptions.ForceApplicationShutdown);
The following code snippet listens for the package update event, updates the progress bar and handles completion. It also handles the cancellation of the update upon failure and displays the error message.
PackageCatalog packageCatalog = PackageCatalog.OpenForCurrentUser();
packageCatalog.PackageUpdating += OnPackageUpdating;
How do I run this sample?
- Checkout branch ‘dev-labs-exercise-1-appupdate’ from your GitHub Desktop Client.
- Use the steps listed in the MyEmployees app setup section to build and run the application. Again, you can choose to either debug and run the application from Visual Studio or publish it as an MSIX package and install it.
- Notice that the Version in the MyEmployees App Settings is now 1.0.0.1
- Create a text file in ‘C:\temp’, name it version.txt and write 2.0.0.1 in it. This file is referenced by the MyEmployees app for the latest version available. In our case, since the latest version i.e. 2.0.0.1 is greater than the app version i.e. 1.0.0.1, the app will want to update itself.
- Now publish another version of the MyEmployees app from Visual Studio, but edit the version number to 2.0.0.1, and save that to ‘C:\temp’. Rename it to MyEmployees.Package.msixbundle. Note that this version should be a .msixbundle file, since the app update feature code looks for a .msixbundle file to update the app.
- Test out the update feature in the MyEmployees app by selecting Menu -> Check for updates. Try installing the update, wait for the app to update and restart, and verify the Version in the MyEmployees App Settings.
- Cleanup your environment as specified in the MyEmployees app setup section.
Exercise 1.5: Embedded .AppInstaller
What does this feature do?
This feature provides developers the ability to easily configure automatic update settings for the package. This feature also provides the functionality of being able to install the MSIX package even if the .AppInstaller URI is inaccessible (i.e the file doesn’t exist at the specified URI). The embedded .AppInstaller feature allows developers to deploy a single MSIX package while simultaneously retaining the features enabled by the .AppInstaller file format.
What is the magic sauce here?
For Windows 10, version 2104 and newer (Min Version):
From Windows 10 SDK 2104 onwards, we can make use of the UAP13 schema which provides the AutoUpdate feature highlighted in the Package.appxmanifest in the code below:
<uap13:AutoUpdate>
<uap13:AppInstaller File="Update.appinstaller" />
</uap13:AutoUpdate>
Within the MyEmployees.Package itself, we embed the “Update.appinstaller” file with the settings we would like to update with and we can publish the package as normal. Essentially, users can install the package with the MSIX package and this property will associate the “Update.appinstaller” file when required.
For Windows 10, version 1809 and newer:
Since the UAP13 schema is not available, the AppInstaller file APIs are used to achieve similar results in the AddAppInstaller() function in Form1.cs. First, the GetAppInstallerInfo() method is used as a check to see if there is an AppInstaller file for the current package as shown below:
AppInstallerInfo info = Package.Current.GetAppInstallerInfo();
if (info == null && inputPackageUri != null)
{
...
If there is not an AppInstaller file for the current package (i.e. if info==null), then the AddPackageByAppInstallerFileAsync() function is used to add one:
deploymentOperation = packageManager.AddPackageByAppInstallerFileAsync(
packageUri,
AddPackageByAppInstallerOptions.ForceTargetAppShutdown,
null);
After this, the application is restarted to trigger the first update and bring the application to the newest version. It is important to note that regardless of how the .AppInstaller is used, the package will not be updated until at least the first launch of the application. Refer to this article for .Appinstaller update settings to see different options.
How do I run this sample?
-
Checkout branch ‘dev-labs-embedded-appinstaller’ from Github Desktop Client.
-
Select the appropriate minimum platform version depending on what versions you want to build the package for. Right click on MyEmployees.Package > Properties, and change the “Target Version” and “Min Version”.
-
In Visual Studio, right-click on MyEmployees.Package > Publish > Create App Packages
-
Check Sideloading and Enable Automatic Updates.
-
Ensure the output location is “C:\temp\” and set the version to 2.0.0.0.
-
Publish the application.
-
Create an “endpoint.appInstaller” file in “C:\temp\” and use a text editor to add the following to it and save the file:
<?xml version="1.0" encoding="utf-8"?>
<AppInstaller
Uri="file:///C:/temp/endpoint.appinstaller"
Version="2.0.0.0" xmlns="http://schemas.microsoft.com/appx/appinstaller/2017/2">
<MainBundle
Name="MyEmployees"
Version="2.0.0.0"
Publisher="CN=Contoso Software"
Uri="file:///C:/temp/MyEmployees.Package_2.0.0.0_Test/MyEmployees.Package_2.0.0.0_x64.msixbundle" />
<UpdateSettings>
<OnLaunch
HoursBetweenUpdateChecks="0" />
</UpdateSettings>
</AppInstaller>
Now, if you publish and install an MSIX package with version prior to 2.0.0.0 or debug the application, you will notice that it will automatically update to 2.0.0.0. To build understanding of how this sample can be manipulated, open “Update.appinstaller” in MyEmployees.Package and test different UpdateSettings.
Exercise 2: Background Task
What does this feature do?
This sample configures a background task for the MyEmployees app that gets triggered every 15 minutes to check for an app update. If an update is available, it configures a box on the MyEmployees app window that says ‘Update Now’ to signal that an app update is available to the user.
What is the magic sauce here?
The function InitiateBackgroundCheck() in Scenario.cs initiates and registers and sets the trigger for the background task which is referenced in the MyEmployees appxmanifest file. The out-of-process task runs in the background, and when triggered, it calls the code to check for an available update, if yes, it pops up a UI box with the option for the user to update the app now or later.
/// <summary>
/// Initiates the scenario background task
/// </summary>
public static void InitiateBackgroundCheck()
{
// For permission reasons, StoreVersionData is needed to be called for a file that is stored locally. It is not required to call StoreVersionData for web server locations
StoreVersionData();
BackgroundUpdateSample.BackgroundTaskImplementation();
}
This is the entry point for the background task in the appxmanifest file.
<Extension Category="windows.backgroundTasks" EntryPoint="BackgroundUpdate.BackgroundUpdateTask">
<BackgroundTasks>
<Task Type="systemEvent" />
</BackgroundTasks>
</Extension>
How do I run this sample?
-
Checkout branch ‘dev-labs-exercise-2-backgroundtask’ from your GitHub Desktop Client.
-
In Visual Studio, open the BackgroundUpdateSample.cs file in MyEmployees -> Helpers, and comment the line:
static MaintenanceTrigger trigger = new MaintenanceTrigger(15, true);
Uncomment the line:
static SystemTrigger trigger = new SystemTrigger(SystemTriggerType.TimeZoneChange, true);
We are doing this only for testing purposes so that we can trigger the background task immediately by changing the system time zone manually, instead of waiting 15 minutes for it to happen using the MaintenanceTrigger.
-
Use the steps listed in the MyEmployees app setup section to build and run the application. Again, you can choose to either debug and run the application from Visual Studio or publish it as an MSIX package and install it.
-
Notice that the Version in the MyEmployees App Settings is now 1.0.0.2
-
Create a text file in ‘C:\temp’, name it version.txt and write 2.0.0.2 in it. This file is referenced by the MyEmployees app for the latest version available. In our case, since the latest version i.e. 2.0.0.2 is greater than the app version i.e. 1.0.0.2, the app will want to update itself.
-
Now publish another version of the MyEmployees app from Visual Studio, but edit the version number to 2.0.0.2, and save that to ‘C:\temp’. Rename it to MyEmployees.Package.msixbundle. Note that this version should be a .msixbundle file, since the app update feature code looks for a .msixbundle file to update the app.
-
Go to Date and time settings from the Windows start menu and change the time zone. This should trigger the background task, and when it detects an available update, an ‘Update Now’ box pops up on the MyEmployees app window.
-
Test out the update feature in the MyEmployees app by clicking on the ‘Update Now’ box. Try installing the update, wait for the app to update and restart, and verify the Version in the MyEmployees App Settings.
-
Cleanup your environment as specified in the MyEmployees app setup section.
Exercise 2.5: COM Background Task
What does this feature do?
This feature enables developers to add lightweight COM background tasks that can run in response to various system and time-based triggers. In this instance, the background task is being used to update the MyEmployees application, however, the background task can run (almost) independently as it runs in its own process. This allows for a multi-purpose feature that stems from the main application. One caveat is that this is only supported on Windows Build 2004 and newer. If you are developing for older versions of Windows, consider Exercise 2’s Background Tasks.
What is the magic sauce here?
For this feature to work, we leverage the Microsoft.Windows.SDK.Contracts package to enable crucial WinRT APIs and interfaces. To implement the COM background task as described here, we use the IBackgroundTask interface in a separate application “MyEmployeesUpdater” in ComBackgroundUpdate.cs.
public sealed class ComBackgroundUpdate : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
…
This class contains the Run(IBackgroundTaskInstance taskInstance) method which executes the contained code when the associated background task is triggered.
In order to build and register the background task, we use the BackgroundTaskBuilder and BackgroundTaskRegistration classes in BackgroundUpdateRegister.cs with the appropriate task trigger, name, and class GUID:
// Build an instance of the task with taskEntrypoint, name, and trigger
BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
builder.SetTrigger(trigger);
builder.Name = taskName;
builder.SetTaskEntryPointClsid(typeof(ComBackgroundUpdate).GUID);
// Register the task if it has not been registered
BackgroundTaskRegistration registration;
try
{
registration = builder.Register();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
registration = null;
return;
}
Finally, in order to register the process for the background task, we register MyEmployeesUpdater as the COM server for the specified background task class:
public static void RegisterProcessForBackgroundTask(Type backgroundTaskClass)
{
RegistrationServices registrationServices = new RegistrationServices();
registrationServices.RegisterTypeForComClients(backgroundTaskClass,
RegistrationClassContext.LocalServer,
RegistrationConnectionType.MultipleUse);
}
How do I run this sample?
-
Checkout branch ‘dev-labs-ComBackgroundUpdater’ from the Github Desktop Client.
-
In Visual Studio, right-click on MyEmployees.Package > Publish > Create App Packages.
-
Check Sideloading and uncheck Automatic Updates.
-
Ensure the output location is “C:\temp\” and set the version to 2.0.0.0.
-
Publish the application.
At this point, you can either run the MyEmployees application and wait 15 minutes for the background task to trigger which you will be alerted of with a popup, or you can follow these steps to trigger it via debugging:
-
Open the “Package.appxmanifest” file in MyEmployees.Package.
-
Navigate to the Packaging tab.
-
Change the package name from MyEmployees to any unique package name.
-
Debug MyEmployees.Package using the appropriate CPU architecture and click Local Machine.
-
To trigger the background task, click on the arrow next to Lifecycle Events -> BackgroundUpdater.
-
At this point, you will notice another instance of MyEmployees open, with the opened version being a packaged MyEmployees 2.0.0.0 versus the unpackaged MyEmployees 12. that runs when debugging in Visual Studio.
-
To build an understanding of how the sample works, try to edit the background task to change its functionality (e.g. Creating a popup with all primes from 1 to 100).
Exercise 3: Toast Notification
What does this feature do?
This sample makes the background task feature more elegant by configuring toast notifications for updates. That means whenever the background task detects an available update, it sends out a toast notification to the Windows notification panel to signal that an app update is available to the user.
What is the magic sauce here?
The function ImplementToastNotification() implements the toast notification and gets called after the background task is completed.
public static void ImplementToastNotification()
{
...
// Construct the visuals of the toast (using Notifications library)
ToastContent toastContent = new ToastContent()
{
...
// Create the toast notification and show it
var toast = new ToastNotification(doc);
DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast);
}
How do I run this sample?
-
Checkout branch ‘dev-labs-exercise-3-toast’ from your GitHub Desktop Client.
-
In Visual Studio, open the BackgroundUpdateSample.cs file in MyEmployees -> Helpers, and comment the line:
static MaintenanceTrigger trigger = new MaintenanceTrigger(15, true);
Uncomment the line:
static SystemTrigger trigger = new SystemTrigger(SystemTriggerType.TimeZoneChange, true);
We are doing this only for testing purposes so that we can trigger the background task immediately by changing the system time zone manually, instead of waiting 15 minutes for it to happen using the MaintenanceTrigger.
-
Use the steps listed in the MyEmployees app setup section to build and run the application. Again, you can choose to either debug and run the application from Visual Studio or publish it as an MSIX package and install it.
-
Notice that the Version in the MyEmployees App Settings is now 1.0.0.3
-
Create a text file in ‘C:\temp’, name it version.txt and write 2.0.0.3 in it. This file is referenced by the MyEmployees app for the latest version available. In our case, since the latest version i.e. 2.0.0.3 is greater than the app version i.e. 1.0.0.3, the app will want to update itself.
-
Now publish another version of the MyEmployees app from Visual Studio, but edit the version number to 2.0.0.3, and save that to ‘C:\temp’. Rename it to MyEmployees.Package.msixbundle. Note that this version should be a .msixbundle file, since the app update feature code looks for a .msixbundle file to update the app.
-
Go to Date and time settings from the Windows start menu and change the time zone. This should trigger the background task, and when it detects an available update, a toast notification pops up in the Windows notification side panel. This notifies the user that an update is available and reminds them to do that from the app menu.
-
Test out the update feature in the MyEmployees app by clicking on MyEmployees Menu -> Check for updates and selecting ‘Update now’. Wait for the app to update and restart, and verify the Version in the MyEmployees App Settings.
-
Cleanup your environment as specified in the MyEmployees app setup section.
Exercise 4: Background Transfer
What does this feature do?
The background transfer feature enables the MyEmployees app to download new employee records in the background, while the app can still be used. The employee records in the app window are updated once the background transfer is complete. This transfer is triggered automatically whenever new employee records are present by the same background task that is also used to detect app updates.
What is the magic sauce here?
The function DownloadNewEmployeesRecordsAsync() is called when the background task is executed, which creates a new BackgroundDownloader object and updates the employee records.
/// <summary>
/// Creates a download operation and initiates the download from a web server
/// </summary>
private static async Task DownloadNewEmployeesRecordsAsync()
{
Uri source = new Uri("https://contososoftwaremyemp.blob.core.windows.net/$web/EmployeeData.csv");
var localFolder = ApplicationData.Current.LocalFolder;
// Creates or replaces DownloadTemp.CSV in the ApplicationData's local folder
StorageFile file = await localFolder.CreateFileAsync("DownloadTemp.CSV", CreationCollisionOption.ReplaceExisting);
BackgroundDownloader downloader = new BackgroundDownloader();
DownloadOperation download = downloader.CreateDownload(source, file);
await download.StartAsync();
}
How do I run this sample?
-
Checkout branch ‘dev-labs-exercise-4-bgtransfer’ from your GitHub Desktop Client.
-
In Visual Studio, open the BackgroundUpdateSample.cs file in MyEmployees -> Helpers, and comment the line:
static MaintenanceTrigger trigger = new MaintenanceTrigger(15, true);
Uncomment the line:
static SystemTrigger trigger = new SystemTrigger(SystemTriggerType.TimeZoneChange, true);
We are doing this only for testing purposes so that we can trigger the background task immediately by changing the system time zone manually, instead of waiting 15 minutes for it to happen using the MaintenanceTrigger.
-
Use the steps listed in the MyEmployees app setup section to build and run the application. Again, you can choose to either debug and run the application from Visual Studio or publish it as an MSIX package and install it.
-
Notice that the Version in the MyEmployees App Settings is now 1.0.0.4
-
Go to Date and time settings from the Windows start menu and change the time zone. This should trigger the background task, and when it detects new employee records, they are automatically fetched using the background transfer feature and updated in the app window.
-
Cleanup your environment as specified in the MyEmployees app setup section.
Exercise 5: Picker
What does this feature do?
The picker feature modernizes the app even further by enabling users to pick a profile picture for each employee record and displays it in the app!
What is the magic sauce here?
The function PickFileAsync() in Scenario.cs sets up the file picker to allow the user to select and upload an image file for each employee.
/// <summary>
/// Pops up a file picker that allows the user to pick a single file
/// </summary>
/// <returns>A StorageFile object that represents the file the user picked</returns>
public static async Task<StorageFile> PickFileAsync()
{
// Creates the picker object and sets some of its properties
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");
openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
// Assigns the window handle to the file pickers' UI
IInitializeWithWindow initWindow = (IInitializeWithWindow)(object)openPicker;
initWindow.Initialize(GetMainWindowHandle());
// Opens the file picker for the user to pick a single file
StorageFile file = await openPicker.PickSingleFileAsync();
return file;
}
How do I run this sample?
-
Checkout branch ‘dev-labs-exercise-5-picker’ from your GitHub Desktop Client.
-
Use the steps listed in the MyEmployees app setup section to build and run the application. Again, you can choose to either debug and run the application from Visual Studio or publish it as an MSIX package and install it.
-
Notice that the Version in the MyEmployees App Settings is now 1.0.0.5
-
Click on the corresponding ‘Img’ column for the employee record you wish to update, select ‘Upload new picture’ and pick the image from the File Explorer window. See it get reflected in the MyEmployees app window.
-
Cleanup your environment as specified in the MyEmployees app setup section.
Exercise 6: Launcher
What does this feature do?
This feature configures launchers in the MyEmployees app for the Photos app (to view employee profile picture), Mail app (for mailto options) and Bing maps (to view employee location on the map, get directions etc.).
What is the magic sauce here?
In the Scenarios.cs file, the functions LaunchMailApp(), LaunchPhotosApp() and LaunchMapsApp() are responsible for setting up launchers at different points in the MyEmployees app. For example, the function LaunchMailApp() is triggered through a click event on the employee email.
/// <summary>
/// Launches the default email app and creates a new message with the specified email address
/// </summary>
/// <param name="email">The specified email address</param>
public static async void LaunchMailApp(string email)
{
String path = "mailto:" + email;
var success = await Windows.System.Launcher.LaunchUriAsync(new Uri(path));
if (!success)
{
MessageBox.Show("The mail uri launcher has failed");
}
}
How do I run this sample?
-
Checkout branch ‘dev-labs-exercise-6-launcher’ from your GitHub Desktop Client.
-
Use the steps listed in the MyEmployees app setup section to build and run the application. Again, you can choose to either debug and run the application from Visual Studio or publish it as an MSIX package and install it.
-
Notice that the Version in the MyEmployees App Settings is now 1.0.0.6
-
Click on any employee ‘Img -> View picture’ to launch the Photos app. Click on any employee Email to launch the Mail app. Click on any employee Address to launch Bing Maps.
-
Cleanup your environment as specified in the MyEmployees app setup section.
Exercise 7: Share
What does this feature do?
This feature configures the share source feature for the MyEmployees app, so a user can share an employee profile image using any app of their choice.
What is the magic sauce here?
The function InitiateShare() in Scenarios.cs file registers the app as a share source and also sets up the UI for sharing the employee picture.
/// <summary>
/// Initiates the scenario share and pops up the standard share UI
/// </summary>
public static void InitiateShare()
{
Share.RegisterForSharing(GetMainWindowHandle());
ShareDataTransferManager.ShowShareUIForWindow(GetMainWindowHandle());
}
How do I run this sample?
-
Checkout branch ‘dev-labs-exercise-7-share’ from your GitHub Desktop Client.
-
Use the steps listed in the MyEmployees app setup section to build and run the application. Again, you can choose to either debug and run the application from Visual Studio or publish it as an MSIX package and install it.
-
Notice that the Version in the MyEmployees App Settings is now 1.0.0.7
-
Click on any employee ‘Img -> Share picture’ to launch the share window and choose any app on your device to share the picture.
-
Cleanup your environment as specified in the MyEmployees app setup section.
Exercise 8: Optional Package
What does this feature do?
Optional packages contain content that can be integrated with a main package. These are useful for downloadable content (DLC), dividing a large app for size restraints, or for shipping any additional content separate from your original app. In this exercise we want to add more functionality to the MyEmployees app and allow it to import HR data for the employee records. Instead of bloating the base app, we decide to create an optional package called ‘MyEmployeesHR’ which will seamlessly plug into the base app, and allows us to componentize the MyEmployees app. This has a few benefits like being able to customize this optional package outside of the main app, monetize it separately, the freedom to choose among distribution methods, for example, the Windows Store, and the Windows platform already knows how to install the package and manage it.
What is the magic sauce here?
The optional package project ‘OptionalPackage’ is a separate UWP app project that is added as a dependent on the main MyEmployees project in the optional package’s appxmanifest file.
<uap3:MainPackageDependency Name="MyEmployees" />
The function LoadDataFromOptionalPackageAsync() implements the main functionality of the optional package i.e. importing HR data, by calling LoadHrData(), which the updates the employee records with the data.
/// <summary>
/// Searches for an optional package in the main package dependencies
/// </summary>
/// <returns>The HrData folder shared from the optional package or null if there is no optional package</returns>
public static async Task<StorageFolder> LoadDataFromOptionalPackageAsync()
{
foreach (var package in Windows.ApplicationModel.Package.Current.Dependencies)
{
if (package.IsOptional)
{
return await LoadHrData(package);
}
}
MessageBox.Show("Please install the optional package. (Refer to the readme for further instructions)");
return null;
}
/// <summary>
/// Retrieves the HrData folder from the optional package
/// </summary>
/// <param name="package">The optional package that contains the HrData folder</param>
/// <returns>The HrData folder shared from the optional package</returns>
public static async Task<StorageFolder> LoadHrData(Windows.ApplicationModel.Package package)
{
StorageFolder appInstalledFolder = package.InstalledLocation;
return await appInstalledFolder.GetFolderAsync("HrData");
}
How do I run this sample?
-
Checkout branch ‘dev-labs-exercise-8-optionalpackage’ from your GitHub Desktop Client.
-
Use the steps listed in the MyEmployees app setup section to build and run the application. Again, you can choose to either debug and run the application from Visual Studio or publish it as an MSIX package and install it.
-
Now publish the optional package (project name ‘OptionalPackage’) from Visual Studio, and install that too. Optional packages are published as appx packages. Note that the main package should be installed before the optional package.
If you chose to run the MyEmployees app via debug in the previous step, you can deploy the optional package from Visual Studio by right clicking on the project ‘OptionalPackage’ and selecting ‘Deploy’.
-
Test out the feature added by the optional package in the MyEmployees app by clicking on MyEmployees Menu -> ‘Import employee HR data’ and verify that the compensation fields got populated. If you close and restart the MyEmployees app, this data still persists (This is only if you installed the app, not via debug). Observe that the Annual compensation field is still 0s for all employees, and this is because we will extend the app with a compensation calculator in the next exercise!
-
Bonus: Wait for 15 minutes and see if the app updates employee records and pulls in new ones! Try importing HR data for these records too!
-
Notice that the Version in the MyEmployees App Settings is now 1.0.0.8. Scroll down the App Settings window and you will find the optional package ‘MyEmployeesHRPackage’ under ‘App add-ons & downloadable content’.
-
Cleanup your environment as specified in the MyEmployees app setup section.
Exercise 9: App Service
What does this feature do?
App services are UWP apps that provide services to other packaged apps. They are analogous to web services, on a device. An app service runs as a background task in the host app and can provide its service to other apps. For example, an app service might provide a bar code scanner service that other apps could use. Or perhaps an Enterprise suite of apps has a common spell checking app service that is available to the other apps in the suite. App services let developers create UI-less services that apps can call on the same device, and starting with Windows 10, version 1607, on remote devices. Starting in Windows 10, version 1607, app services that run in the same process as the host app can be created. This exercise focuses on creating and consuming an app service that runs in a separate background process. This exercise sample configures an app service called ‘MyEmployeesCalcService’ to calculate the annual compensation for employees and is used by the MyEmployees app to populate annual compensation in the employee records.
What is the magic sauce here?
The app service project ‘MyEmployeesCalcService’ is a separate UWP app project, and ‘MyAppService’ is a Windows Runtime Component, that serves as the calculator service. This service cannot directly be referenced by the MyEmployees app, so the ‘MyEmployeesCalcService’ serves as the provider to link MyEmployees to the calc service via the appxmanifest file.
<uap:Extension Category="windows.appService" EntryPoint="MyAppService.AnnualCompCalculator">
<uap3:AppService Name="com.microsoft.AnnualCompCalculator" uap4:SupportsMultipleInstances="true"/>
</uap:Extension>
The function CallAppServiceAsync() is where the magic happens. This function establishes a connection to the app service, which is running in the background, and passes it employee information like hours worked, hourly compensation, and receives the total compensation calculated by the service in an asynchronous way.
/// <summary>
/// Establishes a connection to an AppService and calls the service
/// </summary>
/// <param name="packageFamilyName">The app service provider's package family name</param>
/// <param name="appServiceName">The app service name defined in the app service provider's Package.appxmanifest file</param>
/// <returns>A response message from the AppService</returns>
public static async Task<ValueSet> CallAppServiceAsync(string packageFamilyName, string appServiceName, int numberOfEmployees)
{
AppServiceConnection appService = null;
if (appService == null)
{
appService = new AppServiceConnection();
ValueSet message = new ValueSet();
appService.AppServiceName = appServiceName;
appService.PackageFamilyName = packageFamilyName;
message.Add("EmployeeHoursWorkedData", Form1.employeeHoursWorked);
message.Add("EmployeeHourlyCompData", Form1.employeeHourlyComp);
message.Add("numberOfEmployees", numberOfEmployees);
var status = await appService.OpenAsync();
if (status == AppServiceConnectionStatus.Success)
{
AppServiceResponse response = await appService.SendMessageAsync(message);
return response.Message;
}
else
{
MessageBox.Show(status.ToString());
}
}
return null;
}
How do I run this sample?
-
Checkout branch ‘dev-labs-exercise-9-appservice’ from your GitHub Desktop Client.
-
Publish the app packages:
-
First, publish the app service (project name ‘MyEmployeesCalcService’) from within Visual Studio. Then, make sure that the app service Package family name is accurately referenced in the MyEmployees app code.
-
In the MyEmployeesCalcService project’s Package.appxmanifest file, under Packaging tab, copy the value for ‘Package family name’.
-
Now in MyEmployees project, file Form1.cs, function calculateAnnualCompensationToolStripMenuItem_Click(), verify that the first argument in CallAppServiceAsync() is same as the package family name value you copied in the previous step.
In the example below, it is “MyEmployeesCalcService_rv8ym4y7mg4aw”.
private async void calculateAnnualCompensationToolStripMenuItem_Click(object sender, EventArgs e) { if (employeeHourlyComp != null && employeeHoursWorked != null) { ValueSet annualComp = await Scenarios.CallAppServiceAsync("MyEmployeesCalcService_rv8ym4y7mg4aw", "com.microsoft.AnnualCompCalculator", employeeBindingSource.Count); ... } ... }
-
-
Then publish the MyEmployees package and the optional package (project name ‘OptionalPackage’) from Visual Studio, just like in the previous exercise.
-
Then publish the MyEmployees package.
-
-
Install the MyEmployees main package first, then the optional package and app service.
-
Import the employee HR data in the MyEmployees app by clicking on MyEmployees Menu -> ‘Import employee HR data’ and verify that the compensation fields got populated. Now test out the app service by clicking on the MyEmployees Menu -> ‘Calculate annual compensation’ and verify that the field ‘Annual Comp.’ got populated.
-
Bonus: Wait for 15 minutes and see if the app updates employee records and pulls in new ones! Try importing HR data and calculating annual compensation for these records too!
-
Notice that the Version in the MyEmployees App Settings is now 1.0.0.9. Also notice that the app service ‘MyEmployeesCalcService’ shows up in the Windows Start Menu.
-
Cleanup your environment as specified in the MyEmployees app setup section. Don’t forget to uninstall the app service too.
Exercise 10: App Extension
What does this feature do?
In Windows 10, app extensions provide functionality similar to what plug-ins, add-ins, and add-ons do on other platforms. They are UWP apps or packaged desktop apps that have an extension declaration that allows them to share content and deployment events with a host app. An extension app can provide multiple extensions. App extensions are similar to optional packages with a key difference in the level of trust - App extensions are meant for 3rd parties to build add-on experiences to your application which the app may not trust fully while optional packages run with the identity of the base app. So when the MyEmployees app is declared as an extension host, it creates an opportunity to develop an ecosystem around the app in which other developers can enhance it through extensions. For example, Microsoft Office extensions, Visual Studio extensions, browser extensions, etc. create richer experiences for the base apps.
In this exercise, the app extension called ‘MyEmployeesImageExtension’ allows the MyEmployees app user to set app background images through a simple app menu option.
What is the magic sauce here?
The project ‘MyEmployeesImageExtension’ is a UWP project that is declared as an app extension in its appxmanifest.
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.contosoassetext"
Id="BackgroundImage"
DisplayName="Background Image"
Description ="This extension provides image assets"
PublicFolder="Public">
</uap3:AppExtension>
The MyEmployees app is registered as an app extension host in its appxmanifest so that it can be aware of the app extension.
<uap3:Extension Category="windows.appExtensionHost">
<uap3:AppExtensionHost>
<uap3:Name>com.microsoft.contosoassetext</uap3:Name>
</uap3:AppExtensionHost>
</uap3:Extension>
The function InitiateAndExecuteAppExtensions() serves as an entry point and executes the scenario by calling ExecuteImageLoadScenario() to load the image and set it as the MyEmployees background.
/// <summary>
/// Initiates and executes the scenario app extensions
/// </summary>
public static async void InitiateAndExecuteAppExtensions()
{
AppExtension extension = null;
extension = await ExtensionManager.GetAppExtension(appExtensionId, appExtensionName);
if (extension == null)
{
MessageBox.Show("Please install the app extension (Refer to the readme for further instructions) ");
}
else
{
ExtensionManager.ExecuteImageLoadScenario(extension);
}
}
How do I run this sample?
-
Checkout branch ‘dev-labs-exercise-10-appextension’ from your GitHub Desktop Client.
-
Use the steps listed in the MyEmployees app setup section to build and run the application. Again, you can choose to either debug and run the application from Visual Studio or publish it as an MSIX package and install it.
-
Now publish the app extension (project name ‘MyEmployeesImageExtension’) from Visual Studio, and install that too. App extensions are published as appx packages. Note that the main package should be installed before the app extension.
If you chose to run the MyEmployees app via debug in the previous step, you can deploy the app extension from Visual Studio by right clicking on the project ‘MyEmployeesImageExtension’ and selecting ‘Deploy’.
-
You will need to install the optional package and app service as explained in previous exercises to see all the features work together, although the app extension feature is not coupled with those, and will work independently as long as the main MyEmployees app is installed.
-
Notice that the Version in the MyEmployees App Settings is now 1.0.0.10
-
Click on the MyEmployees ‘Menu -> Change background image’ option and notice the background image has changed in the app window.
-
Cleanup your environment as specified in the MyEmployees app setup section. Don’t forget to uninstall the app service and app extension too.
Exercise 11: Using a WinRT Component
What does this feature do?
A Windows Runtime component is a self-contained software module that can be authored, referenced, and used with any Windows Runtime language (including C#, C++/WinRT, Visual Basic, JavaScript, and C++/CX). This exercise sample creates a Windows Runtime component (project ‘RuntimeComponent’) that is used in the MyEmployees app and enables the MyEmployees user to export employee data to a csv file.
What is the magic sauce here?
The WinRT Component project ‘RuntimeComponent’ implements the export data functionality. When this project is built, it produces a .winmd file, which is referenced by MyEmployees in its .csproj file. The MyEmployees project has been ported to .NET Core to be able to use the WinRT component.
The function ExportData() implements the meat of the functionality.
/// <summary>
/// Calls an API from the WinRT Component, which exports employee hr data to a specified file
/// </summary>
/// <param name="data">The data that is passed in to the WinRT API</param>
public static async void ExportData(IList data)
{
try
{
StorageFile file = await PickCsvFileAsync();
if (file != null && RuntimeComponent.Class1.ExportData(data, file.Path))
{
MessageBox.Show("The file was successfully saved");
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
How do I run this sample?
-
Checkout branch ‘dev-labs-exercise-11-winrtcomponent’ from your GitHub Desktop Client.
-
Use the steps listed in the MyEmployees app setup section to build and run the application. Again, you can choose to either debug and run the application from Visual Studio or publish it as an MSIX package and install it.
-
You will need to install the optional package, app service and app extension as explained in previous exercises to see all the features work together, although the WinRT component is not coupled with those, and will work independently as long as the main MyEmployees app is installed.
-
Notice that the Version in the MyEmployees App Settings is now 1.0.0.11
-
To test the WinRT component, create a csv file in ‘C:\temp’ and name it ‘emp.csv’. Click on the MyEmployees ‘Menu -> Export employee data’ option and select the file emp.csv you just created. A pop up dialog box indicates that the file was successfully saved. Verify that the file emp.csv has all the employee records.
-
Cleanup your environment as specified in the MyEmployees app setup section.
Exercise 12: Windows App SDK
What does this feature do?
The Windows App SDK is a set of additional developer components and tools that extends an application’s capabilities. The framework provides a unified set of APIs and tools that can be used in a consistent way by any desktop app on Windows 11 and downlevel to Windows 10, version 1809. This sample implements the WinAppSDK by adding and then deploying the correct packages.
What is the magic sauce here?
As you can note here, a requirement for this feature to work is that the project use C# .NET 6. This meant that MyEmployees had to be upgraded from .NET 4.7.2 to a supported framework version. To do this, the .NET Upgrade Assistant was used. This upgrade assistant is completely open-source, with the goal of enabling developers to modernize their applications.
Once the application is updated to .NET 6, there are two steps in accessing the full functionality of the WinAppSDK:
- Deploy the Windows App SDK framework package
- Call the Deployment API
To deploy the WinAppSDK framework, we first add the following Nuget packages to the application to provide the functionality:
And then reference them in the package’s project file (MyEmployees.Package.wapproj):
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.1.2"/>
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="[10.0.22621.1]">
<IncludeAssets>build</IncludeAssets>
</PackageReference>
</ItemGroup>
The issue with only referencing the framework package is that the Windows application model does not support declaring a dependency on the Main and Singleton packages. What this means is that in order to access features in these packages, we have to deploy them separately. This can be achieved by either redistributing the MSIX packages using your own install method (not recommended) or deploying with the Deployment API’s (used in this example).
Deploying these packages gives us additional functionality. You should call the Deployment API after your app’s process is initialized, but before your app uses Windows App SDK runtime features that use the Singleton package (e.g., Push Notifications).
In this case, the initializeWinAppRuntime() function in WinAppSDK.cs initializes the packages as follows:
if (DeploymentManager.GetStatus().Status != DeploymentStatus.Ok)
{
var initializeTask = Task.Run(() => DeploymentManager.Initialize());
initializeTask.Wait();
In the event that it is necessary to check if the packages are already deployed, we can use the getWinAppRuntimeStatus(). At this point, you can publish the application or add WinAppSDK features outlined here.
How do I run this sample?
-
Checkout branch ‘dev-labs-WinAppSDK’ from Github Desktop Client.
- To toggle between deploying the additional packages and not deploying them, comment/uncomment the following line in Form1.cs.
MyEmployees.Helpers.WinAppSDK.initializeWinAppRuntime();
-
In Visual Studio, right-click on MyEmployees.Package > Publish > Create App Packages
-
Run the application and if you have chosen to deploy the additional packages, you should see a pop-up indicating its success.
- To build an understanding of how the sample works, try to implement a WinAppSDK API or attempt to use a feature from the singleton package to see what errors you get.
Conclusion
We hope that you enjoyed working through these lab exercises and learnt more about modernizing your Windows app! We recommend the following channels to stay engaged with the MSIX team: