Если кому будет нужна реализация обновления ПО через Интернет — привожу код простого класса для обновления. Как всегда рекомендации и ругань — приветсвуются .
В принципе ничего не мешает оформить этот класс как компонент или сделать классу обвязку.
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 со следующей информацией:
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;
Здравствуйте, DarkMaster, Вы писали:
DM>Если кому будет нужна реализация обновления ПО через Интернет — привожу код простого класса для обновления. Как всегда рекомендации и ругань — приветсвуются .
Спасибо за труд и за то, что делишься наработками!
Начну с вопросов:
— Неужели запущенный экзешник может сам себя переименовать без проблем?
— Даже если сможет, заработает ли это на Win Vista?
Ругань:
— Немного коробит глаза оформление кода по типу "var S1,TmpFileName:string;", читать такой код чуть сложнее, чем код с пробелами и переносами.
DM>>Если кому будет нужна реализация обновления ПО через Интернет — привожу код простого класса для обновления. Как всегда рекомендации и ругань — приветсвуются .
D>Спасибо за труд и за то, что делишься наработками! D>- Неужели запущенный экзешник может сам себя переименовать без проблем?
W2K и выше — может. Для Win9x — нет.
D>- Даже если сможет, заработает ли это на Win Vista?
Я под Вистой и сижу. Могут быть проблемы, если программа установлена в "Program Files" к примеру — там начинаются пляски с бубном вокруг прав доступа. Решается диалогом раздачи прав в свойствах папки.
D>Ругань: D>- Немного коробит глаза оформление кода по типу "var S1,TmpFileName:string;", читать такой код чуть сложнее, чем код с пробелами и переносами.
Упс... Ну забываю я через "облагораживатель" код пропустить
Здравствуйте, DarkMaster, Вы писали:
D>>- Неужели запущенный экзешник может сам себя переименовать без проблем?
DM>W2K и выше — может. Для Win9x — нет.
D>>- Даже если сможет, заработает ли это на Win Vista? DM>Я под Вистой и сижу. Могут быть проблемы, если программа установлена в "Program Files" к примеру — там начинаются пляски с бубном вокруг прав доступа. Решается диалогом раздачи прав в свойствах папки.
Но с точки зрения простого пользователя, как весь этот процесс будет выглядеть? Можно ли его автоматизировать, не принуждая пользователя делать телодвижения?
Здравствуйте, Dimonka, Вы писали:
D>>>- Даже если сможет, заработает ли это на Win Vista? DM>>Я под Вистой и сижу. Могут быть проблемы, если программа установлена в "Program Files" к примеру — там начинаются пляски с бубном вокруг прав доступа. Решается диалогом раздачи прав в свойствах папки.
D>Но с точки зрения простого пользователя, как весь этот процесс будет выглядеть? Можно ли его автоматизировать, не принуждая пользователя делать телодвижения?
CACLS для Vista,ICACLS для W7 — запускаем в скрытом виде через CreateProcess() и вуаля — все права уже розданы Ну и несколько дополнительных телодвижений, если аккаунтов больше. Конечно, это — решение в лоб, и можно понапридумывать еще много чего. В WinAPI форуме об этом наверное более подробно расскажут.
Здравствуйте, DarkMaster, Вы писали:
DM>Если кому будет нужна реализация обновления ПО через Интернет — привожу код простого класса для обновления. Как всегда рекомендации и ругань — приветсвуются .
Единственное чего тут нету, так это Event'a, который возравщает размер скачаного файла.
procedure OnDownloadProgress(Position:Long); //что то типа
Используй функции InternetOpen(), InternetSetFilePointer(), InternetReadFile().
Они позволят узнать размер файла и докачать его, если нужно.
Естественно класс немного усложниться.
Здравствуйте, AC1D, Вы писали:
ACD>Единственное чего тут нету, так это Event'a, который возравщает размер скачаного файла.
ACD>
ACD>procedure OnDownloadProgress(Position:Long); //что то типа
ACD>
ACD>Используй функции InternetOpen(), InternetSetFilePointer(), InternetReadFile(). ACD>Они позволят узнать размер файла и докачать его, если нужно. ACD>Естественно класс немного усложниться.
Ну не знаю, усложнение вообще-то не совсем маленькое, т.к. помимо прочего придется хранить список обновлений со статусом успешности (закачано+установлено, недокачано, вообще не качалось) и хранить промежуточные (недокачанные) файлы обновлений.
Здравствуйте, 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 тоже неплох для того же бегуна или адсенса.
Здравствуйте, 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 тоже неплох для того же бегуна или адсенса.
Здравствуйте, DarkMaster, Вы писали:
DM>
DB>>Плюсы: DB>>1. СУЩЕСТВЕННО упрощается код. Чем проще код, тем меньше ошибок, т.к. в вызове ShellExecute с открытием URL в дефолтном браузере надо постараться сделать что то не так. В вашем же коде при закачке файла, куча условностей со временными директориями, путями загрузки, агрессивно настроенным UAC... вообще можно перечислять до бесконечности.
DM>А как вы узнаете, что версия поменялась? Или сразу отправляете на свою страничку закачки, а дальше пусть пользователь сам разбирается?
Мы решили xml выкладывать, в котором чётко писать номер версии, что нового, линк итд. Качаешь xml и сразу всё понятно. Но линк пока тоже даём на страницу.
Здравствуйте, 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 — номер билда
получаем что число для новой версии всегда > предыдущей
Здравствуйте, De-Bugger, Вы писали:
DB>На сервере лежит XML. В ней перечень продуктов, номера последний версий, что изменилось с предыдущего билда и ссылка на Download соответсвующего продукта.
А нафига отдельный XML если на сервере есть PAD в формате XML, в котором уже есть номер версии, список изменений
и ссылка на инсталлятор?