共用方式為


從程式代碼更新非市集已發佈的應用程式套件

當您以 MSIX 形式傳送應用程式時,可以透過程式設計方式啟動應用程式的更新。 如果您將應用程式部署在存放區之外,則只需檢查伺服器上是否有新版應用程式,並安裝新的版本。 套用更新的方式取決於您是否使用應用程式安裝程式檔案部署應用程式套件。 若要從程式代碼套用更新,您的應用程式套件必須宣告 packageManagement 功能。 請注意,這是跨發行者案例的必要專案,但管理您自己的應用程式應該可以運作,而不需要宣告功能。

本文提供範例,示範如何在套件指令清單中宣告 packageManagement 功能,以及如何從程式代碼套用更新。 第一個區段會探討如果您使用應用程式安裝程式檔案,而第二個區段則說明如何在不使用應用程式安裝程式檔案時執行這項操作。 最後一節會探討如何確定您的應用程式在套用更新之後重新啟動。

將 PackageManagement 功能新增至您的套件資訊清單

若要使用 PackageManager API,您的應用程式必須在 套件資訊清單中,宣告 packageManagement 限制的功能

<Package>
...

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

更新使用應用程式安裝程式檔案部署的套件

如果您要使用應用程式安裝程式檔案部署應用程式,您執行的任何程式代碼驅動更新都必須使用 應用程式安裝程式檔案 API。 這麼做可確保您的一般應用程式安裝程式檔案更新會繼續運作。 若要從程式代碼擷取以應用程式安裝程序為基礎的更新,您可以使用 PackageManager.AddPackageByAppInstallerFileAsyncPackageManager.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();
    }
}

注意

targetPackageFileName代表已封裝應用程式的完整名稱。 (範例: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,您的應用程式才能開始關閉。 以下是使用 Interop 服務在 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

    }
}