코드에서 게시된 비 스토어 앱 패키지 업데이트

앱을 MSIX로 배송할 때 애플리케이션 업데이트를 프로그래밍 방식으로 시작할 수 있습니다. 스토어 외부에 앱을 배포하는 경우 서버에서 새 버전의 앱이 있는지 확인하고 새 버전을 설치하기만 하면 됩니다. 업데이트를 적용하는 방법은 앱 설치 관리자 파일을 사용하여 앱 패키지를 배포하는지 여부에 따라 달라집니다. 코드에서 업데이트를 적용하려면 앱 패키지에서 packageManagement 기능을 선언해야 합니다. 이는 게시자 간 시나리오에 필요하지만 기능을 선언하지 않고도 자체 앱을 관리해야 합니다.

이 문서에서는 패키지 매니페스트에서 packageManagement 기능을 선언하는 방법과 코드에서 업데이트를 적용하는 방법을 보여 주는 예제를 제공합니다. 첫 번째 섹션에서는 앱 설치 관리자 파일을 사용하는 경우 이 작업을 수행하는 방법을 살펴보고, 두 번째 섹션에서는 앱 설치 관리자 파일을 사용하지 않는 경우 이 작업을 수행하는 방법에 대해 살펴봅니다. 마지막 섹션에서는 업데이트가 적용되면 앱이 다시 시작되도록 하는 방법을 살펴봅니다.

패키지 매니페스트에 PackageManagement 기능 추가

PackageManager API를 사용하려면 앱이 packageManagement에서 packageManagementPackageManager을 선언해야 합니다.

<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();
    }
}

업데이트 적용

업데이트가 사용할 수 있는 것으로 확인되면 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

    }
}