Desenvolvendo um Atualizador de arquivos.
Desenvolvendo um atualizador de arquivos por versão via protocolo TCP, com componentes nativos.
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!
Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Artigo