Build your first Optional Package

In my last blog we talked about why you would develop an optional package. In this segment lets try to build a simple optional package. You can find the source code to my sample app on GitHub . Feel free to comment and ask questions!

Let me breakdown what I did in the sample app in 7 simple steps. I used Visual Studio 2017 to develop the app.

Prerequisite: Your OS must be the Windows 10 Creators Update. Also install the corresponding Creators Update SDK (10.0.15063.0).

Step 1: To start you need a main application that you are building the optional package for. My simple app is called MyMainApp.

Step 2: Next I created a blank UWP C++ project and called in OptionalPackage. Note that you cannot have a managed optional package right now, so your optional package needs to be written in native code.

Step 3: Now we are ready to make the OptionalPackage project an optional package for MyMainApp. In order to do this, right click the Package.appxmanifest and open in an XML editor. Add the following in the <Dependencies> node.

 <uap3:MainPackageDependency Name="Demo.MyMainApp" />
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.15063.0" MaxVersionTested="10.0.15063.0" />

Demo.MyMainApp is the Identity of my main app. (Check the <Identity> node in the package.appxmanifest of the main app to get this). Also since optional packages in supported in Windows 10 Creators Update, specify a minimum OS version of  10.0.15063.0.

Step 4: Remove the <Capability> node. Optional packages run in the context of the main application and all the capabilities needed by your main app must be declared in the main app. Also for this example we are only loading content so we do not require any capabilities.

Also given this is a content only optional package, this package cannot have an entry in the all apps list. Set the AppListEntry property to "none"

 <Application Id="op" Executable="$targetnametoken$.exe" EntryPoint="OptionalPackage.op">
    <uap:VisualElements AppListEntry="none"  DisplayName="OptionalPackage"
    ....
    </uap:VisualElements>
</Application>

Step 5: At this point if you were to deploy the main application followed by the optional package, a peak into Windows PowerShell will confirm that you have done the association correctly. Notably, the Dependencies list would contain the optional package element.

 PS C:\WINDOWS\system32> get-appxpackage Demo.MyMainApp

Name              : Demo.MyMainApp
Publisher         : CN=sandepm
Architecture      : X64
ResourceId        :
Version           : 1.0.1.0
PackageFullName   : Demo.MyMainApp_1.0.1.0_x64__22pq88dgcvw56
InstallLocation   : C:\Users\sandepm\Source\Repos\OptionalPackageSample\x64\Debug\MyMainApp\AppX
IsFramework       : False
PackageFamilyName : Demo.MyMainApp_22pq88dgcvw56
PublisherId       : 22pq88dgcvw56
IsResourcePackage : False
IsBundle          : False
IsDevelopmentMode : True
Dependencies      : {Demo.OptionalPackage_1.0.1.0_x64__22pq88dgcvw56,
Microsoft.VCLibs.140.00.Debug_14.0.24210.0_x64__8wekyb3d8bbwe}

Step 6: Now lets load content from the Optional package. If you look at the sample app, I have created a text file "SampleFile.txt" under the Content folder in my Optional Package. We will be reading the content of this file when I click on a button in my main app. Here is the code snippet to enumerate optional packages for a given app.

 // Obtain the app's package first to then find all related packages 
auto currentAppPackage = Windows::ApplicationModel::Package::Current;

// The dependencies list is where the list of optional packages can be determined
auto dependencies = currentAppPackage->Dependencies;
std::vector< Windows::ApplicationModel::Package^ > optionalPackages;
for (auto package : dependencies)
{
    //If it is optional, then add it to our results vector
    if (package->IsOptional)
    {
        DebugPrint(L"    Optional Package found - %ws\n", package->Id->FullName->Data());
        optionalPackages.push_back(package);
    }
}

Step 7: Now lets try the file by enumerating the packages in the vector we created in step 6

 for (auto package : optionalPackages)
{
    LoadTextFromPackage(package);
}

See GitHub for the definition of the LoadTextFromPackage method, but in a nutshell, I got a reference to the InstalledLocation of the optional package I enumerated and then proceeded to get a handle to the "Content" folder and then read the contents of "SampleFile.txt"

 Windows::Storage::StorageFolder^ installFolder = package->InstalledLocation;

concurrency::create_task(installFolder->GetFolderAsync(L"Content"))
         .then([this](concurrency::task< Windows::Storage::StorageFolder^ > result)
{
    try
    {
       auto assetsFolder = result.get();               
            auto fileAsyncOp = assetsFolder->GetFileAsync(L"SampleFile.txt");
            .....
            .....
            ....
        }
}

Comment below with questions and comments! Stay tuned for the next sample where we will try to load code from an optional package!

Sandeep George
Senior Program Manager