다음을 통해 공유


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

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

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

패키지 매니페스트에 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();
    }
}

참고 항목

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

    }
}