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:
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!