Обновление ПО через Инет - простой пример.
От: 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=-]
Re: Обновление ПО через Инет - простой пример.
От: Dimonka Верблюд  
Дата: 20.10.09 08:03
Оценка:
Здравствуйте, DarkMaster, Вы писали:

DM>Если кому будет нужна реализация обновления ПО через Интернет — привожу код простого класса для обновления. Как всегда рекомендации и ругань — приветсвуются .


Спасибо за труд и за то, что делишься наработками!
Начну с вопросов:
— Неужели запущенный экзешник может сам себя переименовать без проблем?
— Даже если сможет, заработает ли это на Win Vista?
Ругань:
— Немного коробит глаза оформление кода по типу "var S1,TmpFileName:string;", читать такой код чуть сложнее, чем код с пробелами и переносами.

В остальном — надо попробовать. Ещё раз спасибо!
Re[2]: Обновление ПО через Инет - простой пример.
От: DarkMaster Украина http://www.bdslib.at.ua
Дата: 20.10.09 11:38
Оценка:
Здравствуйте, Dimonka, Вы писали:


DM>>Если кому будет нужна реализация обновления ПО через Интернет — привожу код простого класса для обновления. Как всегда рекомендации и ругань — приветсвуются .


D>Спасибо за труд и за то, что делишься наработками!

D>- Неужели запущенный экзешник может сам себя переименовать без проблем?

W2K и выше — может. Для Win9x — нет.

D>- Даже если сможет, заработает ли это на Win Vista?

Я под Вистой и сижу. Могут быть проблемы, если программа установлена в "Program Files" к примеру — там начинаются пляски с бубном вокруг прав доступа. Решается диалогом раздачи прав в свойствах папки.

D>Ругань:

D>- Немного коробит глаза оформление кода по типу "var S1,TmpFileName:string;", читать такой код чуть сложнее, чем код с пробелами и переносами.

Упс... Ну забываю я через "облагораживатель" код пропустить
WBR, Dmitry Beloshistov AKA [-=BDS=-]
Re[3]: Обновление ПО через Инет - простой пример.
От: Dimonka Верблюд  
Дата: 22.10.09 08:46
Оценка:
Здравствуйте, DarkMaster, Вы писали:

D>>- Неужели запущенный экзешник может сам себя переименовать без проблем?


DM>W2K и выше — может. Для Win9x — нет.


D>>- Даже если сможет, заработает ли это на Win Vista?

DM>Я под Вистой и сижу. Могут быть проблемы, если программа установлена в "Program Files" к примеру — там начинаются пляски с бубном вокруг прав доступа. Решается диалогом раздачи прав в свойствах папки.

Но с точки зрения простого пользователя, как весь этот процесс будет выглядеть? Можно ли его автоматизировать, не принуждая пользователя делать телодвижения?
Re[4]: Обновление ПО через Инет - простой пример.
От: DarkMaster Украина http://www.bdslib.at.ua
Дата: 22.10.09 09:37
Оценка:
Здравствуйте, Dimonka, Вы писали:

D>>>- Даже если сможет, заработает ли это на Win Vista?

DM>>Я под Вистой и сижу. Могут быть проблемы, если программа установлена в "Program Files" к примеру — там начинаются пляски с бубном вокруг прав доступа. Решается диалогом раздачи прав в свойствах папки.

D>Но с точки зрения простого пользователя, как весь этот процесс будет выглядеть? Можно ли его автоматизировать, не принуждая пользователя делать телодвижения?


CACLS для Vista,ICACLS для W7 — запускаем в скрытом виде через CreateProcess() и вуаля — все права уже розданы Ну и несколько дополнительных телодвижений, если аккаунтов больше. Конечно, это — решение в лоб, и можно понапридумывать еще много чего. В WinAPI форуме об этом наверное более подробно расскажут.
WBR, Dmitry Beloshistov AKA [-=BDS=-]
Re: Обновление ПО через Инет - простой пример.
От: AC1D  
Дата: 10.11.09 11:21
Оценка:
Здравствуйте, DarkMaster, Вы писали:

DM>Если кому будет нужна реализация обновления ПО через Интернет — привожу код простого класса для обновления. Как всегда рекомендации и ругань — приветсвуются .


Единственное чего тут нету, так это Event'a, который возравщает размер скачаного файла.

procedure OnDownloadProgress(Position:Long); //что то типа


Используй функции InternetOpen(), InternetSetFilePointer(), InternetReadFile().
Они позволят узнать размер файла и докачать его, если нужно.
Естественно класс немного усложниться.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: Обновление ПО через Инет - простой пример.
От: DarkMaster Украина http://www.bdslib.at.ua
Дата: 11.11.09 08:40
Оценка:
Здравствуйте, AC1D, Вы писали:

ACD>Единственное чего тут нету, так это Event'a, который возравщает размер скачаного файла.


ACD>
ACD>procedure OnDownloadProgress(Position:Long); //что то типа
ACD>


ACD>Используй функции InternetOpen(), InternetSetFilePointer(), InternetReadFile().

ACD>Они позволят узнать размер файла и докачать его, если нужно.
ACD>Естественно класс немного усложниться.

Ну не знаю, усложнение вообще-то не совсем маленькое, т.к. помимо прочего придется хранить список обновлений со статусом успешности (закачано+установлено, недокачано, вообще не качалось) и хранить промежуточные (недокачанные) файлы обновлений.
WBR, Dmitry Beloshistov AKA [-=BDS=-]
Re: Обновление ПО через Инет - простой пример.
От: De-Bugger  
Дата: 17.11.09 08:49
Оценка: 2 (1) +1
Здравствуйте, DarkMaster, Вы писали:

DM>Если кому будет нужна реализация обновления ПО через Интернет — привожу код простого класса для обновления. Как всегда рекомендации и ругань — приветсвуются .

DM>В принципе ничего не мешает оформить этот класс как компонент или сделать классу обвязку.

Недурно, недурно.

Маленькие замечания.

DM> // Если версия не такая, как у нас => есть обновления

DM> Result:=(S1<>Trim(FProductVersion));
Вот тут лучше не полениться и сделать проверку на "больше" частей версии Major, Minor, Build.
Всякое может случиться даунгрейд же ведь нам не нужен

DM>File=http://www.myhomesite.com/updates/MyProgram_12_setup.exe


Вот этот пункт на любителя, в нашем продукте тут URL не к файлу, а к WEB-страничке загрузки.

Плюсы:
1. СУЩЕСТВЕННО упрощается код. Чем проще код, тем меньше ошибок, т.к. в вызове ShellExecute с открытием URL в дефолтном браузере надо постараться сделать что то не так. В вашем же коде при закачке файла, куча условностей со временными директориями, путями загрузки, агрессивно настроенным UAC... вообще можно перечислять до бесконечности.
2. Пользователь лишний раз "увидит" сайт, прочитает что нового в этой версии, вероятно найдет новые продукты и т.п.
3. Трафик. Следствие п.2 тоже неплох для того же бегуна или адсенса.
Re[2]: Обновление ПО через Инет - простой пример.
От: DarkMaster Украина http://www.bdslib.at.ua
Дата: 17.11.09 09:17
Оценка:
Здравствуйте, De-Bugger, Вы писали:

DM>> // Если версия не такая, как у нас => есть обновления

DM>> Result:=(S1<>Trim(FProductVersion));
DB>Вот тут лучше не полениться и сделать проверку на "больше" частей версии Major, Minor, Build.
DB>Всякое может случиться даунгрейд же ведь нам не нужен

В принципе можно в OnUpdateVersionInfo() что-то свое намутить... К тому же обозначение для версии — кто в лес кто по дрова и универсального правила для разбора строки версии я навскидку не придумаю.

DM>>File=http://www.myhomesite.com/updates/MyProgram_12_setup.exe


DB>Вот этот пункт на любителя, в нашем продукте тут URL не к файлу, а к WEB-страничке загрузки.




DB>Плюсы:

DB>1. СУЩЕСТВЕННО упрощается код. Чем проще код, тем меньше ошибок, т.к. в вызове ShellExecute с открытием URL в дефолтном браузере надо постараться сделать что то не так. В вашем же коде при закачке файла, куча условностей со временными директориями, путями загрузки, агрессивно настроенным UAC... вообще можно перечислять до бесконечности.

А как вы узнаете, что версия поменялась? Или сразу отправляете на свою страничку закачки, а дальше пусть пользователь сам разбирается?

DB>2. Пользователь лишний раз "увидит" сайт, прочитает что нового в этой версии, вероятно найдет новые продукты и т.п.

DB>3. Трафик. Следствие п.2 тоже неплох для того же бегуна или адсенса.

Учту.
WBR, Dmitry Beloshistov AKA [-=BDS=-]
Re[3]: Обновление ПО через Инет - простой пример.
От: Dimonka Верблюд  
Дата: 17.11.09 09:53
Оценка: 1 (1)
Здравствуйте, DarkMaster, Вы писали:

DM>


DB>>Плюсы:

DB>>1. СУЩЕСТВЕННО упрощается код. Чем проще код, тем меньше ошибок, т.к. в вызове ShellExecute с открытием URL в дефолтном браузере надо постараться сделать что то не так. В вашем же коде при закачке файла, куча условностей со временными директориями, путями загрузки, агрессивно настроенным UAC... вообще можно перечислять до бесконечности.

DM>А как вы узнаете, что версия поменялась? Или сразу отправляете на свою страничку закачки, а дальше пусть пользователь сам разбирается?


Мы решили xml выкладывать, в котором чётко писать номер версии, что нового, линк итд. Качаешь xml и сразу всё понятно. Но линк пока тоже даём на страницу.
Re[3]: Обновление ПО через Инет - простой пример.
От: De-Bugger  
Дата: 17.11.09 10:30
Оценка:
Здравствуйте, DarkMaster, Вы писали:

DM>А как вы узнаете, что версия поменялась? Или сразу отправляете на свою страничку закачки, а дальше пусть пользователь сам разбирается?


На сервере лежит XML. В ней перечень продуктов, номера последний версий, что изменилось с предыдущего билда и ссылка на Download соответсвующего продукта.
Re[4]: Обновление ПО через Инет - простой пример.
От: Аноним  
Дата: 22.01.10 11:34
Оценка:
D>Мы решили xml выкладывать, в котором чётко писать номер версии, что нового, линк итд. Качаешь xml и сразу всё
понятно. Но линк пока тоже даём на страницу.

У нас в программе очень часто обновляется база данных небольшая (xml небольшая)
Вот как решаем проблему с обновлениями.

На сервере лежит xml в котором хранится информация

версия базы
список изменений
последняя версия программы
список изменений

Когда программа лезит в интернет, заодно и скачивает в фоне файл update.xml
Этот файл парсится и если база устарела, то скачивается последняя версия базы в отдельном потоке (пользователи заинтересованы в новой базе, почти как в обновлениях антивируса)
Заодно сравнивается версия программы из update.xml с версией программы и если она устарела, то выводим сообщение о выходе новой версии, список изменений и предложением скачать обновление.

В итоге пользователь сам принимает решение нужна ли ему новая версия, на основе данных об изменениях. И если принимает решение скачать попадает на страницу закачки.
Плюсы направления на сайт, а не на файл сразу — пользователь может узнать о новых продуктах и заодно на рекламу может кликнет))).

Еще одной фишкой является показ информации об обновлениях на том языке, который выбран в программе)))

П.С.
В дикой молодости для обновления одной фриварки на сайте лежал текстовый файл с содержимым в виде 123456
Программа тупым GET запросом получала эти циферки и сравнивала их с числом, прошитым в программе. Если число в программе меньше, то вышла новая версия.
12 — мажорная версия
34 — минорная версия
56 — номер билда

получаем что число для новой версии всегда > предыдущей
Re[4]: Обновление ПО через Инет - простой пример.
От: icezone  
Дата: 26.02.10 16:58
Оценка:
Здравствуйте, De-Bugger, Вы писали:

DB>На сервере лежит XML. В ней перечень продуктов, номера последний версий, что изменилось с предыдущего билда и ссылка на Download соответсвующего продукта.


А нафига отдельный XML если на сервере есть PAD в формате XML, в котором уже есть номер версии, список изменений
и ссылка на инсталлятор?
Re: Обновление ПО через Инет - простой пример.
От: Аноним  
Дата: 22.05.10 16:51
Оценка:
Здравствуйте, DarkMaster

А подобного кода но для c++ нету?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.