Windows App SDK deployment guide for framework-dependent apps packaged with external location or unpackaged - Windows apps (original) (raw)

This topic provides guidance about deploying apps that are packaged with external location, or are unpackaged, and that use the Windows App SDK.

Overview

Developers of packaged with external location and unpackaged apps are responsible for deploying required Windows App SDK runtime packages to their end users. This can be done either by running the installer or by installing the MSIX packages directly. These options are described in more detail in the Deploy Windows App SDK runtime section below.

Packaged with external location and unpackaged apps also have extra runtime requirements. You must initialize access to the Windows App SDK runtime using the Bootstrapper API. In addition, the Dynamic Dependencies API can be used if your app makes use of other framework packages aside from the Windows App SDK. These requirements are described in more detail in the Runtime requirements for apps packaged with external location or unpackaged section below.

Prerequisites

Additional prerequisites

Deploy Windows App SDK runtime

Packaged with external location and unpackaged apps have two options to deploy the Windows App SDK runtime:

Option 1: Use the Installer

You can deploy all Windows App SDK runtime packages by running the installer. The installer is available at Downloads for the Windows App SDK. When running the installer (.exe), you should see an output similar to the following:

Deploying package: Microsoft.WindowsAppRuntime.1.0_0.318.928.0_x64__8wekyb3d8bbwe
Package deployment result : 0x0

Deploying package: Microsoft.WindowsAppRuntime.1.0_0.318.928.0_x86__8wekyb3d8bbwe
Package deployment result : 0x0

Deploying package: MicrosoftCorporationII.WindowsAppRuntime.Main.1.0_0.318.928.0_x64__8wekyb3d8bbwe
Package deployment result : 0x0
Provisioning result : 0x0

Deploying package: Microsoft.WindowsAppRuntime.Singleton_0.318.928.0_x64__8wekyb3d8bbwe
Package deployment result : 0x0
Provisioning result : 0x0

Deploying package: Microsoft.WinAppRuntime.DDLM.0.318.928.0-x6_0.318.928.0_x64__8wekyb3d8bbwe
Package deployment result : 0x0
Provisioning result : 0x0

Deploying package: Microsoft.WinAppRuntime.DDLM.0.318.928.0-x8_0.318.928.0_x86__8wekyb3d8bbwe
Package deployment result : 0x0
Provisioning result : 0x0

All install operations successful.

You can run the installer with no user interaction and suppress all text output with the --quiet option:

WindowsAppRuntimeInstall.exe --quiet

You can also choose to force update the MSIX packages and shutdown any currently running Windows App SDK processes using the --force option. This feature is introduced in 1.1.

WindowsAppRuntimeInstall.exe --force

To see all installer command line options, run WindowsAppRuntimeInstall --h.

After the installation is complete, you can run your packaged with external location or unpackaged app. For an example of how to build and run a packaged with external location or unpackaged app that uses the Windows App SDK, see Tutorial: Use the bootstrapper API in an app packaged with external location or unpackaged that uses the Windows App SDK.

Chain the Windows App SDK installer to your app's setup

If you have a custom setup program for your app, you can chain the Windows App SDK setup process in your app's setup process. The Windows App SDK installer currently does not provide a default UI so you will need to chain by using your setup's custom UI.

You can silently launch and track the Windows App SDK setup while showing your own view of the setup progress by using ShellExecute. The Windows App SDK installer silently unpacks the Windows App MSIX bundle and calls the PackageManager.AddPackageAsync method to complete the installation. This is very similar to other runtime installers you may have used, like .NET, Visual C++, or DirectX.

For a code example that demonstrates how to run the Windows App SDK installer from your setup program, see the RunInstaller function in the installer functional tests.

Installer sample

See the sample below to see how to launch the installer from a Win32 setup program without popping up a console window during setup:

Troubleshooting

Return codes

The following table lists the most common return codes for the Windows App SDK .exe installer. The return codes are the same for all versions of the installer.

Return code Description
0x0 Package installation or provisioning was completed successfully.
0x80073d06 One or more packages failed to install.
0x80070005 System-wide install or provisioning was not possible because the app is not running elevated or the user doing the installation doesn't have admin privileges.

Installation errors

If the Windows App SDK installer returns an error during installation, it will return an error code that describes the problem.

Option 2: Deploy Windows App SDK runtime packages directly

As an alternative to using the Windows App SDK installer for deployment to end users, you can manually deploy the MSIX packages through your app's program or MSI. This option can be best for developers who want more control.

For an example that demonstrates how your setup program can install the MSIX packages, see install.cpp in the Windows App SDK installer code.

To check whether the Windows App SDK is installed already (and, if so, what version), you can check for specific package families by calling PackageManager.FindPackagesForUserWithPackageTypes.

From a mediumIL (full trust) unpackaged process (see Application element), you can use the following code to check for a package registered to the current user:

using Windows.Management.Deployment;

public class WindowsAppSDKRuntime
{
    public static IsPackageRegisteredForCurrentUser(
        string packageFamilyName,
        PackageVersion minVersion,
        Windows.System.ProcessorArchitecture architecture,
        PackageTypes packageType)
    {
        ulong minPackageVersion = ToVersion(minVersion);

        foreach (var p : PackageManager.FindPackagesForUserWithPackageTypes(
            string.Empty, packageFamilyName, packageType)
        {
            // Is the package architecture compatible?
            if (p.Id.Architecture != architecture)
            {
                continue;
            }

            // Is the package version sufficient for our needs?
            ulong packageVersion = ToVersion(p.Id.Version);
            if (packageVersion < minPackageVersion)
            {
                continue;
            }

            // Success.
            return true;
        }

        // No qualifying package found.
        return false;
    }

    private static ulong ToVersion(PackageVersion packageVersion)
    {
        return ((ulong)packageVersion.Major << 48) |
               ((ulong)packageVersion.Minor << 32) |
               ((ulong)packageVersion.Build << 16) |
               ((ulong)packageVersion.Revision);
    }
}

For the scenario above, calling FindPackagesForUserWithPackageTypes is preferable to calling FindPackagesForUser. That's because you can narrow the search to (for this example), just framework or main packages. And that avoids matching other types of packages (such as resource, optional, or bundle) which aren't of interest for this example.

To use the current/calling user context, set the userSecurityId parameter is to an empty string.

And now some info to help you decide how to call the function in the code example above. A properly installed runtime is composed of multiple packages that depend on the system's CPU architecture:

For the Main and Singleton packages, their architecture should match the system's CPU architecture; for example, x64 packages on an x64 system. For the Framework package, an x64 system can run both x64 and x86 apps; similarly an arm64 system can run arm64, x64, and x86 apps. A DDLM package check is similar to a Framework check, except that PackageType=main, and the packagefamilyname differs, and more than one (different) packagefamilyname could be applicable, due to DDLM's unique naming scheme. For more info, see the MSIX packages spec. So the checks are more like this:

public static bool IsRuntimeRegisteredForCurrentUser(PackageVersion minVersion)
{
    ProcessorArchitecture systemArchitecture = DetectSystemArchitecture();

    return IsFrameworkRegistered(systemArchitecture, minVersion) &&
           IsMainRegistered(systemArchitecture, minVersion) &&
           IsSingletonRegistered(systemArchitecture, minVersion) &&
           IsDDLMRegistered(systemArchitecture, minVersion);
}

private static ProcecssorArchitecture DetectSystemArchitecture()
{
    // ...see the call to IsWow64Process2(), and how the result is used...
    // ...as per `IsPackageApplicable()` in
    // [install.cpp](https://github.com/microsoft/WindowsAppSDK/blob/main/installer/dev/install.cpp)
    // line 99-116...
    // ...WARNING: Use IsWow64Process2 to detect the system architecture....
    // ...         Other similar APIs exist, but don't give reliably accurate results...
}

private static bool IsFrameworkRegistered(ProcessorArchitecture systemArchitecture,
    PackageVersion minVersion)
{
    // Check x86.
    if (!IsPackageRegisteredForCurrentUser(
        global::Microsoft.WindowsAppSDK.Runtime.Packages.Framework.PackageFamilyName,
        minVersion, ProcessorArchitecture.X86,
        PackageTypes.Framework))
    {
        return false;
    }

    // Check x64 (if necessary).
    if ((systemArchitecture == ProcessorArchitecture.X64) || 
        (systemArchitecture == ProcessorArchitcture.Arm64))
    {
        if (!IsPackageRegisteredForCurrentUser(
            global::Microsoft.WindowsAppSDK.Runtime.Packages.Framework.PackageFamilyName,
            minVersion, ProcessorArchitecture.X64,
            PackageTypes.Framework))
        {
            return false;
        }
    }

    // Check arm64 (if necessary).
    if (systemArchitecture == ProcessorArchitcture.Arm64)
    {
        if (!IsPackageRegisteredForCurrentUser(
            global::Microsoft.WindowsAppSDK.Runtime.Packages.Framework.PackageFamilyName,
            minVersion, ProcessorArchitecture.Arm64,
            PackageTypes.Framework))
        {
            return false;
        }
    }

    return true;
}

private static bool IsMainRegistered(ProcessorArchitecture systemArchitecture,
    PackageVersion minVersion)
{
    return IsPackageRegisteredForCurrentUser(
        global::Microsoft.WindowsAppSDK.Runtime.Packages.Main.PackageFamilyName,
        minVersion,
        systemArchitecture,
        PackageTypes.Main);
}

private static bool IsSingletonRegistered(ProcessorArchitecture systemArchitecture,
    PackageVersion minVersion)
{
    return IsPackageRegisteredForCurrentUser(
        global::Microsoft.WindowsAppSDK.Runtime.Packages.Singleton.PackageFamilyName,
        minVersion,
        systemArchitecture,
        PackageTypes.Main);
}

private static bool IsDDLMRegistered(ProcessorArchitecture systemArchitecture,
    PackageVersion minVersion)
{
    // ...similar to IsFrameworkRegistered, but the packageFamilyName is more complicated...
    // ...and no predefined constant is currently available...
    // ...for more details, see
    // https://github.com/microsoft/WindowsAppSDK/blob/main/specs/Deployment/MSIXPackages.md.
}

The info and code above covers the basic detection scenario. To detect whether the runtime is provisioned for all users, or to do the above from an App Container, and/or do it from a packaged mediumIL process, additional logic is needed.

Deployment scenarios

Runtime requirements for apps packaged with external location or unpackaged

Apps that are packaged with external location or unpackaged have extra runtime requirements to use the Windows App SDK runtime. This involves referencing and initializing the Windows App SDK Framework package at runtime. In addition, the Dynamic Dependencies API can be used to reference other framework packages outside of the Windows App SDK.

Use the Windows App SDK runtime

Packaged with external location and unpackaged apps must call the Bootstrapper API to use the Windows App SDK at run time. This is required before the app can use Windows App SDK features such as WinUI, App Lifecycle, MRT Core, and DWriteCore. A bootstrapper component enables packaged with external location and unpackaged apps to perform these important tasks:

The simplest way to load the Windows App SDK runtime for packaged with external location and unpackaged apps is by setting the <WindowsPackageType>None</WindowsPackageType> property in your project file (.csproj or .vcxproj). You may also call the bootstrapper API directly in your app's startup code for more control over the initialization. For more details, see Use the Windows App SDK runtime for apps packaged with external location or unpackaged and Tutorial: Use the bootstrapper API in an app packaged with external location or unpackaged that uses the Windows App SDK.

Dynamic Dependencies support allows packaged with external location and unpackaged apps to keep their existing deployment mechanism, such as MSI or any installer, and be able to leverage the Windows App SDK in their application. Dynamic dependencies can be used by packaged, packaged with external location, and unpackaged apps; although it is primarily intended to be used by packaged with external location and unpackaged apps.

There is one DDLM for each version and architecture of the Windows App SDK framework package. This means on an x64 computer, you may have both an x86 and an x64 version of the DDLM to support apps of both architectures.

Reference other framework packages using Dynamic Dependencies API

If you want to use features in other framework packages outside of the Windows App SDK (e.g., DirectX), packaged with external location and unpackaged apps can call the Dynamic Dependencies API. In addition to the bootstrapper component, the Windows App SDK also provides a broader set of C/C++ functions and WinRT classes that implement the dynamic dependency API. This API is designed to be used to reference any framework package dynamically at run time.

For more information, see Use MSIX framework packages dynamically from your desktop app and the Dynamic Dependencies sample

Deploy .winmd files to the target machine

Along with your app, we recommend that you go ahead and deploy Windows Metadata (.winmd) files. Metadata can be used by various APIs and behaviors at runtime, and its absence can limit or break functionality. For example, metadata can be used to marshal objects across apartments boundaries; and the need to marshal can be a function of machine performance. Since there's no deterministic way to know whether you need metadata, you should deploy .winmds unless you're extremely concerned about size.