Tooling to create a Related Set

In my previous blog we talked about the requirement to create a related set to load code from an optional package. What is a related set and why should you do it?

When you have a scenario where you need a tight coupling where a version of your main app only works with a certain version of your optional packages(s), the content only model is not sufficient. For example, you are shipping code in the optional package that needs to be loaded or if your optional package depends on some library that is part of the main app. You can also even have just data in your optional package but you need the data to be in sync. To achieve this tight coupling, the Windows platform allows you to create what we call related sets. This versioning information is included in your main application's AppxBundleManifest.xml. The AppxBundleManifest already defines the applications' architecture specific packages, resources packages etc and now it includes the version of the optional packages that it has a version dependency with.

By defining this relationship, the platform does the job of enforcing the versioning that you defined. For example, if your users have already installed your main application, lets say Version 1. Now you decide to ship a code optional package and you also update the Main Application to define this new relationship - lets says Version 2. Now, If a user goes to the store and acquires your optional package, the platform detects that the optional package works only with the Version 2 of your main application and queues up an update for your main application and then installs the optional package. In short you do not need to validate versioning in your application code.

How to create an related set?

Via MakeAppx

The first step is to the first step is to create the individual packages that need to be in my related set. We will be using makeappx.exe that ships in the Windows Creator Update SDK to produce the related set. The input to makeappx.exe is a mapping.txt file. This is what my mapping.txt file looks like

 

 [Files]
"..\MyMainApp_1.0.5.0_x86_x64_arm.appxbundle_Folder\MyMainApp_1.0.5.0_ARM.appx" "MyMainApp_1.0.5.0_ARM.appx"
"..\MyMainApp_1.0.5.0_x86_x64_arm.appxbundle_Folder\MyMainApp_1.0.5.0_Win32.appx" "MyMainApp_1.0.5.0_Win32.appx"
"..\MyMainApp_1.0.5.0_x86_x64_arm.appxbundle_Folder\MyMainApp_1.0.5.0_x64.appx" "MyMainApp_1.0.5.0_x64.appx"


[ExternalPackages]
"..\ActivatableOptionalPackage_1.0.1.0_x86_x64_arm.appxbundle" "ActivatableOptionalPackage_1.0.1.0_x86_x64_arm.appxbundle"

Now lets generate the Main appxbundle.

 MakeAppx.exe bundle /o /p MyMainAppAtomic_1.0.5.0_x86_x64_arm.appxbundle /f mapping.txt

That's it! If you unzip the appxbundle that you just created and look under the AppxMetadata folder you will find the AppxBundleManifest.xml. Here is a Snippet of what your Main applications AppxBundleManifest.xml looks like.

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Bundle xmlns="https://schemas.microsoft.com/appx/2016/bundle" SchemaVersion="3.0">
  <Identity Name="Demo.MyMainApp"  Publisher="CN=sandepm" Version="2016.1217.5.0"/ >
  <Packages>
        <Package Type="application" Version="1.0.5.0" Architecture="arm" FileName="MyMainApp_1.0.5.0_ARM.appx" Offset="56" Size="196065">
         <Resources>
               <Resource Language="EN-US"/>
              <Resource Scale="200"/>
           </Resources>
      </Package>
        <Package Type="application" Version="1.0.5.0" Architecture="x86" FileName="MyMainApp_1.0.5.0_Win32.appx" Offset="196203" Size="188254">
           <Resources>
               <Resource Language="EN-US"/>
              <Resource Scale="200"/>
           </Resources>
      </Package>
        <Package Type="application" Version="1.0.5.0" Architecture="x64" FileName="MyMainApp_1.0.5.0_x64.appx" Offset="384537" Size="206134">
         <Resources>
               <Resource Language="EN-US"/>
              <Resource Scale="200"/>
           </Resources>
      </Package>
    </Packages>
   <OptionalBundle Name="Demo.CodeOptionalPackage"  Publisher="CN=sandepm" Version="1.0.1.0"  FileName="ActivatableOptionalPackage_1.0.1.0_x86_x64_arm.appxbundle">
    </OptionalBundle>
</Bundle>

Next step is to sign this appxbundle as you can only deploy signed apps in Windows. You can do this easily by using the signtool that is part of the Windows 10 SDK.

 signtool.exe sign -f test.pfx -fd SHA256 -v "MyMainAppAtomic_1.0.5.0_x86_x64_arm.appxbundle"

Now go ahead and try sideloading the main app followed by optional package. You can do this quite simply by double clicking the appxbundle! You will also find that now your main app can load the dll from your optional package.

Via Visual Studio 2017

If you were using Visual Studio 2017, this becomes way easier! All you have to do is at the root of your base application, create a Bundle.Mapping.txt. This file defines the optional packages that must be in a related set. This is how I created a related set definition for my test application on GitHub,

 [OptionalProjects]
"..\ActivatableOptionalPackage1\ActivatableOptionalPackage1.vcxproj"
"..\ActivatableOptionalPackage2\ActivatableOptionalPackage2.vcxproj"

Just by doing this, when you F5 your main application or create a package, Visual Studio 2017, will generate the AppxBundle.Mapping.txt. See this article for all the details.

Feel free to ask questions and comment below.

Sandeep George
Senior Program Manager