Обновление ПО через Инет - простой пример.
От: DarkMaster Украина http://www.bdslib.at.ua
Дата: 16.10.09 12:04
Оценка: 28 (6)
Если кому будет нужна реализация обновления ПО через Интернет — привожу код простого класса для обновления. Как всегда рекомендации и ругань — приветсвуются .
В принципе ничего не мешает оформить этот класс как компонент или сделать классу обвязку.

unit BDSInetUpdater;

interface
Uses Classes,UrlMon,Windows,IniFiles;

type
  // событие, сигнализирующее о том, что есть обновленная версия
  TOnStartUpdate=procedure (Sender:TObject; var StartUpdate:boolean) of object;
  // событие при проверке обновленнной версии
  TOnAddUpdateInfoCheck=procedure (Sender:TObject; Inf:TIniFile; var CheckResult,ContinueCheck:boolean;
                                   var DownloadURL:string) of object;
  // событие при проверке версии
  TOnUpdateVersionInfo=procedure (Sender:TObject; Version:string) of object;

  TInetUpdater=class
  private
    FUpdateInfoFile: string;
    FUpdateResultFile: string;
    FOnUpdateComplete: TNotifyEvent;
    FProductVersion:string;
    FProductName: string;
    FOnUpdateExists: TNotifyEvent;
    FOnStartUpdate: TOnStartUpdate;
    FOnAddUpInfoCheck: TOnAddUpdateInfoCheck;
    FOnUpdateVersionInfo: TOnUpdateVersionInfo;
    FInetResult:HRESULT;
  public
    Constructor Create; virtual;
    Destructor Destroy; override;
    // проверить обнвления и обновится при возможности
    function UpdateFile:boolean; virtual;
    // только проверить обновления
    function CheckUpdate(var DownloadURL:string):boolean; virtual;
    // получение кода при операциях с Инетом для разбора полетов
    property InetResult:HRESULT read FInetResult;
    // ссылка на файл с информацией об обновлениях
    property UpdateInfoFileURL:string read FUpdateInfoFile write FUpdateInfoFile;
    // временный файл для записи загруженного обновленного файла
    property UpdateResultFile:string read FUpdateResultFile write FUpdateResultFile;
    // наименование продукта
    property ProductName:string read FProductName write FProductName;
    // версия
    property ProductVersion:string read FProductVersion write FProductVersion;
    // событие, которое позволяет проверить дополнительныую информацию, используется для пользовательской проверки
    property OnAddUpdateInfoCheck:TOnAddUpdateInfoCheck read FOnAddUpInfoCheck write FOnAddUpInfoCheck;
    // событие при старте обновлений
    property OnUpdateStart:TOnStartUpdate read FOnStartUpdate write FOnStartUpdate;
    // событие при завершении обновления
    property OnUpdateComplete:TNotifyEvent read FOnUpdateComplete write FOnUpdateComplete;
    // событие о существовании обновления (т.е. информация об обновлениях загружена и новая версия найдена) 
    property OnUpdateExists:TNotifyEvent read FOnUpdateExists write FOnUpdateExists;
    // событие при проверке версии
    property OnUpdateVersionInfo:TOnUpdateVersionInfo read FOnUpdateVersionInfo write FOnUpdateVersionInfo;
  end;


implementation
Uses SysUtils;

// получить временное имя файла (тут возможна оптимизация, вместо времени можно например использовать GetTickCount())
Function GetTmpFileName:String;
begin
 Result:=FormatDateTime('ddmmyyyyhhmmss',Now)+'.tmp';
end;

{ TInetUpdater }

function TInetUpdater.CheckUpdate(var DownloadURL:string): boolean;
var S1,TmpFileName:string;
    Res:HRESULT;
    Inf:TIniFile;
    CheckResult,ContinueCheck:boolean;
begin
  Result:=False;
  // проверим, есть ли адрес для файла с информацией об обновлениях
  if Trim(FUpdateInfoFile)='' then exit;
  // будем файл с информацией сохранять во временный файл
  TmpFileName:=ExtractFilePath(ParamStr(0))+ChangeFileExt(GetTmpFileName,'.ini');
  // пытаемся получить информацию об обновлениях 
  FInetResult:=UrlDownloadToFile(nil,PChar(FUpdateInfoFile),PChar(TmpFileName),0,nil);
  if FInetResult=S_OK then
     begin
       // ОК, файл с информацией получили, пробуем проверить новую версию
       if Assigned(FOnUpdateExists) then FOnUpdateExists(Self);     
       // предполагается, что файл с информацией об обновлениях представляет собой обычный INI файл
       Inf:=TIniFile.Create(TmpFileName);
       try
         CheckResult:=False; ContinueCheck:=True;
         // здесь мы должны проверить информацию о версии, вызывая обработчик для OnAddUpInfoCheck().
         // если ContinueCheck TRUE  мы должны проверить версию сами в этом классе 
         //(в ином случае получим результат в переменной CheckResult и не будем проверять версию самостоятельно) 
         // Если файл с информацией НЕ INI файл - можем использовать свойство Inf.FileName 
         // для получения имени временного файла и попробовать получить данные другим методом 
         // в обработчике OnAddUpInfoCheck()
         if Assigned(FOnAddUpInfoCheck) then
            FOnAddUpInfoCheck(Self,Inf,CheckResult,ContinueCheck,DownloadURL);
         // Проверяем версию самостоятельно
         if ContinueCheck and Inf.SectionExists(FProductName) then
            begin
              // Читаем версию
              S1:=Trim(Inf.ReadString(FProductName,'Version',FProductVersion));
              // событие о получении информации о версии
              if Assigned(FOnUpdateVersionInfo) then
                 FOnUpdateVersionInfo(Self,S1); 
              // Если версия не такая, как у нас => есть обновления
              Result:=(S1<>Trim(FProductVersion));
              // соответсвенно, получим URL для обновленного файла  
              if Result then DownloadURL:=Inf.ReadString(FProductName,'File',DownloadURL);
            end
          else Result:=CheckResult;
       finally Inf.Free; end;
     end;
  // временный файл нам уже не нужен - удаляем его
  if FileExists(TmpFileName) then SysUtils.DeleteFile(TmpFileName);
end;

// первоначальная инициализация
constructor TInetUpdater.Create;
begin
  FUpdateInfoFile:='http://www.websitename.com/updates/verinfo.ini'; // URL к файлу с данными об обвлениях
  FUpdateResultFile:='';      // результирующий файл 
  FProductName:='';           // имя продукта
  FProductVersion:='1.0.0.0'; // версия
  FOnUpdateComplete:=nil;      
  FOnStartUpdate:=nil;
  FOnUpdateExists:=nil;
  FOnAddUpInfoCheck:=nil;
  FOnUpdateVersionInfo:=nil; 
  FInetResult:=S_OK;
end;

destructor TInetUpdater.Destroy;
begin
  inherited;
end;

// собственно обновление 
function TInetUpdater.UpdateFile;
var Flag:boolean;
    DownloadURL:string;
    Res:HRESULT;
begin
  Result:=False;
  // если мы не знаем, в какой файл сохранять обновления - они нам не нужны, уходим
  if Trim(FUpdateResultFile)='' then exit;
  DownloadURL:='';
  // пробуем проверить обновления и получить URL для скачивания
  if CheckUpdate(DownloadURL) then
     begin
       if Trim(DownloadURL)='' then exit;  // обновления есть, но вот URL для скачивания не получили - уходим
       Flag:=True;
       if Assigned(FOnStartUpdate) then FOnStartUpdate(Self,Flag);  // сигнализируем, что начали процесс обновления
                                                                    // в переменной Flag - согласие на закачку ответ пользователя 
       if Flag then     // если закачка разрешена - качаем 
          begin          
            FInetResult:=UrlDownloadToFile(nil,PChar(DownloadURL),PChar(FUpdateResultFile),0,nil);
            Result:=FInetResult=S_OK;
            // если закачка обновления прошла нормально - сигнализируем, что все прошло удачно
            if Result and Assigned(FOnUpdateComplete) then FOnUpdateComplete(Self);
          end;
     end;
end;

end.



Пример использования:

1) размещаем на своем сайте обычный Ini файл updates.ini со следующей информацией:

  [MyProgram]
  Version=1.2
  File=http://www.myhomesite.com/updates/MyProgram_12_setup.exe


2) пишем примерно такой код

Procedure MainForm.DoSelfInetUpdate;
var IU:TInetUpdater;
begin
   IU:=TInetUpdater.Create;
   try
      IU.UpdateInfoFileULR := 'http://www.myhomesite.com/updates/updates.ini';  // где лежит информация об обновлениях
      IU.UpdateResultFile  := ChangeFileExt(ParamStr(0),'.new');                // куда ложить обновленную версию
      IU.ProductName       := 'MyProgram';                                      // имя продукта 
      IU.ProductVersion    := '1.0.0 beta 123';                                 // текущая версия
      if IU.UpdateFile then                                                     // пробуем обновится 
         begin 
            RenameFile(ParamStr(0),ChangeFileExt(ParamStr(0),'.old');
            RenameFile(IU.UpdateResultFile,ParamStr(0));
            ShowMessage('Обновление прошло удачно!');
            // перезапускаемся или что-то еще
            // .....................................................
         end;
   finally IU.Free; end;
end;
WBR, Dmitry Beloshistov AKA [-=BDS=-]
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.