Aplicação auxiliar de actualização

Introdução

O artigo tem por objectivo ajudar o leitor a compreender o algoritmo base por detrás de uma aplicação auxiliar de actualização. Apenas serão expostos alguns excertos de código relevantes para uma percepção lógica do objectivo final.

A linguagem de programação escolhida para desenvolvimento não é relevante.

Notas

Sempre que haja uma referência de texto a “Aplicação Pai” esta identifica a aplicação principal da qual a aplicação auxiliar está inerente.

Factores a ter em conta

  • Não é guardado nem inserido qualquer tipo de informação confidencial.
  • Os processos são feitos de forma assíncrona permitindo assim uma interacção mais fluída.
  • A .NET Framework utilizada neste artigo é a 3.5 pelo que deverá correr nas versões superiores sem problema.

Resultado final

O artigo dá também a conhecer ao leitor como trabalhar e aceder a diferentes threads sem comprometer a thread principal.

Aplicação de actualização: resultado final

Algoritmo em Fluxograma

Aplicação de actualização: algoritmo em fluxograma

Onde disponibilizo as novas versões?

As novas versões são disponibilizadas numa pasta de FTP com permissões de acesso externas, sendo que o URL neste artigo descrito encontra-se sobre o meu servidor pessoal para usos didácticos.

Como informei anteriormente não é necessário guardar qualquer informação confidencial, o URL é suficiente.

private static string url = "http://stuffpinho.com/portugal-a-programar/revista/artigos/updater/";

Qual a formatação do ficheiro de actualização?

A formatação fica ao critério do leitor sendo necessário, à posterior, a adaptação do código para interpretação dessa mesma formatação.

1.0.3
Nomeficheiro.exe
Nomeficheiro.dll
Etc

Sendo a primeira linha a identificação da versão mais recente e as restantes linhas identificam quais os ficheiros que devem ser transferidos.

Comparação da versão actual com a mais recente

O primeiro objectivo da aplicação, que deve ser interpretado como decisivo, é validar a versão actual da Aplicação Pai que, por norma, se encontra no mesmo sítio da aplicação auxiliar de actualização.

private static string path = Path.GetDirectoryName(Application.ExecutablePath); 

// Input: 1.0.3.0
string version = FileVersionInfo.GetVersionInfo(path + "\\PAP.exe").ProductVersion;

// Input desejado: 1.0.3 (neste artigo)
currentVersion = version.Remove(version.Length - 2);

FileVersionInfo encontra-se sobre o namespace System.Diagnostics e permite-nos ter acesso directo às informações de um ficheiro, seja a versão do mesmo; direitos de autor; empresa de desenvolvimento; etc.

Para identificarmos a versão mais recente é necessário transferir o ficheiro denominado (no meu caso) update.txt, bem como ler a estrutura do mesmo.

Para transferência de ficheiros externos dá-se uso ao namespace System.Net que nos permite para além de receber ficheiros, enviar.

WebClient webClient = new WebClient();
webClient.DownloadFileCompleted += (object sender, AsyncCompletedEventArgs e) => {
   latestVersion = getLatestVersion();
   if (latestVersion == currentVersion) {
      /*
       * A aplicação encontra-se actualizada.
       * 1. Eliminamos o ficheiro 'Update.txt'; 
       * 2. Executamos novamente a Aplicação Pai;
       * 3. Fechamos a aplicação auxiliar;
       */
      return;
   }

   downloadFiles();
};

webClient.DownloadFileAsync(new Uri(url + "update.txt"), path + "\\update.txt");

Transferir os ficheiros da versão mais recente

O método downloadFiles() transfere os ficheiros necessários a bom funcionamento da nova versão.

Mas antes de transferir é necessário ler (do ficheiro .txt transferido anteriormente) quais os ficheiros disponibilizados e necessários.

private string[] getAllFilesToDownload()
{
   return File.ReadAllLines(path + "\\update.txt");
}

private void downloadFiles() 
{
   // Retorna todas as linhas do ficheiro .txt inclusive a versão.
   string[] files = getAllFilesToDownload();
   bool stop = true;
   foreach (string file in files) 
   {
      /*
       * Visto que a primeira linha é a versão da aplicação, não
       * existe nenhum ficheiro para ser transferido, portanto 
       * ignora-se a linha.
       * A solução passa por criar a variável booleana `stop` ou 
       * se o leitor preferir, eliminar a primeira linha. 
       */
      if (stop == true) { stop = false; continue; }
      WebClient webClient = new WebClient();
 
      /*
       * Exemplo de output final:
       * (Download) url + file = “../updater/nomeFicheiro.dll” 
       * (DownloadTo) path + file = “C:\pasta\nomeFicheiro.dll”
       */
      webClient.DownloadFileAsync(new Uri(url + file), path + "\\" + file);

      /*
       * O download é feito através da função DownloadFileAsync()
       * o que implica que, se houverem múltiplos ficheiros a 
       * serem transferidos ao mesmo tempo, poderá causar 
       * uma abrupta falha na aplicação. O ideal será então 
       * verificar, dentro do ciclo, se a transferência 
       * encontra-se concluída e só aí passar para a próxima
       * linha. 
       * 
       * Se o leitor prefeir pode utilizar uma variável 
       * auxiliar em vez de verificar o valor da progressbar
       */
      while (this.progress_bar.Value != 100)
      {
         Application.DoEvents();
      }
   }

   /*
    * A aplicação encontra-se actualizada.
    * 1. Eliminamos o ficheiro ‘Update.txt’; 
    * 2. Executamos novamente a Aplicação Pai;
    * 3. Fechamos a aplicação auxiliar;
    * 4. A aplicação está oficialmente actualizada!
    */
}

Conclusão

Em suma, a aplicação auxiliar é parte integral da Aplicação Pai que possibilita ao cliente final estar sempre a par das actualizações de forma mais rápida e objectiva.