[Delphi] Funcionalidade de Atualização Automática – Parte 3

[Delphi] Funcionalidade de Atualização Automática - Parte 3Leitores, vamos fechar a série de artigos sobre atualização automática? Vamos!
A terceira e última parte dessa série traz algumas observações sobre as codificações do artigo anterior, com o objetivo de esclarecer dúvidas que talvez tenham surgido e também para explicar os recursos que utilizei no tutorial. Recomendo a leitura desse artigo!

 

Enquanto escrevia os dois primeiros artigos da série, procurei prever possíveis dúvidas e dificuldades dos leitores. O resultado foi a lista de observações abaixo, cada uma com sua respectiva justificativa. Mesmo assim, sintam-se à vontade para utilizar o formulário de comentário no final da página para questionar ou complementar algo, ok?

 

1) Arquivo INI
Para manter o aspecto didático no artigo, optei pela utilização de arquivos INI devido à simplicidade de manipulação, mas isso não é uma regra! A informação da versão pode ser armazenada de várias formas, como uma tabela no banco de dados, um arquivo XML ou até mesmo um documento na nuvem. Além disso, a informação da versão também pode ser obtida nas propriedades do executável através da seguinte função:

function TForm1.ExtrairVersaoAplicacao: string;
var
  nTamanhoInfo, nTamanhoValor, Handle: Cardinal;
  pInfoVersao: Pointer;
  pValorVersao: PVSFixedFileInfo;
begin
  nTamanhoInfo := GetFileVersionInfoSize(PChar(Application.ExeName), Handle);
  GetMem(pInfoVersao, nTamanhoInfo);
  try
    GetFileVersionInfo(PChar(Application.ExeName), 0, nTamanhoInfo, pInfoVersao);
    VerQueryValue(pInfoVersao, '\', Pointer(pValorVersao), nTamanhoValor);
 
    result := Format('%d.%d.%d.%d', [
      HiWord(pValorVersao^.dwFileVersionMS),
      LoWord(pValorVersao^.dwFileVersionMS),
      HiWord(pValorVersao^.dwFileVersionLS),
      LoWord(pValorVersao^.dwFileVersionLS)]);
  finally
    FreeMem(pInfoVersao, nTamanhoInfo);
  end;
end;

 

2) Clean Code
Alguns métodos do artigo anterior podem ser melhorados ao aplicar as práticas de Clean Code. Por exemplo, os métodos que consultam a versão local e a versão do FTP são bastante semelhantes e podem ser unificados, recebendo um parâmetro para definir qual arquivo será lido. Do mesmo modo, as instruções que excluem os arquivos existentes também podem ser refatorados para um novo método definido como “ExcluirArquivo”. Porém, mais uma vez, mantive a implementação bem detalhada para facilitar a compreensão.
Além disso, os nomes dos componentes também devem ser adequados, como a alteração do nome do componente TProgressBar de “ProgressBar1” para “pbProgressoDownload”.

 

3) Validação da conexão com a internet
A rotina de atualização pode apresentar algumas exceções caso o computador esteja desconectado da internet. Para garantir a confiabilidade, podemos implementar uma função que verifica se há uma conexão válida e utilizá-la em uma condição IF antes da tentativa de acesso ao FTP:

uses
  WinInet;
 
{ declaração }
function VerificarExisteConexaoComInternet: boolean;
 
{ implementação }
function TForm1.VerificarExisteConexaoComInternet: boolean;
var
  nFlags: Cardinal;
begin
  result := InternetGetConnectedState(@nFlags, 0);
end;
 
{ utilização (método ConectarAoServidorFTP) }
if not VerificarExisteConexaoComInternet then
  Exit;

 

4) Exibir a quantidade de KBytes baixados
Lembram-se que implementamos a porcentagem do download no evento OnWork do componente TIdFTP? Pois bem, no mesmo evento, podemos exibir também a quantidade de KBytes que estão sendo baixados em tempo real:

var
  nTamanhoTotal, nTransmitidos: real;
begin
  // obtém o tamanho total do arquivo em bytes
  nTamanhoTotal := FnTamanhoTotal div 1024;
 
  // obtém a quantidade de bytes já baixados
  nTransmitidos := AWorkCount div 1024;
 
  // atualiza o componente TLabel com a quantidade de KBytes
  Label1.Caption := Format('%s KB de %s KB',
    [FormatFloat('##,###,##0', nTransmitidos), FormatFloat('##,###,##0', nTamanhoTotal)]);
end;

 

5) Reiniciar a aplicação após a atualização
Essa é uma boa ideia, hein? Afinal, a atualização só entrará em vigor quando o sistema for fechado e iniciado novamente. Para executar essa ação, basta implementar os comandos abaixo quando a atualização for finalizada:

ShowMessage('Atualização concluída com sucesso. A aplicação será reiniciada.');
ShellExecute(Handle, nil, PChar(Application.ExeName), '', nil, SW_SHOWNORMAL);
Application.Terminate;

 

6) Executar script
Muitas vezes, só atualizar o executável não é o suficiente. É necessário executar também alguns scripts no banco de dados como parte da atualização. No entanto, essa ação depende de qual banco de dados está sendo utilizado no projeto. Para Firebird, por exemplo, pode-se utilizar o utilitário isql para executar scripts via linha de comando, assim como foi feito com o 7-Zip no artigo anterior. Veja um exemplo da sintaxe:

isql.exe C:\Aplicativo\Banco.fdb -m -b -i C:\Atualizacao\Script.sql -q -u SYSDBA -p masterkey

Opcionalmente, o desenvolvedor também pode criar um aplicativo exclusivo para essa finalidade, executado durante a atualização.
Se este procedimento for necessário, entre em contato comigo. Talvez eu possa ajudá-lo!

 

7) Copiar arquivos adicionais
Recebi essa dúvida na semana passada do leitor Marcos Vinicius (obrigado pela questão, Marcos!).
A cópia de arquivos adicionais, como uma DLL, por exemplo, pode ser feita por meio de um arquivo de lotes (extensão BAT), contendo comandos do MS-DOS, como no exemplo a seguir:

Copy C:\Atualizacao\Biblioteca.dll C:\Aplicativo

A rotina de atualização, por sua vez, deve executar esse arquivo com a função ShellExecute:

ShellExecute(Application.Handle, 'open', 'cmd',
  PChar(' /c ' + 'C:\Atualizacao\Atualizacao.bat'), nil, SW_NORMAL);

 

8) Log de atualização
Procure registrar o processo de atualização em algum local, como um arquivo de texto, contendo os seguintes dados:

  • Data e hora da atualização
  • Tamanho do pacote de atualização
  • Usuário conectado
  • Versão que está sendo atualizada
  • Resultados do(s) script(s)

Essas informações podem ser relevantes para atividades de manutenção, estatísticas (quantidade de atualizações em período) ou simplesmente para controle interno. Para complementar, este log pode ser enviado automaticamente ao desenvolvedor por e-mail após o término da atualização.

 

Bom, pessoal, aqui encerro a série de artigos sobre atualização automática.
Dúvidas? Deixe um comentário!

Na próxima semana comentaremos sobre Engenharia de Valor!
Abraços!


Confira as outras partes desse artigo:

[Delphi] Funcionalidade de Atualização Automática – Parte 1
[Delphi] Funcionalidade de Atualização Automática – Parte 2
[Delphi] Funcionalidade de Atualização Automática – Parte 3


 

Compartilhe!
Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Pin on PinterestEmail this to someone

70 comentários

  1. Fala Batera, tudo joia?
    Acompanhei os 3 posts sobre atualização automática, muito bom. Esse tema sempre rende muitas dúvidas. Referente a execução de scripts no banco firebird, o que você aconselharia, utilizar o isql ou um aplicativo externo? Abraço.

    1. Opa, Ivan, quanto tempo! Tudo certo?
      Bom, eu optaria pelo isql utilizando o parâmetro “-o” para gerar um arquivo de log, como no exemplo abaixo:

      isql Banco.fdb -m -b -i Script.sql -q -u SYSDBA
      -p masterkey -o ArquivoLog.txt

      Dessa forma, após a execução do script, é possível ler o arquivo de log e verificar se houve algum erro. Outra alternativa é utilizar o CreateProcess para exibir o resultado das instruções do script na tela do usuário. Se você precisar de um exemplo, é só falar!

      Abraço!

  2. Olá André, minha dúvida é, você poderia da um exemplo de como faria para executar os scripts no banco enquanto atualizo a aplicação?

    1. Olá, Allan, tudo certo?
      Bom, conforme mencionado no artigo, a forma de executar scripts depende do banco de dados utilizado.
      Para Firebird, por exemplo, basta utilizar o utilitário isql, informando os seguintes parâmetros:

      isql.exe C:\Aplicativo\Banco.fdb -m -b -i
        C:\Atualizacao\Script.sql -q -u SYSDBA -p masterkey

      Para executar este comando, pode-se utilizar o ShellExecute.

  3. Olá André

    Tudo bem?
    Gostei muito do seu post!

    No entanto, acompanhei todas as etapas, mas quando executei deu o seguinte erro:

    Can’t open atualizacao/VersaoFTP.ini: No such file or directory

    Mas a versão está no servidor.
    O que pode estar acontecendo?

    Inclusive, baixei o seu codigo fonte e alterei os parametros, mas ocorreu o mesmo erro.

    Desde de já agradeço

    Anselmo Lima

    1. Olá, Anselmo, tudo bem?
      Bom, o erro informado indica que o arquivo “VersaoFTP.ini” não existe dentro da pasta “atualizacao” no servidor FTP. Experimente seguir as orientações abaixo:
      1) Verifique se a pasta “atualizacao” está na raiz do diretório FTP;
      2) Verifique se o arquivo “VersaoFTP.ini” está com o nome correto, inclusive a extensão;
      3) Acesse o servidor FTP pelo Filezilla ou pela Web e certifique-se de que o arquivo se encontra na pasta.

      Se tudo estiver correto e o erro ainda persistir, volte a avisar!
      Abraço!

  4. Gostaria de saber como colocar uma verificação dos arquivos, no caso ‘VersãoLocal’, por meio do MD5. E verificar também o executável.

  5. Bom dia professor!
    O seu código está baixando apenas um arquivo.zip, como fazer para baixar vários arquivos.zip de uma única vez?

    Tipo:
    atualizacao1.zip
    atualizacao2.zip
    atualizacao3.zip

    E assim por diante… Para baixar todos os arquivos com extensão .zip.

    1. Olá, Eltomarle!
      Para baixar outros arquivos do FTP, basta utilizar o comando

      IdFTP.Get()

      conforme no exemplo abaixo:

      IdFTP1.Get('Arquivo1.zip', C:\Arquivo1.zip', True, True);
      IdFTP1.Get('Arquivo2.zip', C:\Arquivo2.zip', True, True);
      IdFTP1.Get('Arquivo3.zip', C:\Arquivo3.zip', True, True);

      Abraço!

  6. Boa tarde.
    Bom professor, obrigado por me responder, mas o que eu não queria era justamente ter que ficar incrementando linhas no código, por que tipo: Se eu tiver 200 arquivos(isso e só um exemplo), ai terei que fazer 200 linhas de comando isso acaba sendo inviálvel.

    Eu estava pensando mais em algo do tipo:
    IdFTP1.Get( ‘*.zip’, ‘C://*.zip’, True, True);

    mais sei que infelizmente o caracter especial ‘*’ não e aceitado pelo GET. Então a minha duvida e fazer justamente isso mais sem precisar estar repetindo linhas de codigo.

    1. Tem razão, Eltomarle. O comando Get não aceita curingas (ou wildcards). Uma alternativa é varrer o diretório FTP (usando o TIdFTP.List) e jogar em uma lista. Após isso, usar o Get em todos os itens da lista para baixar os arquivos.

      Por exemplo, suponha que você queira baixar os seguintes arquivos:
      – Biblioteca.dll
      – Pacote.zip
      – Texto.txt

      Em primeiro lugar, use o comando List para preencher uma lista com o nome desses 3 arquivos.
      Em seguida, faça um loop nessa lista e chame o método Get para baixá-los.

      Acredito que essa forma seja funcional.
      Abraço!

  7. Mais uma vez agradeço do fundo do meu coração, pois estou quase um mês tentando fazer isso, baixar vários arquivos. Mas sou novo no mundo maravilhoso da programação!!!
    Eu até entendi a sua explicação, mas o problema é que já tentei de todas as formas mas não consegui, e não sei como colocar em pratica o que o senhor explicou logo a cima. Se o senhor poder me ajudar na codificação eu lhe serei grato eternamente.

  8. Primeiramente muito bom o passo a passo.
    Segundamente hehe, quando faço executar o isql.exe passando os parametros, ele executa o sql tudo certinho, porém o arquivo de log fica em branco.
    Pode me dar uma ajuda por email?
    Grato

  9. Olá André, gostaria de agradecer por colaborar com toda a comunidade Delphi com seu blog e com artigos que tendem a ajudar muita gente (inclusive eu rsrsrs).
    Fiz todo o procedimento de update e funcionou quase tudo. Fiquei na mesma dúvida que o amigo Kelvin ali em cima.
    A atualização ocorre normalmente, o ShellExecute roda o aplicativo, porém o log fica em branco e não executa o arquivo sql.
    A rotina do arquivo sql adiciona dois campos em uma tabela, porém isso não ocorre.
    Poderia dar um help fazendo um favor?
    Muito Obrigado!

  10. Ola andre estou com problema para entender o comando para descompactar
    ShellExecute(0, nil, ‘7z’, PWideChar(‘ e -aoa ‘ +
    sNomeArquivoAtualizacao +’ -o’ + ObterDiretorioDoExecutavel), ”, SW_SHOW); o que seguinifica esse 0, e esse e -aoa ,-o ? Pois aqui nao esta funcionando tenho a dll 7z mais nao funciona.

    1. Olá, Alexandre!
      Os comandos do 7z realmnete são um pouco confusos. Tive que ler a documentação para aprender a usá-los.
      Na documentação diz o seguinte:

      Switch -aoa:
      This switch overwrites all destination files.
      Use it when the new versions are preferred.
      Switch o:
      Use this to set the destination directory.

      Pode parecer estranho, mas o “-o” deve vir junto com o nome do diretório, sem espaços.
      Na prática, o comando ficaria dessa forma:

      7z e -aoa Atualizacao.rar -oC:\Aplicativo

      Onde:
      “7z” é o nome do descompactador;
      “e” comando de extração de arquivos;
      “-aoa” substui os arquivos no destino, caso existam;
      “-o” indica o diretório em que os arquivos serão descompactados.

      Mais informações aqui:
      http://www.dotnetperls.com/7-zip-examples

      Abraço!

  11. Caro André, obrigado pela explicação do 7z. Até ai entendi, mas meu problema continua, ate agora consigo baixar o Atualizacao.rar sendo que não consigo extrair. Dentro do meu Atualizacao.rar tem uma pasta com arquivos xlsm. Preciso que o arquivo rar seja descompactado substituindo a pasta com os arquivos xlsm.

    Agradeço pela ajuda!!!

    1. Olá, Alexandre!
      Ainda continuo desconfiando que é um erro de sintaxe do comando do 7z.
      Faça o seguinte: execute este comando pelo prompt do DOS. Se estiver ocorrendo um erro, o próprio console irá exibi-lo.
      Se não souber como trabalhar com o DOS, entre em contato pelo e-mail “contato@andrecelestino.com”.

      Abraço!

  12. Boa tarde!!
    e tentando e aprendendo!! esta tudo funcionando menos a descompactação.

    procedure TForm1.DescompactarAtualizacao;
    var
    sNomeArquivoAtualizacao: string;
    begin
    // deleta o backup anterior, ou melhor, da versão anterior,
    // evitando erro de arquivo já existente
    if FileExists(ObterDiretorioDoExecutavel + ‘prestringtable_Backup’) then
    DeleteFile(ObterDiretorioDoExecutavel + ‘prestringtable_Backup’);

    // Renomeia o executável atual (desatualizado) para “Sistema_Backup.exe”
    RenameFile(ObterDiretorioDoExecutavel + ‘prestringtable’,
    ObterDiretorioDoExecutavel + ‘prestringtable_Backup’);

    // armazena o nome do arquivo de atualização em uma variável
    sNomeArquivoAtualizacao := ObterDiretorioDoExecutavel + ‘Atualizacao.rar’;

    // executa a linha de comando do 7-Zip para descompactar o arquivo baixado
    ShellExecute(0, nil, ‘7z’, PWideChar(‘ e -aoa ‘ +
    sNomeArquivoAtualizacao +’ -o’ + ObterDiretorioDoExecutavel), ”, SW_SHOW);
    end;

    esse e o codigo mais continua nao descompactando

  13. Caro Andre me desculpe pela insistência!!, sim pelo dos funciona o comando 7z e -aoa Atualizacao -oprestringtable
    sendo que ele extrai errado .

    Extracting prestringtable\ru\LanguageData.xlsm
    Extracting prestringtable\ru\stringtable_cutscene_ru.xlsm
    Extracting prestringtable\ru\stringtable_ru.xlsm
    Extracting prestringtable\ru\symbolnostringtable_ru.xlsm
    Extracting prestringtable\ru <= essa linha não era para sair
    Extracting prestringtable <= essa linha não era para sair

    isso tudo pelo dos!!!

    Muito obrigado pela atenção.

  14. Primeiramente parabéns por disponibilizar tão rico e útil conhecimento! Bom, como poderíamos implementar uma forma de informar ao usuário de que existe uma versão nova? A exemplo do CCleanner que exibe uma janelinha no canto inferior do desktop quando de nova versão disponível.

    1. Olá, Sávio, tudo bem?

      Desculpe-me pela demora.
      Na parte 2 da série de artigos sobre atualização monetária, há 2 métodos: ObterNumeroVersaoLocal e ObterNumeroVersaoFTP. Poderíamos utilizá-los para notificar o usuário de que há uma versão nova disponível.
      Por exemplo, a aplicação, ao ser iniciada, compara a versão local com a versão que está no FTP. Se a versão do FTP for maior, significa que uma nova versão está disponível, então apresentaríamos uma mensagem, notificação ou um link (como o CCleaner) para alertá-lo da atualização.

      Esse cenário se encaixa mais em uma atualização manual do que automática, já que o próprio usuário teria que tomar a ação de atualizar o software. Se as alterações forem performáticas (envolvendo apenas usabilidade e/ou performance), então não vejo problemas em atualizações manuais. Porém, se forem alterações importantes, que corrijam erros ou tragam novas funcionalidades importantes para o usuário final, então a atualização automática é mais recomendada.

      Abraço!

  15. Bom dia, André!
    Parabéns por sua iniciativa e por sua magnífica colaboração. Parabéns!
    Professor, vou lhe fazer um pedido: Você não poderia disponibilizar este sistema, para que apenas alterássemos o que fosse necessário e pudéssemos incrementá-lo ao nosso sistema? Digo isto, não por comodismo, mas pelas dificuldades que, pessoas como eu, apaixonadas por Delphi mas praticamente leigas nesta e em todas as linguagens de programação, pudessem fazer uso deste recurso, tão útil.
    André, tenho 67 anos de idade e desfruto de alguns problemas que a idade carrega. Mas minha paixão por esta maravilha, chamada Delphi, não consegue terminar.
    Você é uma pessoa extremamente admirável e de uma grandeza imensurável.
    Mais uma vez, parabéns.
    Abraço

    1. Olá, Fábio, como vai?

      Devo dizer que fiquei sem palavras com o seu comentário! Primeiro, pelos seus elogios, que me motivam a continuar contribuindo para a comunidade de programadores Delphi com o blog. Segundo, pela satisfação em encontrar mais uma pessoa que é tão apaixonada pelo Delphi como eu sou. Fico muito feliz em notar este artigo foi de grande utilidade!
      Fábio, idade não significa nada! O que importa é a sua vontade de aprender e, antes que eu me esqueça, a sua capacidade admirável de escrita. Acho que é um dos melhores (senão o melhor) comentários que já recebi no blog. Muito obrigado!

      Bom, respondendo a sua pergunta, o módulo de atualização automática que tenho hoje é parte de um sistema de gestão de pedidos para representantes comerciais, composto por vários outros módulos. Pensei em disponibilizá-lo neste artigo, mas o arquivo ficaria muito grande, portanto, decidi elaborar um exemplo exclusivo com essa implementação, disponível neste link:

      http://www.andrecelestino.com/wp-content/files/AtualizacaoAutomatica-AndreCelestino.rar

      O exemplo tem toda a parte abordada nas 3 partes do artigo sobre essa funcionalidade. Para adaptá-lo a um sistema, basta somente alterar as propriedades do componente TIdFTP (configurando o nome, usuário e senha do servidor utilizado) e o nome dos arquivos.
      Mesmo assim, Fábio, estarei à disposição para qualquer dúvida!

      Um grande abraço!

  16. Boa noite André.

    Já faz tempo que eu quero falar sobre o quanto este artigo foi útil para mim, mas não foi na parte de atualização ainda, foi no licenciamento do software, com certeza também implementei a atualização, tudo funciona direitinho.
    Pode acreditar toda vez que eu instalo um software novo ou quando visito um cliente e vejo programa abrindo e verificando tudo que eu preciso, eu lembro que isso foi possível a partir deste artigos que você generosamente disponibilizou para nós.
    Eu implementei varias funções desde a liberação e o gerenciamento das licenças de uso que vendo até a dos distribuidores (tinha um mas parei por enquanto).
    Muito obrigado, não só pro essa aproveitei muitas dicas e orientações.

    Um abraço.

    1. Olá, Gerson!
      Suas palavras foram muito gratificantes e me motivam a continuar o trabalho no blog!
      Como você deve ter notado, ando meio ausente (quase 2 meses sem publicações), mas pretendo retornar em breve.
      Agradeço muito por ter deixado este comentário e também fico feliz em saber que os artigos lhe ajudaram.
      Desejo boa sorte nos seus projetos!

      Grande abraço!

  17. Parabéns André, procuro por algo assim há mais de um ano e nunca encontro, e os que encontro, geralmente não tem explicação de como funciona ou o nível “dos cara” é altíssimo, dificultando muito o entendimento de nós programadores mais leigos. Muitíssimo obrigado mesmo, você com toda certeza ajudou muito mais gente na mesma situação que eu, e não mediu esforços para responder aos comentários, é de gente assim, humilde e de bom coração que o mundo precisa, gente que não se gaba do seu intelecto e ajuda o próximo. Mais uma vez muitíssimo obrigado, que Deus lhe abençoe. Abraço.

    1. Boa noite, Diemes, tudo bem?
      Nem sei o que dizer sobre o seu comentário! Agradeço de coração pelas palavras. São pessoas como você que me motivam a continuar o trabalho no blog. Sinto que o meu objetivo em compartilhar o conhecimento está sendo cumprido! Muito obrigado, Diemes!

      Aproveitando, uma novidade para você: em breve pretendo escrever uma nova “versão” dos artigos sobre Atualização Automática. Recebi algumas sugestões e dicas bem interessantes de alguns leitores. Eu estimo que ficará mais fácil.

      Grande abraço!

  18. Bom dia, André.
    Primeiramente quero lhe agradecer por ter me ajudado e ajudado muitos programadores.
    Quero tirar uma duvida. Eu quero extrair todos os arquivos que estão dentro do arquivo Atualizacao.rar.
    Quem poder me Ajudar, agradeço.

  19. Olá, André!!

    Primeiramente meus parabéns , foi o melhor artigo que achei sobre atualização de sistemas. Sou programador desde o delphi 4, e nunca tinha criado nada para automatizar (é aquele negócio que sempre tem algo mais importante e você deixa para depois hehehe)
    Na verdade vejo como a maior dificuldade justamente a gestão dos scripts do banco de dados para atualizar cada cliente. Pelo que você mostrou, é simples fazer a chamada pelo isql (também uso Firebird), porém o script que deve ser rodado é que é o problema. Como você fez ou faria isso?
    Uma vontade que tive mas não consegui achar, foi utilizar alguma api automatica de alguma ferramenta de comparação. Com isso, teria um banco mestre vazio, e a ferramenta faria a comparação independente da versão do banco do meu cliente. Não sei se fui claro na explicação, se puder dar alguma luz, agradeço.
    Abraço!!

    1. Boa noite, Fernando, tudo bem?
      Ótima pergunta! A execução de scripts em atualizações automáticas realmente é algo que deve ser bem administrada.
      Uma alternativa bastante viável é armazenar o nome dos scripts que já foram rodados em alguma tabela de controle no banco de dados. Por exemplo, suponha que os scripts A, B e C estão no pacote de atualização. O cliente 1 tem os scripts A e B executados. Já o cliente 2 tem somente o script A, talvez por estar utilizando uma versão mais antiga do sistema. Na atualização automática, basta verificar os scripts que já foram executados (por meio de uma consulta nessa tabela) e rodar apenas os que faltam. Neste caso, o script C seria executado no cliente 1 e os scripts B e C no cliente 2. Em seguida, após executá-los, o próprio atualizador pode incluir os nomes na tabela de controle.

      Na verdade, essa é a lógica que utilizamos no sistema em que eu trabalho atualmente.

      Obrigado pelo comentário.
      Abraço!

  20. Boa noite André.
    No artigo você mencionou a dificuldade do upload do arquivo .exe no servidor ftp, seria desvantagem ter um servidor ftp próprio(residencial) para disponibilizar os arquivos para atualização?

    1. Boa tarde, Rafael, tudo bem?
      Desculpe-me pela demora. Acabei de chegar de uma viagem a trabalho.
      Rafael, ter um servidor FTP residencial é uma ótima opção, porém, em vista do custo dispendido, eu particularmente optaria por um servidor FTP pago. Dessa forma, o desenvolvedor não teria que se preocupar com a disponibilidade e desempenho do servidor, já que isso seria feito pela empresa contratada.

      Abraço!

  21. Boa Tarde Andre, muito bom, muito útil, muito didático esse tutorial. Parabéns.
    Tenho uma questão, como crio esse log de atualização que você mencionou??
    Obrigado.
    Att, Felipe.

    1. Boa noite, Felipe!
      O log de atualização pode ser criado com uma variável do tipo TextFile.
      A grosso modo, seria dessa forma:

      var
        Arquivo: TextFile;
        sCaminhoArquivo: string;
      begin
        // indica o caminho do arquivo de log
        sCaminhoArquivo := 'C:\Aplicativo\LogAtualizacao.txt';
        
        // aponta a variável TextFile para o arquivo
        AssignFile(Arquivo, sCaminhoArquivo);
        
        // se o arquivo não existir, é criado
        if not FileExists(sCaminhoArquivo) then
          Rewrite(Arquivo)
        // senão, ele é apenas aberto
        else
          Append(Arquivo);
          
        // escreve o texto no arquivo  
        WriteLn(Arquivo, 'Teste de log');
      
        // fecha o arquivo  
        CloseFile(Arquivo);
      end;

      No artigo sobre Singleton há um projeto de exemplo no qual faço uso de uma variável desse tipo:

      http://www.andrecelestino.com/delphi-design-patterns-singleton/

      Abraço!

    2. Olá, Felipe.
      Depende de qual informação você deseja escrever no log.
      No projeto pessoal que eu trabalho, por exemplo, adicionei as seguintes informações no arquivo de log:
      – Data e hora da atualização;
      – Nome do usuário que está conectado no sistema;
      – Número da versão atual do cliente;
      – Número da versão que será atualizada;
      – Resultado da execução do script no banco de dados;
      – Erros durante a atualização, caso existam.

      Portanto, distribui o código de gravação do log (WriteLn) em vários métodos diferentes.

      Abraço!

  22. Boa Noite André, obrigado pela atenção e respostas.
    Poderia ser exatamente como disse no comentário anterior, mas como sou bem iniciante e um entusiasta em Delphi e em pequenas e constantes evoluções, não sei como proceder com o tal nesse projeto, onde coloco, o que colocar, ficaria imensamente que me ajudasse mais uma vez.
    Obrigado.
    Att, Felipe.

    1. Certo, Felipe.
      Bom, em primeiro lugar, você precisa ter o projeto já desenvolvido para adicionarmos essa funcionalidade de criação de log.
      A partir daí, envie um e-mail para “contato@andrecelestino.com” para continuarmos conversando, ok?

      Abraço!

  23. Olá André, estou com o mesmo problema do Alexandre em relação a descompactar o arquivo baixado. Procurei algumas informações sobre a utilização do 7z com o Delphi, porém sem exito. Poderia me auxiliar nesse detalhe ?

  24. Olá André, estou tentando lhe responder no e-mail, mas seu provedor esta recusando o envio. Segue abaixo o que foi solicitado:
    1) Delphi xe6.
    2) Windows 8.1
    3) O executável e a DLL estão na pasta junto ao executável do projeto corretamente.

    * Delphi, executável do projeto e 7z.exe estão estão sendo executado como administrador.
    Obrigado pela ajuda.

  25. Olá André, passando para agradecer seu excelente post e deixar uma dica para galera!

    Fiz umas alterações e coloquei seu “atualizador” em produção para meus sistemas:
    – Atualização do .EXE
    – Envio de DLLs
    – Envio de layout de relatórios
    – Envio da Tabela do IBPT .CSV
    – Atualização de banco de dados

    Na questão do banco de dados, apesar de usar o Firebird instalado pelo meu próprio instalador, fiquei com receio em usar a ferramenta “isql”.
    Dei preferência em usar a combinação de componentes IBDataBase + IBScript, com isso mando um “script.sql” com todas as instruções e carrego no IBScript, depois é só mandar executar.

    Enfim, ficou bem tranquilo mesmo as atualizações, parabéns mais uma vez!

    1. Olá, William, como vai?
      Opa, fico feliz em saber que você conseguiu criar um atualizador que trabalha com vários arquivos! 🙂
      Muito obrigado pela sua colaboração! Tenho recebido algumas dúvidas sobre a execução de scripts na atualização automática e a sua dica será de grande valia! Na verdade, em um futuro breve pretendo “reescrever” essa série de artigos com algumas melhorias, e a execução de scripts está entre elas.

      Grande abraço!

    2. Parabéns pelo Autor e Site.
      Um post muito importante para qualquer aplicação.
      Com relação aos script, uso assim no meu ‘atualizador’ usando as dicas deste post.

      É simples mas resolve….

      /////FUNCAO PARA EXECUÇÃO DO SCRIPT
      function RunScript(aSQLConnection: TSQLConnection; aFileName: String): Boolean;
      var
        TD: TTransactionDesc;
        Arquivo: TextFile;
        Linha, StrTemp: String;
        Scripts: TStringList;
        i: integer;
      begin
        Result:= False;
        if not FileExists(aFileName) then Exit;
        
        Scripts:= TStringList.Create;
        StrTemp:= '';
        AssignFile(Arquivo, aFileName);
        Reset(Arquivo);
        while not Eof(Arquivo) do
        begin
          ReadLn(Arquivo, Linha);
          StrTemp:= StrTemp + Linha;
          if Pos(';', StrTemp) > 0 then
          begin
            Scripts.Add(StrTemp);
            StrTemp:= '';
          end
          else
            StrTemp:= StrTemp + ' ';
        end;
          CloseFile(Arquivo);
        
        TD.TransactionID:= 1;
        TD.IsolationLevel:= xilREADCOMMITTED;
        try
          if not aSQLConnection.Connected then
            aSQLConnection.Connected:= True;
        
          for i := 0 to (Scripts.Count – 1) do
          begin
            try
              aSQLConnection.StartTransaction(TD);
              aSQLConnection.ExecuteDirect(Scripts[i]);
              aSQLConnection.Commit(TD);
            except
              on E: Exception do
              begin
                aSQLConnection.Rollback(TD);
                ShowMessage(e.Message);
                Exit;
              end;
            end;
          end;
        except
          on E: Exception do
          begin
            ShowMessage(e.Message);
            Exit;
          end;
        end;
        Result:= True;
      end;
      
      ////// PROCEDIMENTO PARA EXECUTAR
      begin
         //SQLConnection1 vinculado a conexao da base
        if RunScript(SQLConnection1, 'C:\atualizacoes.sql') then
          ShowMessage(‘Atualização realizada com sucesso!’);
      end;
    3. Olá, Francisco, tudo bem?

      Muito obrigado pela sua contribuição.
      Há muitos desenvolvedores procurando orientações sobre como executar scripts dentro do atualizador. A sua dica irá ajudá-los!

      Grande abraço!

  26. Excelente!
    Um dos melhores artigos que já li. Parabéns!!
    Foi essencial para o que eu estava pensando em desenvolver.

  27. O melhor artigo que encontrei na internet, estou com duvida na questão ainda dos scripts de banco, estou estudando para criar ainda, mais desde já agradeço pelo tutorial excelente.

    1. Olá, William!
      Obrigado pelo feedback sobre o artigo!
      Em breve farei um “remake” dessa série de artigos com algumas melhorias, entre elas, uma orientação sobre a execução de scripts.

      Abraço!

  28. Amigo, boa tarde !
    Não consegui fazer o isql funcionar, será que poderia me ajudar?
    1)Criei um arquivo TESTE.BAT
    isql.exe C:\Banco\BANCO.GDB -i script.ql -u SYSDBA -p masterkey -o ArquivoLog.txt

    2)Criei um arquivo script.qsl
    UPDATE STATUS SET PCTRIB = 10;

    Executo o TESTE.BAT, não gera erro, mas também não atualiza o campo do scritp.

    Já coloquei um COMMIT no arquivo 1, no arquivo 2, e nem assim atualiza.

    Poderia me ajudar por favor?

    Obrigado

    1. Olá, Márcio, tudo bem?
      Notei que na linha de comando a extensão do arquivo está como “ql” ao invés de “sql”. Será que não pode ser isso?
      Aproveitando, em um dos meus projetos eu uso alguns parâmetros adicionais:

      isql Banco.fdb -m -b -i Script.sql -q -u SYSDBA -p masterkey -o Log.txt

      Experimente também dessa forma. Abraço!

  29. André, boa noite.

    Primeiramente, gostaria de lhe agradecer pelo retorno, muito bacana da sua parte.

    Apenas digitei errado no lhe enviar, o nome do arqui o script está escrito errado.

    Executando o comando que você me disse acima, ocorre a seguinte mensagem de erro:
    “Expected end of statement, encountered EOF”

    Você saberia o porquê desta mensagem?

    1. Olá, Elsimar, tudo bem?
      Infelizmente não tenho exemplos de execução de scripts em bases Microsoft Access. Trabalhei muito pouco com esse SGBD.
      Fiz uma pesquisa rápida e não encontrei uma forma de executar scripts por linha de comando no Access. Neste caso, o próprio atualizador automático deverá conectar-se à base de dados e executar as instruções desejadas através de um componente, como o TADOQuery.

      Abraço!

  30. Bom dia, mtu bom o post… só uma pergunta..

    antes do Get do .rar do ftp, n teria que colocar o IdFTP1.TransferType := ftBinary; ?

    t+

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Preencha o campo abaixo * Time limit is exhausted. Please reload CAPTCHA.