Update programu

chmolu

Fajnie byłoby, aby program miał możliwość sprawdzenia, czy wyszła jego nowsza wersja. Zaprezentuję tutaj prosty sposób wg mojego pomysłu.

Potrzebne będą nam: konto na jakimś serwerze i komponent HTTPGet. Nie wiem, czy jest on na tej stronie, ale zamieszczam go do przykładowego programu, który znajdziecie w kodach źródłowych.

No więc zaczynamy.
Na początek stwórz plik version.ini. Potrzebny będzie, aby odczytać, czy wyszła nowa wersja programu. Plik będzie wyglądał tak:

[update]
version=1.3
url=http://www.web-page.pl/program.exe

Oczywiście url trzeba dostosować do swojej strony www.
Teraz przechodzimy do Delphi. Wrzuć na formę komponenty: Memo, Button, 2 HTTPGet i ProgressBar.
Wersję można wyciągnąć z zasobów, ale ja zadeklaruję tylko stałą:

const
  wersja = '1.2';

W zdarzeniu OnClick przycisku wpisz:

procedure TForm1.Button1Click(Sender: TObject);
begin
 HTTPGet1.URL := 'http://www.web-page.pl/version.ini';  //lokalizacja pliku z informacją
 HTTPGet1.FileName := ExtractFilePath(Application.ExeName) + 'version.ini'; 
 HTTPGet1.GetFile;  //pobranie pliku ini
 Memo1.Lines.Add('Pobieranie informacji...'); 
end;

Teraz w zdarzeniu onDoneFile komponentu HttpGet1 wpisz:

procedure TForm1.HTTPGet1DoneFile(Sender: TObject; FileName: String;
  FileSize: Integer);
var
 INI : TINIFile;
 updtVersion : string;
 cyfra1 : integer;
 cyfra2 : integer;
 updtCyfr1 : integer;
 updtCyfr2 : integer;
begin
 Memo1.Lines.Add('Sprawdzanie, czy jest nowa wersja');
 INI := TINIFile.Create(HTTPGet1.FileName);
 try
   updtVersion := INI.ReadString('update','version','');  //odcytanie wersji z pliku ini
 
   //nie śmiejcie sie z tego sposobu. To tylko przykład :)
   cyfra1 := StrToInt(wersja[1]);
   cyfra2 := StrToInt(wersja[3]);
   updtCyfr1 := StrToInt(updtVersion[1]);
   updtCyfr2 := StrToInt(updtVersion[3]);
   if (cyfra1 < updtCyfr1) or (cyfra2 < updtCyfr2) then  //sprawdzenie, czy jest nowa wersja
   begin
     Memo1.Lines.Add('Jest nowa wersja programu');
 
{wyświetlenie zapytania. Jeśli użytkownik wybierze Tak, rozpoczyna się pobieranie programu }
     if Application.MessageBox('Czy chcesz pobrać nową wersję programu?','Informacja',MB_YesNo) =
      ID_NO then
      exit
     else
      begin
       HTTPGet2.URL := INI.ReadString('update','url','');
       if SaveDialog1.Execute then  // tu użytkownik wybierze, gdzie zapisać program
         HTTPGet2.FileName := SaveDialog1.FileName;  
       HTTPGet2.GetFile;  //pobieranie pliku
       Memo1.Lines.Add('Pobieranie pliku');
      end;
   end
   else
      Memo1.Lines.Add('Nie znaleziono nowej wersji prormu');
 finally
   INI.Free;
   DeleteFile(HTTPGet1.FileName); //usunięcie pliku ini
 end;
 
end;

Następnie w OnError komponentu HttpGet1:

procedure TForm1.HTTPGet1Error(Sender: TObject);
begin
 Memo1.Lines.Add('Wystąpił błąd');
end;

Następnie w OnDoneFile komponentu HttpGet2:

procedure TForm1.HTTPGet2DoneFile(Sender: TObject; FileName: String;
  FileSize: Integer);
begin
 Memo1.Lines.Add('Pobieranie zakończone sukcesem');
end;

Teraz w OnError HttpGet2:

procedure TForm1.HTTPGet2Error(Sender: TObject);
begin
  Memo1.Lines.Add('Wystąpił błąd podczas pobierania pliku');
end;

No i na koniec w onProgress komponentu HttpGet2:

procedure TForm1.HTTPGet2Progress(Sender: TObject; TotalSize,
  Readed: Integer);
begin
  ProgressBar1.Max := TotalSize;
  ProgressBar1.Position := Readed;
end;

Teraz wystarczy tylko wrzucić plik version.ini na serwer.
To był oczywiście najprostszy sposób na uaktualnienie swojego programu, ale wystarczy troszkę wyobraźni i
rozbudowanie tego kodu nie będzie problemem.

pozdrawiam:
Piotrek Chmolowski

Kod przykładowego programu możecie pobrać tutaj:

http://4programmers.net/download.html?id=1036

30 komentarzy

Wklejam kod funkcji, która sprawdza czy jest nowa wersja programu bazując na dwóch plikach:

function TMainForm.IsNewVesrion(const VersionFileName, UpdateFileName: ShortString): Boolean;
var
{ZMIENNE PLIKOWE}
iniVersion, iniUpdate: TINIFile;
{ZMIENNA LICZNIKOWA}
I: Byte;
begin
{DOMYŚLNY REZULTAT FUNKCJI}
Result := False;

try
{TWORZENIE ZMIENNYCH PLIKOWYCH}
iniVersion := TINIFile.Create(VersionFileName);
iniUpdate := TINIFile.Create(UpdateFileName);

{SPRAWDZANIE POSZCZEGÓLNYCH LICZB WERSJI}
for I := 0 to 3 do
  if iniUpdate.ReadInteger('CURRENT_VERSION', IntToStr(I), 0) > iniVersion.ReadInteger('CURRENT_VERSION', IntToStr(I), 0) then
    begin
      {JEŚLI JEST WYŻSZA LICZBA WERSJI ZWRÓĆ TRUE}
      Result := True;
      Break;
    end;

finally
{USUŃ ZMIENNE PLIKOWE Z PAMIĘCI}
FreeAndNil(iniVersion);
FreeAndNil(iniUpdate);
end;
end;

Budowa plików jest prosta. Oto plik Version.ini - są w nim informacje na temat aktualnie posiadanej wersji programu:

[CURRENT_VERSION]
0=1
1=0
2=0
3=154

A to zawartość pliku Update.ini - jest to plik pobrany z sieci przechowujący informacje na temat najnowszej wersji programu:

[CURRENT_VERSION]
0=1
1=0
2=0
3=157

Jak widać jest w nim młodsza wersja programu, stąd funkcja zwróci prawdę. Algorytm nie myli się i nalezy go używać tuż po pobraniu pliku wersji z Internetu. Pozdrawiam.

Czy mógł by ktoś zamieścić kod żródłowy tego programu , ponieważ niewiem dlaczego ale wywala mi błąd podczas kliknięcia na button .
bład jest w tej linijce :

updtCyfr1 := StrToInt(updtVersion[1]);

kod programu :

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IniFiles, XPMan, ComCtrls, StdCtrls, HTTPGet;

type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
ProgressBar1: TProgressBar;
XPManifest1: TXPManifest;
SaveDialog1: TSaveDialog;
HTTPGet1: THTTPGet;
HTTPGet2: THTTPGet;
procedure Button1Click(Sender: TObject);
procedure HTTPGet1DoneFile(Sender: TObject; FileName: String;
FileSize: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;

const
wersja = '1.2';

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
HTTPGet1.URL := 'http://www.pm2a.yoyo.pl/updater/version.ini'; //lokalizacja pliku z informacją
HTTPGet1.FileName := ExtractFilePath(Application.ExeName) + 'version.ini';
HTTPGet1.GetFile; //pobranie pliku ini
Memo1.Lines.Add('Pobieranie informacji...');
end;

procedure TForm1.HTTPGet1DoneFile(Sender: TObject; FileName: String;
FileSize: Integer);

var
INI : TINIFile;
updtVersion : string;
cyfra1 : integer;
cyfra2 : integer;
updtCyfr1 : integer;
updtCyfr2 : integer;
begin
Memo1.Lines.Add('Sprawdzanie, czy jest nowa wersja');
INI := TINIFile.Create(HTTPGet1.FileName);
try
updtVersion := INI.ReadString('update','version',''); //odcytanie wersji z pliku ini

//nie śmiejcie sie z tego sposobu. To tylko przykład :)
cyfra1 := StrToInt(wersja[1]);
cyfra2 := StrToInt(wersja[3]);
updtCyfr1 := StrToInt(updtVersion[1]);
updtCyfr2 := StrToInt(updtVersion[3]);
if (cyfra1 < updtCyfr1) or (cyfra2 < updtCyfr2) then //sprawdzenie, czy jest nowa wersja
begin
Memo1.Lines.Add('Jest nowa wersja programu');

{wyświetlenie zapytania. Jeśli użytkownik wybierze Tak, rozpoczyna się pobieranie programu }
if Application.MessageBox('Czy chcesz pobrać nową wersję programu?','Informacja',MB_YesNo) =
ID_NO then
exit
else
begin
HTTPGet2.URL := INI.ReadString('update','url','');
if SaveDialog1.Execute then // tu użytkownik wybierze, gdzie zapisać program
HTTPGet2.FileName := SaveDialog1.FileName;
HTTPGet2.GetFile; //pobieranie pliku
Memo1.Lines.Add('Pobieranie pliku');
end;
end
else
Memo1.Lines.Add('Nie znaleziono nowej wersji prormu');
finally
INI.Free;
DeleteFile(HTTPGet1.FileName); //usunięcie pliku ini
end;
end;

end.

Co w nim jest nie tak ??

bordeux, nie dodałeś komponentu SaveDialog na formatkę.

Mam jedną uwagę dot. linijki 21 z procedury TForm1.HTTPGet1DoneFile. Tam jest:

if (cyfra1 < updtCyfr1) or (cyfra2 < updtCyfr2) then ...

ale jeśli program będzie np. w wersji 2.1 a ktoś nieszczęśliwie zapomni zaktualizować pliku na serwerze i tam będzie podane 1.9 to zacznie się nie upgrade, tylko DOWNGRADE (bo w tym wypadku cyfra2 > updtCyfr2)! To jest sytuacja wyjątkowa spowodowana przez osobę opiekującą się serwerem, ale pamięć ludzka jest zawodna więc proponuję takie coś:

if (cyfra1 < updtCyfr1) or ((cyfra1 = updtCyfr1) and (cyfra2 < updtCyfr2)) then ...

chyba będzie działać, ale nie jestem pewny bo dopiero zaczynam przygodę z Delphi

Mam mały problem w use występuje mi błąd [Fatal Error] Unit1.pas(7): File not found: 'HTTPGet.dcu' i zaznacza mi się HTTPGet prosze poradzcie mi co mam z tym zrobić [???]

Zapraszam na moją stronę www.programistakuty.pl. Tam prezentuje mój darmowy program (bardzo łatwo konfigurowalny) do aktualizacji programu. Prezentuje tam wykorzystanie go w delphi oraz dołączam przykładowy projekt. Strona artykułu http://programistakuty.pl/kuty-updater/

BTW: A poza tym algorytm nie jest w pełni poprawny, by jeżeli wersja aplikacji wzrośnie do 10, program odczyta go jako 1...

Metoda z liczbami jest uniwersalna, na łańcuchach niestety nie...

Witajcie. Dlaczego wersję programu odczytujecie jako łańcuch? Przecież klasa TINIFile posiada możliwość odczytywania zawartości kluczy jako liczb metodą INI.ReadInteger... Zamiast kombinować z konwertowaniem tekstu na liczby wystarczy podzielić numer wersji na bloki i je porównywać (zawartość pliku version.ini):

[CURRENT_VERSION]
Major=1
Minor=0
Release=0
Build=2043

Teraz pobrać do odpowiednich zmiennych te liczby i porównać. Można by było zapisać je do tablicy i jedną pętlą sprawdzić czy wersja aplikacji w pliku version.ini jest nowsza niż tej na dysku. No ale trzeba by było trochę pogłówkować =)

Kodzik ciekawy, mi się przyda niedługo :)

na yoyo żeby ściągać plik przez HTTP nigdzie się nie musisz logować, nie wiem skąd to wziąłeś

nav, pisałem o tym trochę niżej...

a znacie może jakaś dobra stronkę gdzie by założyć konto ?? bo na yoyo trzeba sie zalogować żeby ściągnąć plik na cba.pl i republice tez :(((

Zawsze można zrobić tak że na serwerze jest plik np. nazwa_numer.wersji i przy odpalaniu program próbuje pobrać ten plik jeśli go nie ma na serwerze, to nie ma aktualizacji wydaje mi się za bardzo prymitywna wersja jest chyba najlepsza :]

a mi w tym miejscu kodu podkreśla błąd... o co chodzi??
INI : TINIFile;

Undeclared identifer: 'TINIfile'

używam delphi 7 personal

uprałem sie z problemem xD jak ktoś nie wie to do uses trzeba dopisać IniFiles

Teraz juz wiem..... moglem najpierw pomyslec, a pozniej dodac komentarz :)

A mi wyskakuje bląd cos SaveDialog1 undeclared? co to znaczy??

wyskakuje blad httpget, bo nie masz tego komponentu zainstalowanego [ot, caly problem]

Wpisz INIFiles do uses

Ja mam problem...[Error] Unit2.pas(53): Undeclared identifier: 'TINIFile'

Wiecie dlaczego?

Swoją drogą, co wy tak macie z tym HTTPGet ? ;) Ja tam wolę stare dobre UrlDownloadToFile, bo mozna go zastosować wszędzie, również programując w API. I dzięki temu mój programik Downloader nie musi mieć formy w dfm ani classes w uses :P

dzieki. moze sie przydac :)
a wiecie ze ja znalazlem gdzies w sieci opis uzycia OpenDialog. Slowko w slowko to samo ;) Musieli zerznac z ksiazki Adama B. ;)

prawie to samo jest w kompendium programisty d7 Adama... ;]

Zgadzam sie z Wolverinem, na taki sam pomysl wpadlem podczas pisania swojego programu. Bo najlatwiej go zrealizowac :P

Dobre do czasu, do kiedy nie usuna Ci konta (na bezplatnym serwerze) :D

hej nie bulwersuj sie, na taka metode wpada sie po 5 sekundach myslenia o update'ach programu. Poza tym nawet ja nie pisze "sluwko"

zerżnołeś albo ktoś zerżną z ciebie słuwko w słuwko znalazłem to na koder.prv.pl
// o mamo, ale byki... - ŁF

ja nie mowie ze zerznales..mowie tylko ze kod adama to 1/3 tego

Nawet niezłe, ale powien Ci co ja zrobiłem : na serwerku walnąłem plik textowy w takim formacie :

NAZWAPLIKU;URL_DO_NIEGO

W ten sposób można zamieścić nie tylko jedną wersję, ale np. uaktualnienia do bibliotek (jeśli ich używamy), starsze wersje programu itp...

Przecież mój sposób bardzo łatwo rozbudować.
Wystarczy jakiś pomysł. Można np. dać drugi link do pliku z informacją co nowego w programie.

zapewniam Cię, lofix, że tej książki nie widziałem na oczy.

TO samo (tzn podobny sposob, tyle ze prostszy) znajduje sie w ksiazce Adama (D7cwzaw) ciekawe.. :/

Taki drobny problem - 1.9 będzie nowsza niż 2.1 :-) Lepiej skonwertować na floata.