O exemplo com fonte se realiza da seguinte forma:

 

Procura no servidor de FTP pelo executável de atualização, se o executável do seu

computador for mais novo que o executável no servidor, quer dizer que você é o
desenvolvedor e quem possui uma nova versão do sistema e pergunta se quer realizar o

upload da nova versão. Se o executável for mais velho que o que está no FTP, ele baixa

e atualiza automaticamente o sistema.

 
Instalação do componente:
 
Baixe o arquivo com o componente e exemplo no seguinte link: AtualizacaoAutomatica.zip
 
 

Para instalar o componente, execute o arquivo “webupdatepk.dpk”, será aberto a janela
de instalação do componente:
Clique em Install e confirme a instalação. Feche as janelas confirmando que deseja

salvar.

Em seguida vá ao menu do Delphi: Tools / Enviromment Options e na pasta Library,

defina o caminho onde está o componente.

Pronto! O componente está instalado.

Comentários
 

O artigo e o componente são do
Ricardo Alves Carvalho, e-mail:

ricardo.alves@fazenda.mg.gov.br
 

Fiz algumas alterações no componente (adaptei para o Indy 9/Delphi 7, alterações de
layout, etc) , e criei um exemplo próprio para utilização do mesmo.
O exemplo que fiz faz o upload e o download de novas versões do programa.

Segue abaixo o artigo do Ricardo na integra.

Implementando Atualizações Automáticas
 

Resumo:
automáticas de aplicações

Cenário inicial
 

Creio que muitos desenvolvedores Delphi já passaram pela situação de ter aplicativos
instalados em diversas máquinas em um ambiente corporativo. Normalmente o
programa começa com uma pequena base de usuários e, com o tempo, mais pessoas

passam a utilizá-lo. Se por um lado isso é um sinal do sucesso do software, por outro,

com o aumento da base de usuários começam a surgir novos requisitos e descobertas de

bugs que culminam com a necessidade de gerar novas versões do aplicativo. E uma

tarefa das mais desagradáveis, além de tomar muito tempo, é sair de máquina em

máquina atualizando aplicativos. Em grandes corporações, aplicativos podem estar

instalados inclusive em outras cidades ou estados. Deixar esta tarefa para os usuários é

boa receita para dores de cabeça. Com certeza, com esta política, não dá pra se

considerar que todos farão as atualizações.

A solução para este problema, em um ambiente de rede, é fazer com que o software se

auto-atualize. No entanto esta tarefa não é das mais triviais. A primeira solução que

desenvolvi neste sentido me custou uns dois meses para se tornar estável (lógico que

não fiquei este tempo só por conta dessa atividade). Há questões um tanto complicadas

envolvidas como, por exemplo, a dificuldade de fazer debug em equipamentos remotos,

as diferenças de comportamento entre versões diferentes do Windows, a estratégia para

fazer com que um software possa substituir seu próprio arquivo, entre outras. Se isso

vale a pena para um aplicativo importante, é desanimador quando pensamos em

softwares mais triviais.

Assim, após desenvolver uma solução de auto-update para um aplicativo, foi natural

concluir que esta funcionalidade deveria idealmente ficar em um componente para que

pudesse ser facilmente reaproveitada. Afinal trata-se de uma necessidade comum à

maioria dos programas desenvolvidos em um ambiente corporativo. Portanto, investi

algum tempo na elaboração do componente TAutoUpdate. O resultado prático foi tão

satisfatório, para mim assim como para diversos colegas, que me motivou a escrever o

presente artigo.

Pressupostos
 

O pressuposto básico, é óbvio, é a disponibilidade de um ambiente de rede. No entanto,
é bom esclarecer que a versão apresentada do componente foi projetada para uma rede
LAN de bom desempenho. Ainda não é uma boa solução para conexões de baixa

velocidade ou em ambiente Web, pois o programa principal congela durante o

download. Para redes de baixa velocidade o download deveria ser realizado em uma

thread distinta, o que implicaria em uma série de alterações no projeto.

Considera-se, ainda, que o software será distribuído a partir de um servidor FTP. Uma

vez que o arquivo executável esteja disponível no servidor, sua distribuição será

automática. Por ser muito oportuno, o componente também é capaz de fazer o upload da

aplicação para o servidor quando o desenvolvedor libera uma nova versão. Para tanto, o

servidor deverá permitir operações de escrita ao usuário configurado no componente.

Na implementação proposta, entende-se que a atualização do programa consiste na

simples substituição do executável pelo arquivo da nova versão, de nome idêntico, não

sendo realizado nenhum procedimento adicional de instalação. Não é difícil, entretanto,

fazer alterações para a inclusão de funcionalidades semelhantes.

Embora o componente não faça nenhuma pressuposição a respeito da aplicação (não

impõe requisitos à aplicação), é necessário que haja algum mecanismo de identificação

e comparação das versões do executável. Assim, esta tarefa pode ser personalizada pelo

aplicativo, mas se as informações de versão forem habilitadas (em Project/Options), o

componente será capaz de realizá-la sem necessidade de codificação via aplicação.

Nesse caso, o esquema de identificação de versões será o tradicional de quatro números

separados por pontos.

O componente
 

Sem mais delongas, passemos então ao componente. Para facilitar a exposição, irei
apresentar recortes de código sem a implementação; basicamente para apresentar a
interface. As propriedades estão no ponto em que teclaríamos Ctrl+Shift+C para que o

Delphi completasse o código. Ao final do artigo, disponibilizo um link para o código

fonte completo.

Faz sentido manter o Client de FTP no componente, já que a utilização do protocolo, a

princípio, não diz respeito à aplicação e sim à tarefa de auto-atualização. Portanto,

iremos encapsular um TIdFTP. Optamos por utilizar a versão 10 dos componentes Indy.

Para a versão anterior, há uma ligeira diferença na codificação, mas não iremos abordála

aqui, por não ser nada substancial.

TAutoUpdate = class(TComponent)
private
Client: TIdFTP;

AntiFreeze: TIdAntiFreeze;

public

published

property FTPHost: string;

property FTPUser: string;

property FTPPassword: string;

property FTPDir: string;

property FTPPassive: Boolean;

end;

Seria natural, nesse ponto, imaginar que deveríamos sobrepor o construtor para
instanciar o Client FTP. No entanto, há uma particularidade: espera-se que os
aplicativos sejam atualizados apenas ocasionalmente e, portanto, a instanciação seria

inútil à maioria das vezes. Vamos então, deixar para instanciar o componente interno

quando concluirmos pela sua necessidade. O que podemos adiantar é o método privado

para executar esta tarefa, assim como o destrutor que fará a limpeza de contrapartida.

procedure TAutoUpdate.CreateClient;
begin
if Client = nil then

begin

AntiFreeze := TIdAntiFreeze.Create(Self);

Client := TIdFTP.Create(Self);

end;

end;

destructor TAutoUpdate.Destroy;

begin

if Client <> nil then

begin

Client.Free;

AntiFreeze.Free;

end;

inherited;

end;

Quando a atualização for iniciar é conveniente apresentar uma mensagem informando a
ocorrência ao usuário. Vamos criar uma propriedade para conter o texto dessa
mensagem. Seu valor inicial pode ser configurado no construtor da classe. Vamos ainda,

considerar que a atualização poderá ser opcional ou obrigatória, e criar uma propriedade

para ajuste desse comportamento.

published
property UpdateMessage: string;

property OptionalUpdate: Boolean;

end;

constructor TAutoUpdate.Create(AOwner: TComponent);

begin

inherited;

UpdateMessage :=

‘Há uma nova versão do aplicativo disponível.’#13 +

‘A atualização automática será iniciada.’;

end;

Uma questão central no que diz respeito a este componente, é a questão do estilo de
identificação das versões. Isso é importante porque é comparando a versão do
executável corrente com o disponível é que será possível saber se a operação a realizar

deve ser de atualização, de distribuição ou nenhuma. Uma boa alternativa é adotar o

estilo padrão com quatro números separados por pontos. No entanto, há

desenvolvedores que preferem trabalhar com um ou dois números, outros com uma

data, outros com textos (personal, professional, entreprise) etc. Se, por um lado, não

queremos impor um estilo em particular, por outro, achamos que o componente deve

oferecer algum tratamento com essa finalidade. Assim, optamos por adotar o estilo

padrão, mas com a possibilidade de personalização. Como há um momento em que as

versões devem ser comparadas, criamos um evento que o desenvolvedor pode ignorar,

assumindo assim o tratamento padrão, ou escrever um manipulador para impor uma

regra de comparação personalizada.

type
TCompareVersions = procedure(Sender: TObject; ExeVersion,
DeployVersion: string; var DeployIsLatest: Integer) of object;

TAutoUpdate = class(TComponent)

Published

property OnCompareVersions: TCompareVersions;

end;

Se o parametro var DeployIsLatest for maior que zero, a aplicação deve ser atualizada,
se for menor que zero deve ser distribuída (é o caso em que o desenvolvedor está
liberando uma versão para deploy e quer fazer o upload para o servidor FTP) e, se for

igual a zero nada deve ser feito (as versões são as mesmas).

Ainda temos outra questão com relação à identificação da versão. Precisamos conhecer

a versão disponível no servidor para fazer a comparação. Claro que não faz sentido

baixar o arquivo só pra ver a versão. Então optei por deixar esta verificação a cargo da

aplicação. Usualmente, basta gravar esta informação no banco de dados, o que torna a

tarefa muito simples para o aplicativo. Fazer isso no componente exigiria protocolos

adicionais ou acesso ao banco, o que não é (nem deveria ser) do conhecimento desta

camada. Para isso criamos mais um evento.

Type
TNeedVersion = procedure (
Sender: TObject; var DeployVersion: string) of object;

TAutoUpdate = class(TComponent)

Published

property OnNeedVersion: TNeedVersion;

end;

Para facilitar a eventual gravação da versão atual no banco de dados ou outra utilização
que o desenvolvedor possa ter, disponibilizamos, ainda, uma propriedade de só leitura.

property ExeVersion: string read GetExeVersion;
 

O último evento disponibilizado pelo componente é disparado quando o componente
conclui que a versão corrente aplicação é mais recente que a versão do servidor de FTP.
Talvez pareça um pouco estranho, afinal como a aplicação poderia ser mais nova que a

do servidor? Na verdade é simples: quando o desenvolvedor gera uma nova versão.

Assim, esse evento é um ótimo facilitador para programar a distribuição do novo

arquivo. Basta alterar o número da versão e o programa pode fazer automaticamente o

upload e, se for o caso, gravar no banco de dados a identificação de versão do arquivo.

property OnNeedDeploy: TNotifyEvent;
 

Bom, até agora está muito bonito, mas como o processo é iniciado? Precisamos de um
método de tempo de execução que acione a verificação e orquestre todo o processo.
Este método, chamamos de Execute. Criamos ainda us métodos Update e Deploy, que

fazem respectivamente o download e o upload a partir do servidor de FTP.

protected
procedure Update;

public

procedure Execute;

procedure Deploy;

Vejamos a procedure Execute:
 

procedure TAutoUpdate.Execute;
var
VersaoExecutavel, VersaoDisponivel: string;

i: integer;

botoes: TMsgDlgButtons;

begin

if not Assigned(FOnNeedVersion) then

raise Exception.Create(

‘O manipulador do evento OnNeedVersion é obrigatório.’

);

VersaoDisponivel := ”;

FOnNeedVersion(Self, VersaoDisponivel);

if VersaoDisponivel = ” then

raise Exception.Create(‘Versão disponível inválida (vazia).’);

VersaoExecutavel := VersaoExe;

if VersaoExecutavel = ” then

VersaoExecutavel := ’1.0.0.0′;

i := CompareVersion(VersaoDisponivel, VersaoExecutavel);

if Assigned(FOnCompareVersions) then

FOnCompareVersions(Self, VersaoExecutavel, VersaoDisponivel, i);

if i > 0 then

begin

botoes := [mbOK];

if OptionalUpdate then

Include(botoes, mbCancel);

if MessageDlg(UpdateMessage, mtInformation, botoes, 0) = mrOk then

Update;

end

else if (i < 0) and Assigned(FOnNeedDeploy) then

FOnNeedDeploy(Self);

end;

Inicialmente nos certificamos que exista um manipulador para o evento
OnNeedVersion, levantando uma exceção em caso negativo. Este manipulador deve
fornecer o valor da versão disponível no servidor e, portanto, é obrigatório.

A seguir obtemos a versão atual através da função VersaoExe (link para fontes

completos ao final do artigo) e a comparamos com a versão disponível. Se houver um

manipulador para o evento OnCompareVersions, este poderá alterar o resultado da

comparação padrão.

Finalmente, se a versão disponível for a mais recente iniciamos o procedimento de

Update. Para isso, enviamos uma mensagem para o usuário avisando que a versão será

atualizada. Esta mensagem terá um botão OK e, se OptionalUpdate for verdadeiro, um

botão Cancel para que o usuário possa cancelar o procedimento. Se a versão atual for

mais recente que a disponível, disparamos o evento OnNeedDeploy. Optamos por

deixar a decisão quanto a realizar a distribuição da nova versão a cargo do manipulador

de evento. Além disso, disponibilizamos um método Deploy que pode ser chamado

neste manipulador.

O próximo método a comentar é o Update, entendo que o mais importante do

componente, pois realiza a tarefa para o qual o mesmo foi desenvolvido.

procedure TAutoUpdate.Update;
var
tempFile, NomeExe, batchName, NomeDos: string;

lista: TStringList;

existe: Boolean;

begin

if FTPHost = ” then

raise Exception.Create(‘FTPHost não definido’);

CreateClient;

Client.Host := FTPHost;

Client.Username := FTPUser;

Client.Password := FTPPassword;

Client.Passive := FTPPassive;

if not Client.Connected then

Client.Connect;

if not Client.Connected then

raise Exception.Create(‘Erro na conexão com o servidor de FTP’);

Client.ChangeDir(FTPDir);

// verificar disponibilidade do arquivo no servidor

NomeExe := ExtractFileName(Application.ExeName);

lista := TStringList.Create;

frmAtualizando := TfrmAtualizando.Create(Self);

try

Client.TransferType := ftASCII;

Client.List(lista, NomeExe, False);

existe := (lista.Count > 0) and

(UpperCase(lista[0]) = UpperCase(NomeExe));

if not existe then

raise Exception.Create(‘Arquivo não disponível no servidor

FTP.’);

// Exibir transferência para o usuário

Client.TransferType := ftBinary;

BytesToTransfer := Client.Size(NomeExe);

frmAtualizando.Show;

// baixar arquivo temporário

tempFile := GetTmpDir + ChangeFileExt(NomeExe, ‘.tmp’);

Client.Get(NomeExe, tempFile, True);

Client.Disconnect;

if not FileExists(tempFile) then

exit;

// criar bath e sobrepor exe

NomeDos := ExtractShortPathName(ParamStr(0));

lista.Clear;

batchname := GetTmpFileName(‘.bat’);

FileSetAttr(ParamStr(0), 0);

lista.Add(‘:Label1′);

lista.Add(‘@echo off’);

lista.Add(‘del ‘ + NomeDos);

lista.Add(‘if Exist ‘ + NomeDos + ‘ goto Label1′);

lista.Add(‘Move ‘ + tempFile + ‘ ‘ + NomeDos);

lista.Add(‘Call ‘ + NomeDos);

lista.Add(‘del ‘ + batchname);

lista.SaveToFile(batchname);

ChDir(GetTmpDir);

WinExec(PChar(batchname), SW_HIDE);

finally

lista.Free;

FreeAndNil(frmAtualizando);

Application.Terminate;

end;

end;

Inicialmente, procedemos à criação, configuração e conexão do client FTP. Em seguida
verificamos a existência no servidor de um arquivo com o mesmo nome da aplicação.
Se tudo estiver certo até aqui, podemos iniciar o download. Para dar um feedback ao

usuário, utilizamos um formulário a parte (frmAtualizando), que encapsula um Gauge e

será atualizado pelo evento FTPWork do IdFTP. Para isso, alteramos o método

CreateCliente descrito anteriormente adicionando os manipuladores de evento:

Client.OnWorkBegin := FTPWorkBegin;
Client.OnWork := FTPWork;

procedure TAutoUpdate.FTPWorkBegin(Sender: TObject; AWorkMode:

TWorkMode;

AWorkCountMax: Integer);

begin

if AWorkCountMax > 0 then

frmAtualizando.Max := AWorkCountMax

else

frmAtualizando.Max := BytesToTransfer;

end;

procedure TAutoUpdate.FTPWork(Sender: TObject; AWorkMode: TWorkMode;

AWorkCount: Integer);

begin

frmAtualizando.Position := AWorkCount;

end;

Em seguida, fazemos o download salvando o arquivo no diretório temporário do
sistema. Neste momento surge a questão mais complicada do procedimento. Nós
teríamos, neste ponto, que sobrepor o executával da aplicação. Mas como fazer isso se a

mesma está rodando? O Sistema Operacional não permitiria sobrepor um arquivo em

uso. A solução, um tanto tortuosa, é verdade, foi criar um arquivo batch com esta

finalidade. O componente cria e aciona o script que irá entrar em looping até conseguir

apagar o arquivo executável. Como a aplicação terminará em seguida, o script sairá do

looping e realizará as tarefas subsequentes que são mover o arquivo temporário,

executá-lo e, por fim deletar seu próprio arquivo (sim, arquivos batches podem fazer

isso, provavelmente porque são inteiramente carregados para a memória antes da

execução).

O proximo método, o Deploy, é similar ao anterior, porém faz um updolad em vez de

um download, e não precisa fazer toda essa ginástica para sobrepor o arquivo.

procedure TAutoUpdate.Deploy;
var
NomeExe: string;

begin

if FTPHost = ” then

raise Exception.Create(‘FTPHost não definido’);

CreateClient;

Client.Host := FTPHost;

Client.Username := FTPUser;

Client.Password := FTPPassword;

Client.Passive := FTPPassive;

if not Client.Connected then

Client.Connect;

if not Client.Connected then

raise Exception.Create(‘Erro na conexão com o servidor de FTP’);

Client.ChangeDir(FTPDir);

NomeExe := Application.ExeName;

frmAtualizando := TfrmAtualizando.Create(Self);

try

Client.TransferType := ftBinary;

BytesToTransfer := FileLength(NomeExe);

Client.Put(NomeExe, ExtractFileName(NomeExe));

frmAtualizando.Show;

Client.Disconnect;

finally

FreeAndNil(frmAtualizando);

Screen.Cursor := crDefault;

end;

ShowMessage(‘Deploy finalizado.’);

end;

Conclusão
 

O componente TAutoUpdate tem se revelado muito útil no dia-a-dia. Com ele, em
questão de instantes conseguimos configurar uma aplicação que rode em um ambiente
de rede para que seja auto-autualizável e auto-distribuível.

Como sugestão de desenvolvimento, aponto a melhoria do componente para que o

mesmo possa operar adequadamente no ambiente Internet. Outra melhoria seria ter a

opção de, após o download, perguntar ao usuário se a aplicação deve ser atualizada

imediatamente ou apenas na próxima execução. Isso seria importante em aplicações que

possam ter dados não salvos.

Desenvolvimento de um componente para atualização e distribuição

AtualizacaoAutomatica.zip

  • Facebook
  • Blogger Post
  • Bookmarks.fr
  • DZone
  • DailyMe
  • Delicious
  • Design Float
  • Digg
  • Diglog
  • Favoriten
  • Google Bookmarks
  • Google Buzz
  • Google Gmail
  • Google Reader
  • Hatena
  • Hotmail
  • Imera Brazil
  • LinkaGoGo
  • LinkedIn
  • LiveJournal
  • MSDN
  • Mozillaca
  • MyLinkVault
  • MySpace
  • Netlog
  • Netvibes Share
  • Oneview
  • Orkut
  • Ping
  • Plaxo Pulse
  • Protopage Bookmarks
  • Read It Later
  • Reddit
  • Technorati Favorites
  • Technotizie
  • Twitter
  • Windows Live Favorites
  • Windows Live Spaces
  • WordPress
  • Yahoo Bookmarks
  • Yahoo Buzz
  • Yahoo Mail
  • Yahoo Messenger
  • YiGG
  • Yoolink
  • diHITT
  • Share/Bookmark

Nosso componente atualmente suporta o certificado digital modelo A1 e A3 para emissão de nota fiscal eletrônica. Mas cuidado, o modelo A3 pode gerar alguns problemas.

Vou explicar aqui o motivo.

Os certificados são diferentes tecnicamente, o modelo A1 só precisa de uma senha para funcionar, o modelo A3 necessita de um leitor do cartão. Justamente o cartão e o leitor impedem o uso em sistema on-line (SaaS), ou em terminal server, pois como nosses casos sistema é online, e nada fica instalado na máquina do cliente, não tem como fazer a assinatura da nota fiscal com esse certificado.

Uma Outra desvantagem do modelo A3 é que ele também é usado para alguns trâmites na Receita Federal e Secretaria da Fazenda. Então algumas vezes o contador precisa do cartão, deixando a empresa impossibilitada de assinar as notas nesse meio tempo. Além disso, o certificado modelo A1 é mais barato, cerca de R$130,00 nos Correios.

Você pode saber mais sobre a diferença entre esses certificados no site dos correios: http://www.correios.com.br/produtos_servicos/certificacaodigital/perguntas_respostas.cfm#perg21

 

Visite o hotsite do nosso componente para Nota Fiscal Eltrônica aqui: http://www.delphifontes.com.br/?pagina=nfe


 

  • Facebook
  • Blogger Post
  • Bookmarks.fr
  • DZone
  • DailyMe
  • Delicious
  • Design Float
  • Digg
  • Diglog
  • Favoriten
  • Google Bookmarks
  • Google Buzz
  • Google Gmail
  • Google Reader
  • Hatena
  • Hotmail
  • Imera Brazil
  • LinkaGoGo
  • LinkedIn
  • LiveJournal
  • MSDN
  • Mozillaca
  • MyLinkVault
  • MySpace
  • Netlog
  • Netvibes Share
  • Oneview
  • Orkut
  • Ping
  • Plaxo Pulse
  • Protopage Bookmarks
  • Read It Later
  • Reddit
  • Technorati Favorites
  • Technotizie
  • Twitter
  • Windows Live Favorites
  • Windows Live Spaces
  • WordPress
  • Yahoo Bookmarks
  • Yahoo Buzz
  • Yahoo Mail
  • Yahoo Messenger
  • YiGG
  • Yoolink
  • diHITT
  • Share/Bookmark

BlogBlogs.Com.Br
Seja bem-vindo.

Este espaço foi criado para servir como um canal de ajuda e discussão sobre os nossos produtos e serviços, e também, sobre  análise e desenvolvimento profissional de software em geral, linguagens de programação Delphi e C#.

Este é nosso blog, talvez você gostaria de visitar nosso site principal, acesse: http://www.delphifontes.com.br/


  • Facebook
  • Blogger Post
  • Bookmarks.fr
  • DZone
  • DailyMe
  • Delicious
  • Design Float
  • Digg
  • Diglog
  • Favoriten
  • Google Bookmarks
  • Google Buzz
  • Google Gmail
  • Google Reader
  • Hatena
  • Hotmail
  • Imera Brazil
  • LinkaGoGo
  • LinkedIn
  • LiveJournal
  • MSDN
  • Mozillaca
  • MyLinkVault
  • MySpace
  • Netlog
  • Netvibes Share
  • Oneview
  • Orkut
  • Ping
  • Plaxo Pulse
  • Protopage Bookmarks
  • Read It Later
  • Reddit
  • Technorati Favorites
  • Technotizie
  • Twitter
  • Windows Live Favorites
  • Windows Live Spaces
  • WordPress
  • Yahoo Bookmarks
  • Yahoo Buzz
  • Yahoo Mail
  • Yahoo Messenger
  • YiGG
  • Yoolink
  • diHITT
  • Share/Bookmark

Optimized by SEO Ultimate