Bom, como todos sabem, um atualizador cai muito bem para quem desenvolve programas empresariais, pela sua praticidade de atualização.

Bom, vamos começar fazendo o servidor, que é a parte mais simples!

Vamos precisar de:
1* Pagecontrol
1* Memo
1* TidTCPServer
1* TidAntiFreeze
1* TGroupBox
2* TEdits

Altere a propriedade name do formulário para: "FormPrincipalServer".
Altere a propriedade name de um edit para: "edtVersoes"e coloque o caption dele de: "Versoes.txt"
Altere a propriedade name de outro edit para: "edt1" e coloque o caption dele de: "C:\ServerAtualizador"
Altere a propriedade name do idTCPServer1 para: "Servidor"
Altere a propriedade DefaultPort do Servidor para: 34005
Altere a propriedade Bindings do Servidor para: ":34005"
Altere a propriedade Active do Servidor para: True
Crie duas TABS no PageControl, e uma chame de LOG e coloque o memo dentro dela com a propriedade align= alClient
A outra, chame de Configuração, e coloque os dois edits lá.

Crie uma pasta no seu diretório C:\ chamada "ServerAtualizador"
Crie um arquivo nesta pasta chamado versoes.txt contendo o arquivo que você desja atualizar e a versão dele ao lado EX: "Modulo1.exe=1.0.0.1"


Desta forma:
server1.JPGserver2.JPG

Agora vá no Servidor no evento OnExecute e coloque:

var
  Comando, Parametro, Linha, IP: string;
  Stream: TMemoryStream;
begin
  //Procesando os comandos
  Linha:= AThread.Connection.ReadLn; //Lê a linha enviada.
  IP:= AThread.Connection.Binding.PeerIP;
  Parametro:= Copy(Linha, Pos(` `,Linha)+1, length(Linha) );
  Comando:= LowerCase(copy(Linha, 1, pos( ,Linha)-1));

  if Comando = `arquivoversoes` then begin
    //Envia para o cliente, o aquivo de versões
    try
      mmo1.Lines.Add(IP + ` :: Requisição de arquivo de versões`);
      Stream:= TMemoryStream.Create;
      Stream.LoadFromFile(edt1.Text + `\` + edtVersoes.Text);
      AThread.Connection.WriteLn(inttostr(Stream.Size)); //Envia o tamanho do arquivo
      AThread.Connection.WriteStream(Stream);
    finally
      Stream.Free;
    end;
  end else

  if Comando = `get` then begin
    //Envia para o cliente, o aquivo que ele pediu.
    try
      mmo1.Lines.Add(IP + ` :: Requisição de arquivo "` + Parametro + `"`);
      Stream:= TMemoryStream.Create;
      Stream.LoadFromFile(edt1.Text + `\` + Parametro);
      AThread.Connection.WriteLn(inttostr(Stream.Size)); //Envia o tamanho do arquivo
      AThread.Connection.WriteStream(Stream);
    finally
      Stream.Free;
    end;
  end;

No OnConnect coloque:
  mmo1.Lines.Add(AThread.Connection.Binding.PeerIP + ` Conectado`);
  AThread.Connection.WriteLn(`[Servidor]Você está conectado ao servidor de atualização`);

Agora, Declare uma variável Global Chamada: "Local" do tipo string e antes do End. coloque isto:

initialization
  Local:=ExtractFilePath(Application.ExeName);

finalization


O Servidor Já está pronto, salve, compile-o e mantenha-o aberto.

-----------

O Cliente...
O Cliente é a chave fundamental para a atualização, é ele quem verifica e faz o pedido do que tem que ser atualizado de acordo com a versão do arquivo.

Para o Cliente, utilizaremos os seguintes componentes:
2* Panels
1* Memo
1* Button
1* TidTCPClient
1* TidAntiFreeze

Agora, esta é a parte mais trabalhosa, pois há bastante código.

*Defina o nome do formulário de: "FormPrincipalAtualizador"

*Crie duas Variáveis Globais (Elas servirão para armazenar o local do executavel e se já foi atualizado ou não.):

  Atualizado: Boolean;
  Local: String;
 

*Altere a propriedade name do Memo1 para: "mmo1" coloque-o dentro de um panel, com a propriedade align do panel como alClient

*Defina o caption do Button1 para "Atualizar", coloque-o dentro de um panel. com a propriedade align do panel para alBottom

*Defina a propriedade name do idTCPClient1 para "Cliente", e também troque a propriedade Port para 34005

**Ambos os panels separados dentro do form... e não um dentro do outro.

O formulário tera de ficar mais ou menos assim:


---
Para que consigamos a versão do executável, temos que utilizar uma função; Coloque esta função em baixo do {$R *.DFM}

function GetVersion(Arquivo:String): string;
var
  VerInfoSize: DWORD;
  VerInfo: Pointer;
  VerValueSize: DWORD;
  VerValue: PVSFixedFileInfo;
  Dummy: DWORD;
begin
  Result := ``;
  VerInfoSize := GetFileVersionInfoSize(PChar(Arquivo), Dummy);
  if VerInfoSize = 0 then Exit;
  GetMem(VerInfo, VerInfoSize);
  GetFileVersionInfo(PChar(Arquivo), 0, VerInfoSize, VerInfo);
  VerQueryValue(VerInfo, `\`, Pointer(VerValue), VerValueSize);
  with VerValue^ do
  begin

    Result := IntToStr(dwFileVersionMS shr 16);
    Result := Result + `.` + IntToStr(dwFileVersionMS and $FFFF);
    Result := Result + `.` + IntToStr(dwFileVersionLS shr 16);
    Result := Result + `.` + IntToStr(dwFileVersionLS and $FFFF);
  end;
  FreeMem(VerInfo, VerInfoSize);
end;

Esta função retorna a versão de um arquivo no formato X.X.X.X... EX: 1.0.0.1
Será de muita importância para a atualização correta dos arquivos.

Adicione estas funções extras na seção Private

Procedure TFormPrincipalAtualizador.AdicionaLog(Linha:String);
Begin
  mmo1.Lines.add( DateToStr(now)+` `+ TimeToStr(Now) + ` :: ` + Linha );
  Application.ProcessMessages;
end;

Function TFormPrincipalAtualizador.VerificaVersao(Internet, Local:String):Boolean;
var
  Buffer : String;
  I : Integer;
  NET : array [1..4] of integer;
  LOC : array [1..4] of integer;
Begin
  Buffer:= Internet+`.`;//Define a variavel para a versao da internet
  for I:=1 to 4 do begin
    NET[I]:= StrToInt( Copy(Buffer, 1, pos(`.`,Buffer)-1 ) ); //copia o  numero até o ponto
    Delete(Buffer, 1, pos(`.`,Buffer)); //excui até o ponto para pegar o próximo
  end;
  Buffer:= Local+`.`;//Define a variavel para a versao Local
  for I:=1 to 4 do begin
    LOC[I]:= StrToInt( Copy(Buffer, 1, pos(`.`,Buffer)-1 ) ); //copia o numero até o ponto
    Delete(Buffer, 1, pos(`.`,Buffer)); //excui até o ponto para pegar o próximo
  end;
  Result:= (NET[1] > LOC[1]) or (NET[2] > LOC[2]) or (NET[3] > LOC[3]) or (NET[4] > LOC[4]);
end;

Esta função testa se há alguma versão maior... e retorna True caso haja ou False caso contrário.

Agora, vá na ação OnClick do Button1 e coloque o seguinte código:

if
Atualizado then begin
  Cliente.Disconnect;
  Close;
end else begin
  Button1.Enabled:=False;
  InicializarAtualizacao;
  Button1.Caption:=`Fechar`;
end;

Isto fará com que a atualização seja iniciada e após ela libere o botão e faça sair.

Na ação OnWorkBegin do Cliente coloque o seguinte código:

AdicionaLog(`Baixando arquivo. Tamanho`+inttostr(AWorkCountMax div 1024)+`KB`);

---
Agora, antes do End., coloque este código, ele fará com que a variável "Local" guarde o local do aplicativo.

initialization
  Local:=ExtractFilePath(Application.ExeName);

finalization

---
OK! Agora falta a mais importante de todas!!!
O Procedimento que atualiza.
Declare ele na seção Private.

Procedure TFormPrincipalAtualizador.InicializarAtualizacao;
  procedure HabilitaCampos;
  Begin
    Atualizado:=True;
    Button1.Enabled:=True;
  end;
var

  Stream : TMemoryStream;
  Versoes : TStrings;
  I : Integer;
  Arquivo, Versao: String;
begin
  AdicionaLog(`Iniciando Atualização`);
  try
    AdicionaLog(`Tentando Conexão com:`+Cliente.Host);
    Cliente.Connect; //Tenta se conectar
    AdicionaLog(Cliente.ReadLn);
    AdicionaLog(`Conexão estabelecida com sucesso`);
  except
    on
e: Exception do begin
      AdicionaLog(`Falha ao se conectar com o servidor de atualização.`);
      AdicionaLog(`ERRO: "`+e.Message+`"`);
      HabilitaCampos;
      Exit;
    end;
  end;


  //A conexão obteve sucesso.
  AdicionaLog(`Pedindo Arquivo de versões...`);
  with Cliente do begin
    Versoes:= TStringList.Create;
    Stream:= TMemoryStream.Create;
    WriteLn(`arquivoversoes `); //manda o comando para o servidor. (deixe um espaço após o texto.)

    Stream.size:=StrToInt( ReadLn ); //Lê o tamanho que o servidor enviou.
    ReadStream(Stream, Stream.size, False); //Lê a stream que o servidor enviou.
    Stream.Position:=0; //manda a memória do Stream para o início

    If Stream.Memory = nil then begin //se estiver zerado deu erro.
      AdicionaLog(`Falha ao transferir a lista de versões.`);
      HabilitaCampos;
      Exit;
    end;

    Versoes.LoadFromStream(Stream); //Faz as linhas de versões lerem
    AdicionaLog(`Lista de versões transferida com sucesso.`);
  end;
  AdicionaLog(`Verificando Versões...`);

  For I:= 0 to Versoes.Count-1 do begin
    Arquivo:= copy(Versoes.Strings[I], 1, pos(`=`,Versoes.Strings[I])-1);
    Versao:= copy(Versoes.Strings[I], pos(`=`,Versoes.Strings[I])+1, length(Versoes.Strings[I]));
    if FileExists(Local + Arquivo) then
      if VerificaVersao(Versao, GetVersion(Local + Arquivo)) then begin
        AdicionaLog(`Versão `+GetVersion(Local + Arquivo)+` do Arquivo:"`+extractfilename(Arquivo)+`"`+` será atualizada para: `+versao);
        AdicionaLog(`Requisitando arquivo ao servidor...`);

        with Cliente do begin
          WriteLn(`get `+Arquivo);
          Stream.size:=StrToInt( ReadLn ); //Lê o tamanho que o servidor enviou.
          ReadStream(Stream, Stream.size, False); //Lê a stream que o servidor enviou.
          Stream.Position:=0; //manda a memória do Stream para o início
          Stream.SaveToFile(Local + Arquivo);
          AdicionaLog(`Download do arquivo: `+Arquivo+` Concluído.`);
        end;
      end;//if verifica....

 
AdicionaLog(`Atualização Terminada.`);
  end;
  HabilitaCampos;
end;

Este procedimento, manda um comando de requisição de versões para o servidor, após receber, ele percorre a lista de versões e verifica os seus respectivos arquivos e suas versões, caso sejam inferior, ele requisita um novo arquivo para o servidor.
Também, caso o arquivo não exista no diretório onde está o atualizador, ele não executa nada.

Pronto!
Agora uma imagem dos dois funcionando.


Está concluído o nosso Atualizador!

Um forte abraço, até a próxima!