Atualizar por meio do código pacotes de aplicativos publicados fora da Store

Ao enviar seu aplicativo como um MSIX, você pode iniciar de forma programática uma atualização do seu aplicativo. Se você implantar seu aplicativo fora da Store, só precisará verificar se há uma nova versão do seu aplicativo em seu servidor e instalá-la. A maneira como você aplica a atualização depende de você estar implantando o pacote do aplicativo usando um arquivo do Instalador de Aplicativo ou não. Para aplicar atualizações do código, o pacote do aplicativo precisa declarar a capacidade packageManagement. Isso é necessário para cenários entre editores, mas gerenciar o próprio aplicativo deve funcionar sem que seja necessário declarar o recurso.

Este artigo fornece exemplos que demonstram como declarar a capacidade packageManagement no manifesto do pacote e como aplicar uma atualização do código. A primeira seção descreve como fazer isso se você estiver usando o arquivo Instalador de Aplicativo e a segunda seção descreve como fazê-lo sem usar esse arquivo. A última seção descreve como garantir que o aplicativo seja reiniciado após a aplicação de uma atualização.

Adicionar a funcionalidade PackageManagement ao manifesto do pacote

Para usar as APIs PackageManager, o aplicativo deve declarar a funcionalidade restritapackageManagement no manifesto do pacote.

<Package>
...

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

Atualizando pacotes implantados usando um arquivo Instalador de Aplicativo

Se você estiver implantando o aplicativo usando o arquivo Instalador de Aplicativo, as atualizações controladas por código que você executar deverão usar as APIs do arquivo Instalador de Aplicativo. Isso garante que as atualizações regulares do arquivo Instalador de Aplicativo continuarão funcionando. Para inciar uma atualização baseada no Instalador de Aplicativo do código, use PackageManager.AddPackageByAppInstallerFileAsync ou PackageManager.RequestAddPackageByAppInstallerFileAsync. Verifique se uma atualização está disponível usando a API Package.CheckUpdateAvailabilityAsync. Veja abaixo um código de exemplo:

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

Atualizando pacotes implantados sem um arquivo Instalador de Aplicativo

Verificar se há atualizações no servidor

Se você não estiver usando o arquivo Instalador de Aplicativo para implantar o pacote do aplicativo, a primeira etapa será verificar diretamente se uma nova versão do aplicativo está disponível. O exemplo a seguir verifica se a versão do pacote em um servidor é maior que a versão atual do aplicativo (este exemplo refere-se a um servidor de teste para fins de demonstração).

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

Observação

O targetPackageFileName é representante do nome completo do aplicativo empacotado MSIX. (Exemplo: Contoso.HeadTrax_1.0.0.0_x64__PublisherHash)

Aplicar a atualização

Após determinar que uma atualização está disponível, você pode enfileirá-la para download e instalá-la usando a API AddPackageAsync. Isso também deve funcionar para instalar um pacote opcional, desde que o pacote principal já tenha sido instalado no dispositivo. A atualização será aplicada na próxima vez em que seu aplicativo for desligado. Depois que o aplicativo for reiniciado, a nova versão estará disponível para o usuário. Veja abaixo um código de exemplo:


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

Reiniciando o aplicativo automaticamente após uma atualização

Se o aplicativo for um aplicativo UWP, passar AddPackageByAppInstallerOptions.ForceApplicationShutdown OU AddPackageOptions.ForceTargetAppShutdown ao aplicar uma atualização deverá agendar a reinicialização do aplicativo após o desligamento + atualização. Para aplicativos que não são da UWP, você precisa chamar RegisterApplicationRestart antes de aplicar a atualização.

Chame RegisterApplicationRestart antes que o aplicativo comece a ser desligado. Veja abaixo um exemplo de como fazer isso usando serviços de interop para chamar o método nativo em C#:

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

Um exemplo da classe auxiliar para chamar o método RegisterApplicationRestart nativo em C#:

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

    }
}