<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>TechTips &#187; Componentes</title>
	<atom:link href="http://www.techtips.com.br/category/programacao/delphi/componentes/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.techtips.com.br</link>
	<description>Dicas de Tecnologia</description>
	<lastBuildDate>Wed, 24 Jun 2009 19:06:32 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>FastMM e Delphi 2006</title>
		<link>http://www.techtips.com.br/programacao/fastmm-e-delphi-2006/</link>
		<comments>http://www.techtips.com.br/programacao/fastmm-e-delphi-2006/#comments</comments>
		<pubDate>Thu, 22 Feb 2007 16:06:39 +0000</pubDate>
		<dc:creator>Alexandre José</dc:creator>
				<category><![CDATA[Componentes]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Gerenciamento de Memória]]></category>
		<category><![CDATA[IDE]]></category>
		<category><![CDATA[Linguagem Delphi]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Programação]]></category>
		<category><![CDATA[Práticas]]></category>

		<guid isPermaLink="false">http://www.techtips.com.br/programacao/fastmm-e-delphi-2006/</guid>
		<description><![CDATA[Continuando um tema anteriormente publicado, neste artigo vamos começar a detectar os vazamentos de memória (memory leaks) existentes em nossos aplicativos. (isso se existirem, é claro&#8230;) A partir do&#160;Delphi 2006 o gerenciador de memória padrão que acompanha o Delphi passa a ser o FastMM. Quem não trabalha com Delphi 2006 pode baixar o FastMM aqui. [...]]]></description>
			<content:encoded><![CDATA[<p>Continuando um tema anteriormente publicado, neste artigo vamos começar a detectar os vazamentos de memória (memory leaks) existentes em nossos aplicativos. (isso se existirem, é claro&#8230;)</p>
<p>A partir do&nbsp;Delphi 2006 o gerenciador de memória padrão que acompanha o Delphi passa a ser o FastMM. Quem não trabalha com Delphi 2006 pode baixar o FastMM <a href="http://sourceforge.net/project/showfiles.php?group_id=130631">aqui</a>.</p>
<p>Se você estiver começando agora a desenvolver com Delphi deve estar se perguntando: Tá, e dai? O que é memory leak?</p>
<p><strong>Memory leak é um objeto criado pela aplicação que não foi corretamente destruído.</strong></p>
<p>Você pode simular esta situação criando qualquer objeto no OnCreate do formulário principal da sua aplicação. Exemplo:</p>
<p>
<pre>procedure TForm1.FormCreate(Sender: TObject);
var
  lstVazamento : TStrings;
begin
  lstVazamento := TStringList.Create;
  try
    lstVazamento.Add('Linha 1');
    lstVazamento.Add('Linha 2');
    lstVazamento.Add('Linha 3');
  finally

  end;
end;</pre>
</p>
<p>Execute o aplicativo e feche-o. Você notará que não acontece nada! <img src='http://www.techtips.com.br/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Esse teste ridículo foi feito só para você ver como é comum ocorrerem problemas que resultem em&nbsp;vazamentos de memória (memory leaks)&nbsp;como a falta de atenção (nesse caso),&nbsp;a pressa, Control+C / Control+V entre alguns outros.</p>
<p>Agora insira esta linha logo após o begin da sua procedure:</p>
<p>ReportMemoryLeaksOnShutdown := True;</p>
<p>Deixando o código assim:</p>
<p>
<pre>procedure TForm1.FormCreate(Sender: TObject);
var
  lstVazamento : TStrings;
begin
  ReportMemoryLeaksOnShutdown := True;

  lstVazamento := TStringList.Create;
  try
    lstVazamento.Add('Linha 1');
    lstVazamento.Add('Linha 2');
    lstVazamento.Add('Linha 3');
  finally

  end;
end;</pre>
</p>
<p>Repita a operação e veja o que acontece após fechar o aplicativo:</p>
<p><img src="http://www.waveobjects.com.br/blog/img/FastMMeDelphi2006_60DB/ML0127.png"> </p>
<p>Veja que recebemos a notificação do TStringList que criamos e das 3 linhas que adicionamos (String x 3) a ele. A&nbsp;partir dai podemos começar a evitar esse tipo de erro na hora de desenvolvermos aplicativos.</p>
<p>Uma dica importante é você utilizar o&nbsp;bloco <strong>try&#8230;finally&#8230;end;</strong> sempre que criar um objeto dinamicamente. Isto evita que ocorra um erro no meio da execução e ele não seja destruído devidamente. Veja os dois exemplos:</p>
<p><u>Método não apropriado:</u></p>
<p>
<pre>procedure TForm1.FormCreate(Sender: TObject);
var
  lstVazamento : TStrings;
begin
  ReportMemoryLeaksOnShutdown := True;

  lstVazamento := TStringList.Create;
  lstVazamento.Add('Linha 1');
  lstVazamento.Add('Linha 2');
  lstVazamento.Add('Linha 3');
  lstVazamento.Free;
end;</pre>
</p>
<p><u>Método apropridado de utilização:</u></p>
<p>
<pre>procedure TForm1.FormCreate(Sender: TObject);
var
  lstVazamento : TStrings;
begin
  ReportMemoryLeaksOnShutdown := True;

  lstVazamento := TStringList.Create;
  try
    lstVazamento.Add('Linha 1');
    lstVazamento.Add('Linha 2');
    lstVazamento.Add('Linha 3');
  finally
    lstVazamento.Free;
  end;
end;</pre>
</p>
<p>Utilizando o bloco <strong>try&#8230;finally&#8230;end;</strong> mesmo que ocorra algum erro na execução do procedimento o objeto é destruído. Pelo método não apropriado lstVazamento não seria destruído caso ocorresse algum problema na execução da linha lstVazamento.Add(&#8216;Linha 1&#8242;);, por exemplo.</p>
<p>Em tempo: utilizando ReportMemoryLeaksOnShutdown := True; a mensagem ocorrerá sempre que o aplicativo for terminado, inclusive fora do Delphi. Para evitarmos esse situação, substitua o True por DebugHook &lt;&gt; 0; deixando a linha assim:</p>
<p>ReportMemoryLeaksOnShutdown := DebugHook &lt;&gt; 0; </p>
<p>Para quem utiliza outras versões do Delphi isto deve ser configurado no arquivo FastMM4Options.inc.</p>
<p>No&nbsp;próximo artigo abordaremos mais funções do FastMM&nbsp;e configurações do arquivo FastMM4Options.inc para quem utiliza FastMM em versões mais antigas do Delphi. Até mais!</p>
<p><center>&copy; <a href="http://www.waveobjects.com.br/blog">TechTips</a></center></p>]]></content:encoded>
			<wfw:commentRss>http://www.techtips.com.br/programacao/fastmm-e-delphi-2006/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Executando v&#225;rios m&#233;todos ao mesmo tempo.</title>
		<link>http://www.techtips.com.br/programacao/delphi/componentes/executando-vrios-mtodos-ao-mesmo-tempo/</link>
		<comments>http://www.techtips.com.br/programacao/delphi/componentes/executando-vrios-mtodos-ao-mesmo-tempo/#comments</comments>
		<pubDate>Mon, 29 Jan 2007 23:21:00 +0000</pubDate>
		<dc:creator>leosimas</dc:creator>
				<category><![CDATA[Componentes]]></category>

		<guid isPermaLink="false">http://www.techtips.com.br/semcategoria/executando-vrios-mtodos-ao-mesmo-tempo/</guid>
		<description><![CDATA[Calma! É isso mesmo que você leu!Puro mérito de uma unit chamada AsyncCalls:Página do ASyncCallsDownload do arquivo Após o download, nem é necessário instalar nada no delphi. Basta adicionar a unit ASyncCalls.pas no seu projeto. Mostrarei agora um exemplo de como essa maravilha funciona: 1- Crie um projeto novo com uma tela assim: Nosso exemplo [...]]]></description>
			<content:encoded><![CDATA[<p>Calma! É isso mesmo que você leu!<br />Puro mérito de uma unit chamada AsyncCalls:<br /><a href="http://andy.jgknet.de/async/">Página do ASyncCalls</a><br /><a href="http://andy.jgknet.de/async/AsyncCalls.zip">Download do arquivo</a></p>
<p>Após o download, nem é necessário instalar nada no delphi. Basta adicionar a unit ASyncCalls.pas no seu projeto.</p>
<p>Mostrarei agora um exemplo de como essa maravilha funciona:</p>
<p>1- Crie um projeto novo com uma tela assim:</p>
<p><a href="http://delphi.wikispaces.com/space/showimage/asynccallantes.jpg"><img height="202" src="http://www.techtips.com.br/wp-content/uploads/2007/01/WindowsLiveWriter/Executandovriosmtodosaomesmotempo_11FDC/asynccallantes%5B1%5D%5B2%5D2.jpg" width="240" border="0"></a> </p>
<p>Nosso exemplo consiste em pegar os números pares de cada memo e jogá-los ao memo3.<br />Para isso, faremos uma procedure chamada pegarpares que será assim:<br />&nbsp;</p>
<pre>  procedure PegarPares(MemoAtual, MemoResultado : TMemo);
  var x: Integer;
  begin
    for x := 0 to MemoAtual.Lines.count-1 do
      If strtoint(MemoAtual.Lines[x])mod 2 = 0 then
        MemoResultado.Lines.add(MemoAtual.Lines[x]);
  end;
</pre>
<p>&nbsp;</p>
<p>Só que ao chamar um método seguidamente assim:<br />pegarpares(Memo1, Memo3);<br />pegarpares(Memo2, Memo3);</p>
<p>O conteúdo do memo3 ficaria assim:</p>
<p>2<br />6<br />10<br />14<br />18<br />4<br />8<br />12<br />16<br />20</p>
<p>Ou seja, logicamente ele faz primeiro para os números do memo1 e depois para o memo2.</p>
<p>Por incrível que pareça, o AsyncCalls fará nosso memo3 ficar dessa forma:</p>
<p>2<br />4<br />6<br />8<br />10<br />12<br />14<br />16<br />18<br />20</p>
<p>Ou seja, ele faz &#8220;ao mesmo tempo&#8221; para o memo1 e para o memo3.</p>
<p>Você já deve estar curioso com a infinidade de linhas de código que será preciso pra fazer isso né? Então fique impressionado com &#8220;tanta complexidade&#8221; <img src='http://www.techtips.com.br/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> </p>
<p>1- Com a unit AsyncCalls.pas adicionada ao projeto, coloque no uses AsyncCalls;</p>
<p>2-Crie a nossa função que será chamada várias vezes ao mesmo tempo:</p>
<pre>  procedure PegarPares(MemoAtual, MemoResultado : TMemo);cdecl;
  var x: Integer;
  begin
    for x := 0 to MemoAtual.Lines.count-1 do
      If strtoint(MemoAtual.Lines[x])mod 2 = 0 then
        MemoResultado.Lines.add(MemoAtual.Lines[x]);
  end;
</pre>
<p>&nbsp;&nbsp;Repare que, no fim da declaração da procedure, existe um keyword chamado &#8220;cdecl&#8221;. Esse&nbsp;keyword permite que o AsuncCall saiba a forma certa de passar os parâmetros da procedure.<br />Para que passar por um array? É que podemos ter várias procedures diferentes sendo executadas &#8220;ao mesmo tempo&#8221; e todas com seus parâmetros.</p>
<p>&nbsp;</p>
<p>3-No onclick do botão vamos declarar 3 variáveis da interface ISyncCall:</p>
<pre>  procedure TForm2.Button1Click(Sender: TObject);
  Var M1,M2 : IAsyncCall;
  begin
</pre>
<p>4-Carregaremos cada variável dessa maneira:</p>
<pre>    M1 := Asynccall(@PegarPares, [Memo1, Memo3]);
    M2 := Asynccall(@PegarPares, [Memo2, Memo3]);
</pre>
<p>5-E por último. Sim, por último já <img src='http://www.techtips.com.br/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' />  !&nbsp;Chamaremos as procedures com o AsyncMultiSync assim:</p>
<pre>    while AsyncMultiSync([M1,M2],true, 10) &lt; 0 do
      Application.ProcessMessages;
  end;
</pre>
<p>Dessa forma o Asynccalls sabe que tem que executar essas procedures ao mesmo tempo.<br />O primeiro parâmetro é seu array de&nbsp;IAsyncCall.<br />O segundo parâmetro é se vc quer esperar o fim de todas as procedures para continuar.<br />E por último, o tempo em milisegundos para atualizar a tela.</p>
<p>Por fim sua aplicação rodará assim:</p>
<p><a href="http://delphi.wikispaces.com/space/showimage/asynccall.jpg"><img height="202" src="http://www.techtips.com.br/wp-content/uploads/2007/01/WindowsLiveWriter/Executandovriosmtodosaomesmotempo_11FDC/asynccall%5B1%5D%5B4%5D1.jpg" width="240" border="0"></a> </p>
<p>Este foi um exemplo simples para entender como funciona o AsyncCalls. Mas preparem-se, porque&nbsp;artigos mais complexos&nbsp;estarão vindo aí.</p>
<p>Espero que tenham gostado.<br />Obrigado.</p>
<p><center>&copy; <a href="http://">TechTips</a></center></p>]]></content:encoded>
			<wfw:commentRss>http://www.techtips.com.br/programacao/delphi/componentes/executando-vrios-mtodos-ao-mesmo-tempo/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Auto-Atualização de Aplicativos em Delphi</title>
		<link>http://www.techtips.com.br/programacao/delphi/componentes/auto-atualizacao-de-aplicativos-em-delphi/</link>
		<comments>http://www.techtips.com.br/programacao/delphi/componentes/auto-atualizacao-de-aplicativos-em-delphi/#comments</comments>
		<pubDate>Sat, 13 Jan 2007 19:40:22 +0000</pubDate>
		<dc:creator>ric@rdo</dc:creator>
				<category><![CDATA[Componentes]]></category>

		<guid isPermaLink="false">http://www.techtips.com.br/semcategoria/auto-atualizacao-de-aplicativos-em-delphi/</guid>
		<description><![CDATA[Este artigo demonstra a construção passo a passo de um componente para atualização automática de aplicativos em um ambiente de rede através de um servidor FTP na linguagem Delphi. Procuro dar uma abordagem didática, explicando a codificação e as decisões de projeto, sem, no entanto, apresentar um componente “de brincadeirinha”. De fato, o componente em questão foi criado para suprir uma necessidade real e tem se mostrado muito útil e eficaz.]]></description>
			<content:encoded><![CDATA[<p><strong>Cenário inicial</strong></p>
<p>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. </p>
<p>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.</p>
<p>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. </p>
<p><strong>Pressupostos </strong></p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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. </p>
<p><strong>O componente </strong></p>
<p>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, apresento o link para o código fonte completo. </p>
<p>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.</p>
<p>&nbsp;</p>
<pre>  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;
</pre>
<p>&nbsp;</p>
<p>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.</p>
<p>&nbsp;</p>
<pre>procedure TAutoUpdate.CreateClient;
begin
  if Client = nil then
  begin
    AntiFreeze := TIdAntiFreeze.Create(nil);
    Client     := TIdFTP.Create(nil);
  end;
end;

…

destructor TAutoUpdate.Destroy;
begin
  if Client &lt;&gt; nil then
  begin
    Client.Free;
    AntiFreeze.Free;
  end;
  inherited;
end;
</pre>
<p>&nbsp;</p>
<p>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. </p>
<pre>…
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;
</pre>
<p>&nbsp;</p>
<p>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.</p>
<p>&nbsp;</p>
<pre>type
  TCompareVersions = procedure(Sender: TObject; ExeVersion,
    DeployVersion: string; var DeployIsLatest: Integer) of object;

TAutoUpdate = class(TComponent)
…
Published
  property OnCompareVersions: TCompareVersions;
…
end;</pre>
<p>&nbsp;</p>
<p>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). </p>
<p>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.</p>
<p>&nbsp;</p>
<pre>Type
  TNeedVersion = procedure (
    Sender: TObject; var DeployVersion: string) of object;

TAutoUpdate = class(TComponent)
…
Published
  property OnNeedVersion: TNeedVersion;
…
end;
</pre>
<p>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.</p>
<p>&nbsp;</p>
<pre>property ExeVersion: string read GetExeVersion;
</pre>
<p>&nbsp;</p>
<p>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.</p>
<p>&nbsp;</p>
<pre>property OnNeedDeploy: TNotifyEvent;
</pre>
<p>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. </p>
<pre>…
protected
  procedure Update;
public
  procedure Execute;
  procedure Deploy;
…</pre>
<p>&nbsp;</p>
<p>Vejamos a procedure Execute:</p>
<p>&nbsp;</p>
<pre>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 &gt; 0 then
  begin
    botoes := [mbOK];
    if OptionalUpdate then
      Include(botoes, mbCancel);
    if MessageDlg(UpdateMessage, mtInformation, botoes, 0) = mrOk then
      Update;
  end
  else if (i &lt; 0) and Assigned(FOnNeedDeploy) then
    FOnNeedDeploy(Self);
end;</pre>
<p>&nbsp;</p>
<p>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. </p>
<p>A seguir obtemos a versão atual através da função VersaoExe (ver 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. </p>
<p>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. </p>
<p>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.</p>
<p>&nbsp;</p>
<pre>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 &gt; 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;
</pre>
<p>&nbsp;</p>
<p>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:</p>
<p>&nbsp;</p>
<pre>…
    Client.OnWorkBegin := FTPWorkBegin;
    Client.OnWork      := FTPWork;
…

procedure TAutoUpdate.FTPWorkBegin(Sender: TObject; AWorkMode: TWorkMode;
  AWorkCountMax: Integer);
begin
  if AWorkCountMax &gt; 0 then
    frmAtualizando.Max := AWorkCountMax
  else
    frmAtualizando.Max := BytesToTransfer;
end;

procedure TAutoUpdate.FTPWork(Sender: TObject; AWorkMode: TWorkMode;
  AWorkCount: Integer);
begin
  frmAtualizando.Position := AWorkCount;
end;
</pre>
<p>&nbsp;</p>
<p>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).</p>
<p>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.&nbsp;&nbsp;</p>
<p>&nbsp;</p>
<pre>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;
</pre>
<p><strong></strong>&nbsp;</p>
<p><strong>Conclusão</strong></p>
<p>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. </p>
<p>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. </p>
<p>Claro, gostaria de receber qualquer melhoria porventura implementada.</p>
<p>&nbsp;</p>
<p><strong>Fontes</strong></p>
<p>Os fontes completos do componente estão disponíveis em <a title="http://www.dbquester.com/files/artigos/autoupdate/autoupdate.rar" href="http://www.dbquester.com/files/artigos/autoupdate/autoupdate.rar">http://www.dbquester.com/files/artigos/autoupdate/autoupdate.rar</a></p>
<p><center>&copy; <a href="http://www.dbquester.com">TechTips</a></center></p>]]></content:encoded>
			<wfw:commentRss>http://www.techtips.com.br/programacao/delphi/componentes/auto-atualizacao-de-aplicativos-em-delphi/feed/</wfw:commentRss>
		<slash:comments>87</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 0.488 seconds -->

