ストア以外で公開されたアプリ パッケージをコードから更新する

アプリを MSIX として公開する場合は、アプリケーションの更新をプログラムを使用して開始できます。 ストアの外部にアプリを配置する場合、アプリの新しいバージョンがないかサーバーを確認し、その新しいバージョンをインストールするだけで済みます。 更新プログラムの適用方法は、アプリ インストーラー ファイルを使用してアプリ パッケージを配置するかどうかによって異なります。 コードから更新プログラムを適用するには、アプリ パッケージで packageManagement 機能を宣言する必要があります。 これは公開元間のシナリオでは必須ですが、機能を宣言しなくても、独自のアプリを管理することは可能です。

この記事では、パッケージ マニフェストで packageManagement 機能を宣言する方法と、コードから更新プログラムを適用する方法を示す例を紹介します。 最初のセクションでは、アプリ インストーラー ファイルを使用する場合にこれを実行する方法について説明します。2 番目のセクションでは、アプリ インストーラー ファイルを使用しない場合の方法について説明します。 最後のセクションでは、更新プログラムが適用された後にアプリが再起動されることを確認する方法について説明します。

PackageManagement 機能をパッケージ マニフェストに追加する

PackageManager API を使用するには、アプリについて、packageManagement制限付き機能パッケージ マニフェストで宣言する必要があります。

<Package>
...

  <Capabilities>
    <rescap:Capability Name="packageManagement" />
  </Capabilities>
  
...
</Package>

アプリ インストーラー ファイルを使用して配置されたパッケージを更新する

アプリケーション インストーラー ファイルを使用してアプリケーションを配置する場合、実行するコード駆動型の更新では、アプリ インストーラー ファイル API を使用する必要があります。 これにより、通常のアプリ インストーラー ファイルの更新が引き続き機能するようになります。 コードからアプリ インストーラー ベースの更新プログラムを開始するには、PackageManager.AddPackageByAppInstallerFileAsync または PackageManager.RequestAddPackageByAppInstallerFileAsync を使用できます。 Package.CheckUpdateAvailabilityAsync API を使用して更新プログラムが利用可能かどうかを確認できます。 下にコード例を示します。

using Windows.Management.Deployment;

public async void CheckForAppInstallerUpdatesAndLaunchAsync(string targetPackageFullName, PackageVolume packageVolume)
{
    // Get the current app's package for the current user.
    PackageManager pm = new PackageManager();
    Package package = pm.FindPackageForUser(string.Empty, targetPackageFullName);

    PackageUpdateAvailabilityResult result = await package.CheckUpdateAvailabilityAsync();
    switch (result.Availability)
    {
        case PackageUpdateAvailability.Available:
        case PackageUpdateAvailability.Required:
            //Queue up the update and close the current instance
            await pm.AddPackageByAppInstallerFileAsync(
            new Uri("https://trial3.azurewebsites.net/HRApp/HRApp.appinstaller"),
            AddPackageByAppInstallerOptions.ForceApplicationShutdown,
            packageVolume);
            break;
        case PackageUpdateAvailability.NoUpdates:
            // Close AppInstaller.
            await ConsolidateAppInstallerView();
            break;
        case PackageUpdateAvailability.Unknown:
        default:
            // Log and ignore error.
            Logger.Log($"No update information associated with app {targetPackageFullName}");
            // Launch target app and close AppInstaller.
            await ConsolidateAppInstallerView();
            break;
    }
}

アプリ インストーラー ファイルを使用して配置されたパッケージを更新する

サーバーで更新プログラムを確認する

アプリケーション パッケージの配置にアプリ インストーラー ファイルを使用しない場合、最初の手順は、アプリケーションの新しいバージョンが使用可能かどうかを直接確認することです。 次の例では、サーバー上のパッケージのバージョンが、アプリの現在のバージョンよりも新しいかどうかを確認しています (この例では、デモンストレーション用のテスト サーバーを参照しています)。

using Windows.Management.Deployment;

//check for an update on my server
private async void CheckUpdate(object sender, TappedRoutedEventArgs e)
{
    WebClient client = new WebClient();
    Stream stream = client.OpenRead("https://trial3.azurewebsites.net/HRApp/Version.txt");
    StreamReader reader = new StreamReader(stream);
    var newVersion = new Version(await reader.ReadToEndAsync());
    Package package = Package.Current;
    PackageVersion packageVersion = package.Id.Version;
    var currentVersion = new Version(string.Format("{0}.{1}.{2}.{3}", packageVersion.Major, packageVersion.Minor, packageVersion.Build, packageVersion.Revision));

    //compare package versions
    if (newVersion.CompareTo(currentVersion) > 0)
    {
        var messageDialog = new MessageDialog("Found an update.");
        messageDialog.Commands.Add(new UICommand(
            "Update",
            new UICommandInvokedHandler(this.CommandInvokedHandler)));
        messageDialog.Commands.Add(new UICommand(
            "Close",
            new UICommandInvokedHandler(this.CommandInvokedHandler)));
        messageDialog.DefaultCommandIndex = 0;
        messageDialog.CancelCommandIndex = 1;
        await messageDialog.ShowAsync();
    } else
    {
        var messageDialog = new MessageDialog("Did not find an update.");
        await messageDialog.ShowAsync();
    }
}

Note

targetPackageFileName は、MSIX パッケージ アプリのフル ネームを代表しています。 (例: Contoso.HeadTrax_1.0.0.0_x64__PublisherHash)

更新プログラムを適用する

更新プログラムが使用可能であることを確認したら、AddPackageAsync API を使用して、それをダウンロードしてインストールするようにキューに入れておくことができます。 また、メイン パッケージがデバイスに既にインストールされている限り、オプションのパッケージをインストールすることもできます。 更新プログラムは、次回アプリがシャットダウンされたときに適用されます。 アプリが再起動されると、ユーザーは新しいバージョンを使用できるようになります。 下にコード例を示します。


// Queue up the update and close the current app instance.
private async void CommandInvokedHandler(IUICommand command)
{
    if (command.Label == "Update")
    {
        PackageManager packagemanager = new PackageManager();
        await packagemanager.AddPackageAsync(
            new Uri("https://trial3.azurewebsites.net/HRApp/HRApp.msix"),
            null,
            AddPackageOptions.ForceApplicationShutdown
        );
    }
}

更新後にアプリを自動的に再起動する

アプリケーションが UWP アプリの場合は、更新の適用時に AddPackageByAppInstallerOptions.ForceApplicationShutdown または AddPackageOptions.ForceTargetAppShutdown を渡すと、シャットダウン + 更新後にアプリを再起動するようにスケジュールされます。 UWP 以外のアプリの場合は、更新プログラムを適用する前に RegisterApplicationRestart を呼び出す必要があります。

アプリがシャットダウンを開始する前に、RegisterApplicationRestart を呼び出す必要があります。 下に示すのは、相互運用サービスを使用して C# でネイティブ メソッドを呼び出す例です。

 // Register the active instance of an application for restart in your Update method
 uint res = RelaunchHelper.RegisterApplicationRestart(null, RelaunchHelper.RestartFlags.NONE);

C# でネイティブの RegisterApplicationRestart メソッドを呼び出すヘルパー クラスの例です。

using System;
using System.Runtime.InteropServices;

namespace MyEmployees.Helpers
{
    class RelaunchHelper
    {
        #region Restart Manager Methods
        /// <summary>
        /// Registers the active instance of an application for restart.
        /// </summary>
        /// <param name="pwzCommandLine">
        /// A pointer to a Unicode string that specifies the command-line arguments for the application when it is restarted.
        /// The maximum size of the command line that you can specify is RESTART_MAX_CMD_LINE characters. Do not include the name of the executable
        /// in the command line; this function adds it for you.
        /// If this parameter is NULL or an empty string, the previously registered command line is removed. If the argument contains spaces,
        /// use quotes around the argument.
        /// </param>
        /// <param name="dwFlags">One of the options specified in RestartFlags</param>
        /// <returns>
        /// This function returns S_OK on success or one of the following error codes:
        /// E_FAIL for internal error.
        /// E_INVALIDARG if rhe specified command line is too long.
        /// </returns>
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
        internal static extern uint RegisterApplicationRestart(string pwzCommandLine, RestartFlags dwFlags);
        #endregion Restart Manager Methods

        #region Restart Manager Enums
        /// <summary>
        /// Flags for the RegisterApplicationRestart function
        /// </summary>
        [Flags]
        internal enum RestartFlags
        {
            /// <summary>None of the options below.</summary>
            NONE = 0,

            /// <summary>Do not restart the process if it terminates due to an unhandled exception.</summary>
            RESTART_NO_CRASH = 1,
            /// <summary>Do not restart the process if it terminates due to the application not responding.</summary>
            RESTART_NO_HANG = 2,
            /// <summary>Do not restart the process if it terminates due to the installation of an update.</summary>
            RESTART_NO_PATCH = 4,
            /// <summary>Do not restart the process if the computer is restarted as the result of an update.</summary>
            RESTART_NO_REBOOT = 8
        }
        #endregion Restart Manager Enums

    }
}