Leitores, 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 usar 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function TForm1.ExtrairVersaoAplicacao: string; var lTamanhoInfo, lTamanhoValor, Handle: Cardinal; lInfoVersao: Pointer; lValorVersao: PVSFixedFileInfo; begin lTamanhoInfo := GetFileVersionInfoSize(PChar(Application.ExeName), Handle); GetMem(lInfoVersao, lTamanhoInfo); try GetFileVersionInfo(PChar(Application.ExeName), 0, lTamanhoInfo, lInfoVersao); VerQueryValue(lInfoVersao, '\', Pointer(lValorVersao), lTamanhoValor); result := Format('%d.%d.%d.%d', [ HiWord(lValorVersao^.dwFileVersionMS), LoWord(lValorVersao^.dwFileVersionMS), HiWord(lValorVersao^.dwFileVersionLS), LoWord(lValorVersao^.dwFileVersionLS)]); finally FreeMem(lInfoVersao, lTamanhoInfo); 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 ProgressBarDownload
.
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 ou ocorrer uma queda. 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
uses WinInet; { declaração } function VerificarExisteConexaoComInternet: boolean; { implementação } function TForm1.VerificarExisteConexaoComInternet: boolean; var lFlags: Cardinal; begin result := InternetGetConnectedState(@lFlags, 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var lTamanhoTotal, lTransmitidos: real; begin // obtém o tamanho total do arquivo em bytes lTamanhoTotal := FlTamanhoTotal div 1024; // obtém a quantidade de bytes já baixados lTransmitidos := AWorkCount div 1024; // atualiza o componente TLabel com a quantidade de KBytes Label1.Caption := Format('%s KB de %s KB', [FormatFloat('##,###,##0', lTransmitidos), FormatFloat('##,###,##0', lTamanhoTotal)]); 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:
1 2 3 |
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:
1 |
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:
1 |
Copy C:\Atualizacao\Biblioteca.dll C:\Aplicativo |
A rotina de atualização, por sua vez, deve executar esse arquivo com a função ShellExecute
:
1 2 |
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. Abraço!
Confira as outras partes desse artigo:
[Delphi] Atualização Automática – Parte 1
[Delphi] Atualização Automática – Parte 2
[Delphi] Atualização Automática – Parte 3
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.
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:
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!
Olá André, minha dúvida é, você poderia da um exemplo de como faria para executar os scripts no banco enquanto atualizo a aplicação?
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:
Para executar este comando, pode-se utilizar o ShellExecute.
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
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!
Parabens André… mto bom o passo a passo….
Obrigado, Peixoto! Abraço!
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.
Olá, Eltomarle, tudo bem?
Bom, depende do tipo de verificação que você pretende aplicar. Vou entrar em contato com você por e-mail, ok?
Abraço!
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.
Olá, Eltomarle!
Para baixar outros arquivos do FTP, basta utilizar o comando
conforme no exemplo abaixo:
Abraço!
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.
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!
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.
Certo, Eltomarle. Vou tentar ajudá-lo por e-mail.
Obrigado professor, estarei anciosamente no aguardo. 🙂
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
Olá, Kelvin, tudo certo?
Opa, posso ajudá-lo, sim! Abraço!
Parabéns pelo tutorial, muito bom, bem didático e funcional, show de bola.
Opa, muito obrigado, Saulo!
Em breve pretendo atualizar alguns passos desse artigo para torná-lo mais fácil.
Abraço!
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!
Olá, Marlon, tudo bem?
Funcionou quase tudo? Vamos fazer funcionar tudo então, rsrs!
Vou entrar em contato com você. Abraço!
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.
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:
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:
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!
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!!!
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 “[email protected]”.
Abraço!
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
Olá, Alexandre. Você tentou executar o comando do 7-Zip pelo DOS conforme sugeri no comentário anterior? Que erro ocorre?
Abraço!
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.
Alexande, vou entrar em contato por e-mail, ok?
Abraço!
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.
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!
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
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:
https://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!
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.
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!
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.
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!
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.
Olá, Luiz!
Já estamos conversando por e-mail. 🙂
Abraço!
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!!
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!
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?
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!
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.
Boa noite, Felipe!
O log de atualização pode ser criado com uma variável do tipo TextFile.
A grosso modo, seria dessa forma:
No artigo sobre Singleton há um projeto de exemplo no qual faço uso de uma variável desse tipo:
https://www.andrecelestino.com/delphi-design-patterns-singleton/
Abraço!
Onde e quando coloco esse código??
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!
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.
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 “[email protected]” para continuarmos conversando, ok?
Abraço!
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 ?
Claro, posso ajudá-lo, sim, Gabriel.
Vou pedir mais informações por e-mail, ok?
Abraço!
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.
Olá, Gabriel, acho que foi um problema temporário do servidor de e-mail.
Vou entrar em contato com outra conta. Abraço!
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!
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!
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….
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!
Excelente!
Um dos melhores artigos que já li. Parabéns!!
Foi essencial para o que eu estava pensando em desenvolver.
Olá, Diego! Opa, obrigado pelo feedback!
Em breve vou fazer um remake desse artigo com algumas melhorias!
Abraço!
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.
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!
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
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:
Experimente também dessa forma. Abraço!
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?
Boa tarde, tem algum exemplo de executar um script com base Access, se é possível?
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!
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+
Olá, Welson, tudo bem?
Na segunda parte do artigo eu já menciono essa configuração em tempo de projeto.
Abraço!
Excelente artigo. Me ajudou bastante neste meu retorno ao Delphi depois de me aventurar em Java. Parabéns pelo excelente tutorial, mais mastigado impossível…. Parabéns.
Olá, Jonismar!
Agradeço muito pelo comentário! Fico feliz por saber que estou alcançando o meu principal objetivo com o blog, que é compartilhar conhecimento de uma forma descomplicada. 🙂
A respeito dessa série de artigos, pretendo publicar uma atualização em breve, utilizando o protocolo HTTP e as classes nativas do Delphi para compactação e descompactação. Ficará bem melhor!
Abraço!
André, muito top!
Vlw por compartilhar uma ideia magnifica rsrs.
Obrigado, Luiz!
Futuramente pretendo atualizar essa série de artigos para tirar proveito de algumas classes nativas das versões mais recentes do Delphi.
Continue acompanhando!
Abraço!
Bela dica André, parabéns.
Aqui na empresa temos um sistema que faz isso durante a abertura.
Na propria tela de splash, ele verifica se há atualizações, e mostra o progresso e nome dos arquivos durante o download.
Será que seria possivel fazer assim com o seu exemplo?
Boa noite, Renan!
Sim, é dessa forma que faço em um dos meus projetos. Acredito que a tela de inicialização do sistema é o “melhor momento” para executar a atualização automática.
Abração!
Boa tarde, André, tudo bem? Estou com um problema no arquivo “atualizacao.zip”. Eu coloquei um arquivo “Relatorio.rav” e o “Arquivo.Exe” dento dele.
Porém, ao fazer a atualização, ele baixa o arquivo “atualizacao.zip” e extrai somente o “Relatorio.Rav”. E executável não está extraindo.
Segue o código:
Poderia me ajudar por favor? Obrigado.
Olá, Carlos, tudo bem?
Por se tratar de um executável, acredito que o problema da descompactação possa estar relacionado com o serviço de segurança do Windows.
Um teste que você pode fazer é executar este comando do 7-Zip diretamente no DOS (cmd). Se realmente estiver ocorrendo algum problema, o próprio comando vai retornar uma mensagem de erro na janela do DOS. Dessa forma será mais fácil identificar e solucionar o erro.
Lembre-se também que existe a classe TZipFile nativa do Delphi que dispensa o uso de ferramentas de terceiros!
Abraço!
Olá, André. Meus parabéns pelo site e pela matéria. Eu estou tendo um probleminha, não com o código que funciona perfeitamente no meu computador de desenvolvimento que tem uma velocidade boa, mas no cliente onde o sistema é um windows XP. Na hora de checar se há atualização, ele fica quase uns 4 minutos depois da um erro de timeout.
O que devo fazer?
Muito Obrigado.
Olá, Eduardo, tudo bem?
Vou entrar em contato com você por e-mail.
Abraço!
André, muito obrigado.
Eu desativei o antivírus, adicionei as exceções do firewall e mesmo assim não deu certo. Que mais posso tentar?
Obrigado.
André, Obrigado por todo conteúdo postado no seu blog,
Estava comentando com um amigo, não tem um tutorial que o André poste que não consiga colocar pra funcionar / compilar sem erros, mais uma vez parabéns!
Agora vamos lá, eu queria saber se você tem uma solução para comparar a estrutura de dois bancos de dados, a fim de incluir tabelas novas, procedures, triggers etc.
Grato desde já;
Bom dia, Joca, tudo bem?
Em primeiro lugar, agradeço pelo seu feedback sobre os artigos! Muito obrigado mesmo!
Joca, eu ainda não tive a oportunidade de desenvolver uma solução para comparar estruturas, mas, pelo que eu saiba, há duas formas:
1) Por script, usando comandos como IF EXISTS / IF NOT EXISTS;
2) Por codificação, através da criação de um wizard (um pequeno programa) para comparar cada tabela/coluna.
Há ainda uma terceira forma, que consiste em armazenar os scripts que já foram executados no banco de dados. Na empresa em que trabalho, por exemplo, há uma tabela para registrar todos os scripts que são executados no cliente. Nós usamos essa tabela como parâmetro de comparação. Caso um script esteja na atualização, mas não consta nessa tabela, significa que é preciso executá-lo. Dessa forma, conseguimos manter todos os clientes na mesma “versão” do banco de dados.
Abraço!
Parabéns pela sequencia e estou aguardando ansioso pelo “remake” utilizando HTTP. Hoje eu ja utilizo classes nativas do Delphi para compactar e descompactar, mais tenho certeza que fará um ótimo novo artigo que terei o prazer de ler e “copiar” as boas ideias que você sempre tem.
Gostaria de tirar uma dúvida contigo:
Hoje eu baixo a sequencia de arquivos todas abertas, para não ficar “travado” muito tempo a conexão do GET e tals, porem estou tendo problema em alguns clientes. Uma pasta que tem muitos arquivos (exemplo Schemas de NFe que tem mais de 100) o idFTP baixa só uma parte deles, e depois desconecta e não segue com a atualização. Exemplo de um cliente que sempre baixa só os primeiros 50 arquivos de schema, depois ele não consegue baixar mais. Você já pegu um caso assim? Sabe me dizer se é configuração, problema ou comportamento normal e deveria compactar tudo antes de enviar?
Olá, Emerson! Ótima pergunta!
Acredito que o problema está relacionado com o timeout de leitura do componente TIdFTP.
Vou pedir para que você altere as seguintes propriedades e refaça o teste:
– TransferType para ftBinary
– NATKeepAlive.UseKeepAlive para True
– NATKeepAlive.IdleTimeMS para 10000
– NATKeepAlive.IntervalMS para 1000
Além disso, verifique também se a propriedade Readtimeout está com um valor muito baixo. O padrão é 60000 (60 segundos).
Abraço!
Boa Tarde, André, em primeiro lugar, preciso parabenizá-lo pelo artigo. Você ajuda muito aos mais perdidos no Delphi, alem é claro de ser um professor extremamente didático, fica fácil aprender assim.
Bom, fiz uma teste com seu programa, e olha que curioso, na empresa onde trabalho o sistema funciona perfeitamente, já em casa não conecta de jeito algum, dá um erro chamado #10054 ao conectar. Fiz uma série de pesquisas na net, e cheguei a conclusão de que o problema seria com a minha internet pois não permite acesso a porta 21 (padrão). Interessante é que nem o FileZilla estava conectando até que mudei o campo criptografia no FileZilla para “USE SOMENTE FTP SIMPLES (INSEGURO)”, e pronto, o FileZilla começou a conectar ao FTP, já o Delphi mesmo assim não conecta.
Alguma ideia do que eu possa fazer? pensei que o IdFtp tivesse essa configuração, mas não achei nada.
Fico muito agradecido de você tiver alguma solução, mas se não tiver fico agradecido também.
Olá, Márcio, tudo bem?
Obrigado pelo feedback sobre o artigo 🙂
A sua situação é interessante. Ainda não tenho uma resposta, mas vou entrar em contato com você, ok?
Abraço!
Muito bom o post e eu usei. Tudo funcionando perfeitamente até a parte de atualização do banco. Utilizei o RUNSCRIPT do amigo acima, só que ao executar script com SET TERM ^ ele dá erro. Teria alguma forma de contornar isso? Estou tentando criar triggers e necessito deste comando.
Abraços e parabéns. No aguardo do novo POST.
Olá, Pedro, boa noite!
Na funcionalidade de atualização automática que codifiquei em um dos meus projetos, utilizei a ferramenta nativa de execução de scripts do Firebird, chamada ISQL, apresentada na dica 6 deste artigo. Para mais informações sobre este utilitário, acesse a documentação abaixo:
https://www.firebirdsql.org/pdfmanual/html/isql.html
Abraço!
Ola André, seu trabalho é ótimo, gosto muito de acompanhar, mas uma coisa que ainda não achei nem na internet e que ainda não vi você colocar em pauta é a verificação de arquivos por CRC ou MD5.
No caso meu projeto e de atualizar todo um aplicativo (vários arquivos), só que a verificação tinha q ser dessa maneira:
Comparar MD5 ou CRC de um certo arquivo (teste.exe) que está no FTP, com o arquivo “texte.exe” que esta no cliente. Se MD5 for igual ele não baixa do FTP, caso for diferente ele baixa o arquivo do FTP substituindo o mesmo.
Já tentei de tudo e ainda não tive resultado… queria saber se você tem alguma dica ou exemplo para isso.
Agradeço deste já…
Obrigado pela atenção, e uma ótima semana…
Boa noite, Pedro. Ótima pergunta.
Ainda não publiquei artigos sobre a verificação de hashes no Delphi, mas já vou anotar aqui.
Bom, Pedro, a sua ideia é interessante, porém, para que você possa ler o MD5 de um arquivo que está no FTP, é preciso baixá-lo primeiro. Portanto, de qualquer forma, você sempre irá baixar o arquivo “teste.exe” para comparar os hashes. Analisando por este lado, talvez não seja uma boa solução.
Por este motivo que, nos artigos dessa série, apresentei uma forma de utilizar apenas um arquivo texto. É um arquivo pequeno, rápido de ser baixado, e que contém a versão atual do sistema para fins de comparação. Somente quando as versões estiverem diferentes é que, de fato, o executável atualizado será baixado.
Abraço!
Boa noite André! Há anos venho desenvolvendo várias aplicações e só agora implementei essa parte de atualização automática, tenho meu próprio servidor de domínio, instalei o FileZilla Server, a comparação de versão faço via banco de dados, comparando a versão do aplicativo que está no cliente com a versão em uma tabela no banco de dados remoto em meu próprio servidor, e gerando as versões incrementais a cada build no próprio Delphi. Sua publicação ajudou muito, são pessoas como você que traz conhecimento pra todos, e assim cada desenvolvedor pode ir aprimorando seus sistemas e adquirir mais experiência, muito obrigado, abração!!!
Olá, Matheus, boa noite!
Sua solução ficou excelente! Comparar a versão com uma tabela no banco de dados é mais seguro e confiável do que um arquivo texto. Além disso, builds incrementais são um ótimo mecanismo neste contexto de atualização automática!
Agradeço muito pelo seu comentário, Matheus. Espero continuar contribuindo para a comunidade Delphi sempre que possível!
Abração!
Parabéns pelo artigo, implementei em meu sistema. Só uma ressalva, tive que baixar a versão mais atual do 7z. A do link que você passou não funcionou no windows 10.
Olá, Armando!
Obrigado pela ressalva! Atualizei o artigo para que o link seja direcionado para a página oficial de downloads do 7-Zip.
Grande abraço.
Olá, mais uma vez André. Mais uma ressalva.
Com as últimas atualizações do 7z existe o comando “x” que faz a extração completa do .rar, ou seja, podemos distribuir a pasta dos relatórios ou outras pastas do sistema tranquilamente.
O meu comando ficou assim:
Perfeito, Armando! Muito obrigado pela contribuição!
Pretendo reescrever essa série de artigos em um futuro próximo para abordar essas melhorias. Essa dica que você enviou estará entre elas!
Abração!
Prezado André, boa noite!
Você é uma pessoa especial, graças a Deus, a pessoa que compartilha conhecimentos, principalmente como meio de sobrevivência, é realmente uma virtude, uma nobreza, e por isso você se torna uma pessoa especial. Parabéns!!!
Agora vamos à pergunta: caro André, desculpe minha ignorância na linguagem, poderia me informa qual a função que compara a versão local e a versão FTP para poder colocar no evento OnShow do formulário principal
Desde já agradeço por todos os posts, tem ajudado bastante!!!
Olá, José Raimundo! O código que faz a comparação é este:
A parte 2 dessa série de artigos tem mais detalhes!
Abraço!
Professor, boa noite. Sempre útil esse seu post. Me ajuda com uma coisa? Queria que o executável fizesse um AUTO-UPDATE, depois de verificado a nova versão, baixado, ele fizesse a atualização mas o executável é sempre o mesmo que esta rodando… Alguma ideia ou link que possa ajudar?
Olá, Germano, boa noite.
Não tenho certeza se entendi muito bem a sua dúvida, mas a funcionalidade proposta nessa série de artigos é exatamente um Auto-Update, ou seja, o próprio executável baixa a atualização e, em seguida, é sobrescrito pelo novo executável descompactado. Veja o método
DescompactarAtualizacao
da parte 2 dessa série.Qualquer dúvida, envie um e-mail para [email protected].
Abraço!
Ola, André , Bom dia.
Passei só para deixar meus aplausos e da meus parabéns pelo post. Então eu fiz aqui já tem mais de um mês e já fiz umas 3 atualizações e meus cliente estão super satisfeitos pois agora ficou mais fácil de eu atualizar aplicação e claro que eu dei mais algumas incrementada e ficou show!
Abraços!!
E muito obrigado!
Que bom, Roberto! Essa funcionalidade realmente é uma mão na roda!
Em breve eu pretendo refazer esses artigos simplificando algumas partes. Quando eu postar, te aviso!
Abração!
Estarei aguardando !!!
Abração!!
Excelente post professor! Sempre muito útil tudo o que vem nos ensinando.
Eu apliquei o exemplo em um projeto meu, ficou muito legal, porem enrosquei em um caso aqui (acho que estou estou querendo muito, até onde pesquisei parece não ser possível). Gostaria de reinicializar minha aplicação antes mesmo dela iniciar, ou seja, antes do Application.Run.
No meu .dpr fiz uma função pra verificar se há nova versão para atualizar, caso existir, faz o download do novo .exe e o substitui. Chamo a função com ShellExecute para executar o .exe externamente e uso também o Application.Terminate logo após. Porém não funciona.
É possível realizar essa tarefa antes do Application.Run ?
Desde já agradeço
Olá, Eduardo, tudo bem?
Excelente pergunta!
Eu fiz o mesmo procedimento em um dos meus projetos pessoais, e foi justamente da forma como você mencionou:
A diferença é que, no meu caso, a aplicação abre (ou seja, passa pelo
Application.Run
), e logo após verifica se há uma nova atualização. Talvez esse seja o motivo pelo qual funcionou no meu projeto.A propósito, no sistema em que eu trabalho atualmente, a verificação por uma nova atualização é feita na Splash Screen (tela inicial de carregamento). Neste momento, o
Application.Run
já foi executado também. Talvez você pode fazer uma tentativa com essa abordagem!Abraço!
Fala professor!
Tudo em ordem e por aí ?
Era exatamente assim que eu estava fazendo, no meu Splash que tem toda o rotina de atualização.
Consegui resolver meu problema!
Foi a solução mais fajuta que já vi kkk
Coloquei a chamada da função logo após o Application.Run, ou seja, no OnShow do meu FormPrincipal, o primeiro evento no OnShow antes de fazer qualquer outra coisa, mesmo assim não estava rolando ..
A Solução?! – Um Sleep(1000); rs
Como é preciso descompactar o arquivo e renomear os executáveis, não esta dando tempo do ShellExecute chamar o novo .exe, com isso há tempo necessário pra acabar os eventos externos, ficando tudo pronto para o ShellExecute chamar o novo executável atualizado.
#Que fique como dica para quem está enroscado no mesmo problema em que me encontrava! (;
Muito obrigado Professor, com sua pequena explanação já consegui fluir aqui.
Abraço!
Opa, que bom que deu certo, Eduardo!
Sua dica certamente vai ajudar outros desenvolvedores, sim!
Boa sorte no projeto. Abraço!
Bom dia, André parabéns pelo conteúdo queria tirar uma duvida com vc que estou no Delphi. Estou criando um navegador usando o componente do Chromium. Até aí tudo blz, está funcionando direitinho, porém o Edit ou o campo de url padrão do TChromium tem limite de 2046 caracteres. Como faço para rodar javascript na url que tem mais de 15000 caracteres? Exemplo : > javascript : +code
lendo do fonte ou lendo de um arquivo txt hospedado no meu ftp?
te agradeço desde já!
Olá, Cristiano, bom dia.
Infelizmente tenho pouquíssima experiência com o componente TChromium 🙁
Mesmo assim, uma dúvida: não seria possível utilizar outro componente com mais limites, como um “Memo” no lugar do Edit?
Olá, muito bem explicado.
Estou só com problema no Delphi Rio 10.3, onde não consigo mostrar a tela de atualização, com a ProgressBar.
No Berlin, funciona tranquilo, agora no Rio ele fica a tela de login congelada, enquanto faz o download, ai já aparece o arquivo baixado e para instalar.
Olá, Fernanda.
Vou entrar em contato com você para pedir mais detalhes.
Olá André!
Meus parabéns pelo artigo, excelente, porém poderia me auxiliar num erro?
Uso o Delphi 7 64bits, e na linha de comando:
ShellExecute(0, nil, ‘7z’, PWideChar(‘ x -aoa ‘ + sNomeArquivoAtualizacao +’ -o’ + ObterDiretorioDoExecutavel), ”, SW_SHOW);
dá o seguinte erro:
[Warning] Unit1.pas(164): Suspicious typecast of String to PWideChar
[Error] Unit1.pas(164): Incompatible types: ‘WideChar’ and ‘Char’
[Fatal Error] Project1.dpr(5): Could not compile used unit ‘Unit1.pas’
Já tentei de tudo, o comando pelo cmd funciona, mas o programa com esse erro não compila.
Me ajude por favor se puder, obrigado, abraço
Olá, Emanuel, tudo bem?
Em primeiro lugar, peço desculpas pela demora. Essa quarentena tá sendo difícil pra gente.
No Delphi 7, utilize PChar ao invés de PWideChar. Provavelmente irá funcionar.
Abraço!
Boa tarde André!
Primeiro quero te parabenizar pelos conteúdos de valor que agrega nossos conhecimentos!
Implementei essa rotina no meu software, rapaz o sistema foi para outro nível rsrs…
Agora estou precisando de uma ajuda no item 6 desse 3º método, com relação ao Script de atualização de BD, pensei em usar o TFDScript do Firedac para atualizar/criar novos campos no Firebird, mas pra ser sincero nem sei por onde começar (sou novo ainda rsrs).
Poderia me dar uma ideia(um exemplo) de como implementar isso nessa rotina de atualização?
Mais uma vez obrigado pelos conteúdos!
Estou sempre seguindo seu site.
Um abraço
Olá, Josué, como vai?
Em primeiro lugar, agradeço pelo seu feedback sobre o blog. Já faz mais de 1 ano que não faço novas publicações, mas pretendo retomar em breve.
Josué, sua dúvida é muito válida. Na verdade, recebo muitas dúvidas relacionadas à execução de scripts em atualizações.
Bom, você tem três opções:
Com o isql (utilitário de execução de scripts do Firebird):
O isql pode ser executado por linha de comando, portanto, após a atualização do executável, basta chamá-lo passando o arquivo de script como parâmetro. Em um dos meus sistemas, eu crio um arquivo .bat com essa instrução do isql, e o executo de dentro do Delphi durante a atualização.
Com o TFDScript:
Este componente do FireDAC é bastante útil para execução de scripts. Você pode criar uma instância desse componente, preencher a propriedade de conexão (Connection) e do arquivo de script (SQLScriptFileName), e chamar o método
ExecuteAll
. Na documentação da Embarcadero há informações e exemplos mais detalhados.Ao abrir a aplicação:
Alguns desenvolvedores preferem executar o script “separado” da atualização. Para isso, por exemplo, ao abrir a aplicação, alguma rotina verifica se existe algum arquivo de script no diretório da aplicação. Se existir, esse script é carregado, executado (pode ser com o TFDQuery ou TFDScript) e, em seguida, excluído (para que não seja executado novamente).
A decisão por uma dessas opções é do próprio desenvolvedor ou da realidade do projeto.
Eu, particularmente, prefiro o isql pela facilidade de uso e por ficar “desacoplado” do executável principal.
Abraço!
Meus parabéns, excelente, coloquei pra rodar na empresa em que trabalho, muito bom mesmo!
Uma dúvida, onde está sendo armazenado o ExtrairVersaoAplicacao?
Obrigado!
Olá, Humberto! Que bom que deu certo!
Não tenho certeza se entendi muito bem a sua dúvida, mas o código do método “ExtrairVersaoAplicacao” está na parte 3 da série de artigos. Essa função pode ser utilizada no início da verificação de novas atualizações para comparar a versão local com a versão que está no FTP.
Abraços!
Olá, gostaria de agradecer e parabenizar por esse ótimo tema, me ajudou muito, está muito bem explicado.
Já estou com atualizador pronto, mas me esbarrei na parte de executar o script, é que sou novo em programação e tenho muitas dúvidas ainda. Poderia me passar um exemplo ou mais detalhes de como usar essa linha para rodar os scripts na atualização, por favor?
Olá, Geova, tudo bem?
Para rodar o(s) script(s), basta chamar este comando utilizando o
WinExec
,ShellExecute
ouCreateProcess
no Delphi.Clique aqui para ver um exemplo com
WinExec
no ActiveDelphi.Abraço!
Parabéns pelos artigos André, ajudam bastante! Gostaria de deixar uma sugestão de artigo, que provavelmente deve ser a dúvida de vários programadores iniciantes também, que é sobre proteção da aplicação contra cópias ilegais. Portanto, se possível, tente fazer um artigo sobre esse tema.
Olá, Marcos!
Muito obrigado pelo feedback e também pela sugestão de tema! Interessante!
No momento o blog está meio desatualizado, mas pretendo retomar o trabalho em breve.
Grande abraço!
Olá André, excelente post realmente uma aula ! Tenho este exemplo rodando em meu FTP(pc-local) com no-ip para acessos externos. Gostaria de saber se consigo fazer a mesma coisa usando o Google Drive? ao invés de acessar meu pc com FTP eu acessaria o Google Drive! será que da certo ?
Olá, Marcos, bom dia!
Primeiramente, obrigado pelo feedback!
É possível utilizar o Google Drive, porém, eles usam uma forte camada de segurança para aplicações externas. Você teria que trabalhar com a API própria do Drive e utilizar autenticação OAuth 2.0.
Como é algo bem complexo, minha sugestão é manter os arquivos em algum diretório FTP. Como alternativa, você pode subir um servidor HTTP e usar o componente TIdHTTP para baixar os arquivos, mas, neste caso, não haveria autenticação, o que deixaria a funcionalidade mais vulnerável.
Abraço!
Professor, neste exato momento cá estou novamente pra usar seu exemplo no meu sistema. A primeira vez que tive acesso a este material foi em 2016!
Opa, legal, Marcos!
E lá se vão 8 anos, rsrs.
André, em relação aos passos 6 e 7 desta última parte. Gostaria de trocar uma ideia com você, meu pensamento: 1-transferir arquivos junto do executável: o servidor de atualização devolve tudo zipado (empacotado), ao extrair esses arquivos extras automaticamente estarão dentro da pasta do aplicativo, considerando uma abordagem mais simplória, na qual o pacote é baixado dentro da pasta do executável e não há preocupação com au diretórios; e, 2-ataulizar banco de dados, considere que o sistema principal, após o login do usuário, verifica se há atualização e caso sim, faz o download e extração, conforme seu exemplo. Então o primeiro usuário do dia que logar no sistema dispara a atualização do banco também. Agora e tira a dúvida como eu posso cercar pra garantir que o banco só seja atualizado mesmo na primeira atualização do dia?
Possível solução, na minha visão: 1-o banco precisa ter um controle da versão em que está, 2-o banco precisa ter controle de quem está logado, 3-o atualizador verifica se a versão do banco é inferior a q está no servidor. Se for dispara a rotina que executa script do banco e bloqueia o acesso do banco a outros usuários, 4-atualiza demais arquivos do sistema.
André, gostaria que compartilhasse sua visão sobre possível solução também.
Olá, Marcos, tudo bem?
Peço desculpas pela demora.
Bom, Marcos, devo admitir que esse artigo já está relativamente obsoleto. De 2015 pra cá, uma série de alterações e aprimoramentos foram aplicados ao Delphi para facilitar algumas implementações. Atualmente, portanto, seria bem mais simples implementar essa funcionalidade de atualização automática. Por exemplo, utilizar a classe nativa TZipFile ao invés do 7-Zip.
Não tive tempo ainda, mas pretendo fazer um “remake” de alguns artigos, e esse está na lista.
A respeito do banco de dados, concordo com a abordagem de controlar a versão. Na verdade, essa é uma solução que encontramos no sistema em que eu trabalho. Há uma tabela de “informações do cliente”, e a versão do banco é uma das colunas. Dessa forma, conseguimos identificar se a estrutura das tabelas do cliente precisa de alguma alteração durante a atualização do software.
Se você não pretende trabalhar com o banco de dados para manter esse controle, uma alternativa é utilizar arquivos INI ou de texto armazenados no servidor.
Só deixo aqui algumas recomendações:
1) Sempre invista em backups, seja do banco de dados ou dos arquivo(s) de texto que armazenam as versões dos clientes.
2) Antes de iniciar a atualização, certifique-se de que todas as instâncias do software estejam fechadas para evitar corrupção de dados ou inconsistências.
3) Copie os arquivos antigos para alguma pasta ao invés de removê-los. Dessa forma, Em caso de algum erro crítico na nova versão, basta fazer um “downgrade” para a versão antiga.
Abraço!
Boa tarde! Será que conseguiria fazer algo parecido para atualização de um app Android em Delphi? Mas fazendo a atualização direta sem as perguntas de baixar ou não o arquivo, Instalar ou não o arquivo que acontece quando instalamos app de fontes desconhecidas?
Obrigado!
Olá, Sergio.
A atualização de aplicativos em sistemas Android ocorre de maneira bem diferente de aplicações Desktop.
Infelizmente não tenho o conhecimento necessário em Firemonkey para responder a sua pergunta. Sugiro entrar em contato com o MVP Adriano Santos, que é especialista na área.
Abraço!
Obrigado pela resposta! Não resolvi ainda mas usando o Http dá pra buscar o apk em um IP com uma porta definida na nuvem usando por exemplo o xamp. Consegui baixar o apk, mas não instalou depois que baixou amanhã vou ver se consigo. Se eu conseguir vou postar aqui o código. Pode ser que mais alguém precise um dia. Obrigado até
Obrigado, Sergio.
Se conseguir, fique à vontade para postar o código. Com certeza ajudará a comunidade!
Abração!
Bom dia ! quase lá! Mas já se consegue baixar e inicia a instalação, no entanto ainda pergunta se pode atualizar e depois pede para abrir o programa, mas se alguém perceber onde eu estou errando, ajuda ai!!! segue o código:
function OpenURL(const URL: string; const DisplayError: Boolean = False): Boolean;
{$IFDEF ANDROID}
var
Intent: JIntent;
aFile : Jfile;
MyFile : TMemoryStream;
IdHTTP : TiDHTTP ;
rsl : boolean;
fdir,Filename:string;
begin
fdir := GetSharedDownloadsDir+'/DiretorioParaInstalacao';
if not DirectoryExists(fdir) then
CreateDir(fdir);
IdHTTP := TiDHTTP.Create;
MyFile := TMemoryStream.Create;
{Download do arquivo}
IdHTTP.Get(URL, MyFile);
MyFile.SaveToFile(fdir+'/Meuapp.apk') ;
FileName := TPath.Combine( fdir, 'Meuapp.apk' );
aFile := TJFile.JavaClass.init(StringToJString(FileName));
Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_INSTALL_PACKAGE);
Intent.setDataAndType(TAndroidHelper.JFileToJURI(aFile), StringToJString('application/vnd.android.package-archive'));
Intent.setFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
try
TAndroidHelper.context.startActivity(Intent);
// Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW, TJnet_Uri.JavaClass.parse(StringToJString(TIdURI.URLEncode(URL))));
// SharedActivity.startActivity(Intent);
exit(true);
except
on e: Exception do
begin
if DisplayError then ShowMessage('Error: ' + e.Message);
exit(false);
end;
end;
Espero que alguém passe por aqui e possa ajudá-lo, Sergio.
Mesmo assim, vou enviar seu código para alguns colegas para análise.
Abraço!