Zerpico
Дабы не бегать по своей работе к каждому человеку который использует мою программу разумно сделать автообновление, которое будет обновлять программу, если загрузить новую версию на сервер. Испробовав множество способов, нашел самый простой в использовании (хотя и не самый правильный)
Итак алгоритм:
- Программа при включении проверяет на сервере последнюю версию.
- Если на сервере выше текушей то скачиваем Zip-архив c программой.
- Переименовываем файл приложения на другое (ради бэкапа и доступности к файлу), например с program.exe на program.backup.
- Распаковываем архив заменяя файлы в папке.
- Удаляем архив с обновлением.
- Перезапускам программу.
Но перед всем этим сначала создадим таблицу Updater (например) на MSSQL с полями:
name - тип varchar;
version -тип varchar;
files - тип varbinary(max) (или любое другое blob-поле).
Лучше сразу создать в таблице запись, где в поле name будет program.
Добавить эту процедуру в код для извлечения номера версии программы:
function TForm1.GetMyVersion: string;
type
TVerInfo=packed record
Nevazhno: array[0..47] of byte; // ненужные нам 48 байт
Minor,Major,Build,Release: word; // а тут версия
end;
var
s:TResourceStream;
v:TVerInfo;
begin
result:='';
try
s:=TResourceStream.Create(HInstance,'#1',RT_VERSION); // достаём ресурс
if s.Size>0 then begin
s.Read(v,SizeOf(v)); // читаем нужные нам байты
result:=IntToStr(v.Major)+IntToStr(v.Minor)+ // вот и версия...
IntToStr(v.Release)+IntToStr(v.Build);
end;
s.Free;
except; end;
end;
Теперь нам понадобится библиотека для распаковки архивов, ведь я буду хранить обновление в zip архиве, использую библиотеку SevenZip, добавляем в Delphi в library путь до исходников библиотеки.
Теперь нам надо закачать сам архив с новой версией программы в базу данных. Я сделал форму с загрузкой обновления на сервер:
по открытию OpenDialog думаю всё понятно переписываем путь в edit
if OpenDialog1.Execute then
cxButtonEdit1.Text:=OpenDialog1.FileName;
Номер версии лучше вписывать в MaskEdit с маской - !9.9.9.0;1;_
По кнопке сохранить закачиваем файл процедурой:
var blobF: TBlobField;
begin
if not FileExists(OpenDialog1.FileName) then
begin
ShowMessage('Файл не найден!');
exit;
end else cxButtonEdit1.Text:=OpenDialog1.FileName;
try
ADOTable1.TableName:=Updater; //выбираем таблицу в которой будем хранить архив
ADOTable1.Close;
ADOTable1.Open;
//ищем поле name с записью program
ADOTable1.Filtered := False;
ADOTable1.Filter := 'name='+#39+'program'+#39;
ADOTable1.Filtered := True;
ADOTable1.Edit;
blobF := ADOTable1.FieldByName('files') as TBlobField;
blobF.LoadFromFile(OpenDialog1.FileName);
ADOTable1.FieldByName('version').AsString:=cxMaskEdit1.Text;
ADOTable1.Post;
except
Showmessage('Ошибка загрузки!');
end;
Это немного неправильный метод, лучше всего сделать отдельным потоком, особенно если архив большого размера.
Необходимо добавить в Uses - SevenZip и ShellAPI.
Теперь создадим самую важную процедуру обновления, назовав её Update:
Procedure TForm1.Update;
var path,fullpath,Ourversion,LastVersion:string;
blobF: TBlobField;
begin
//получем номер версии на сервере
adoquery4.Active:=false;
adoquery4.sql.text:='SELECT version FROM [dbo].[Updater] WHERE name='+#39+'program'+#39;
adoquery4.Active:=true;
//добавляем номера версии на сервере и текушей в переменые, и убираем точки
Ourversion:=GetMyVersion;
LastVersion:=adoquery4.FieldByName('version').Value;
while pos('.',LastVersion)<>0 do
delete(LastVersion,pos('.',LastVersion),1);
//сравниваем версии если текушая меньше чем на сервере то спрашиваем обновлять или нет
if strtoint(LastVersion)>strtoint(Ourversion) then
If messageBox(Handle,'Появилось свежая версия программы. Обновить?','Обновить?',
mb_YesNo or mb_iconquestion)=mrYes then
try
path:=ExtractFileDir(ParamStr(0));
if FileExists(path+'\Program.backup') then DeleteFile(path+'\Project2.backup');
RenameFile(path+'\Program.exe', path+'\Program.backup'); //переменовываем оригинальный файл
//фильтруем
//ADOTable1 указывает на таблицу Updater
ADOTable1.Close;
ADOTable1.Open;
ADOTable1.Filtered := False;
ADOTable1.Filter := 'name='+#39+'program'+#39;
ADOTable1.Filtered := True;
ADOTable1.Active:=true;
//скачиваем файл
blobF := ADOTable1.FieldByName('files') as TBlobField;
if blobF.Value = nil then Exit;
blobF.SaveToFile(path+'\Update_ARMTitan.zip');
ADOTable1.Active:=false;
// Распаковывает файлы
with CreateInArchive(CLSID_CFormatZip) do
begin
OpenFile(ExtractFilePath(ParamStr(0)) + 'Update_ARMTitan.zip');
ExtractTo(ExtractFilePath(ParamStr(0)));
Close;
end;
//удаляем архив который скачали
DeleteFile(path+'\Update_ARMTitan.zip');
//перезапуск программы
fullpath:=path+'\Project2.exe';
ShellExecute(0, 'open', PWideChar(fullpath), '', nil, SW_SHOW);
//WinExec(PAnsiChar(fullpath), SW_SHOW);
Application.Terminate; // or: Close;
finally
end;
end;
Здесь тоже есть недочет, распаковывать лучше в отдельности каждый файл и проверять на наличие ошибки, а не весь архив целиком как сделал я.
Тут есть 2 мелочи которые надо соблюдать:
1. В директории с программой должен быть файл 7z.dll, поэтому лучше сделать проверку:
if FileExists(path+'\7z.dll') then
begin
ShowMessage('Отсутствует файл 7z.dll');
exit;
end;
или еще как то по другому.
2. В архиве с обновлением не должен присутствовать файл 7z.dll, так как он используется, или же распаковать всё по отдельности как я писал выше и не распаковывать только этот файл.
Вот и всё. Главное не забыть перед компиляцией готовой программы для обновления прежде чем добавить её в архив, изменить номер версии программы.
В этом способе есть некоторые недочёты и я это знаю. Но пока что я им пользуюсь и проблем не испытывал, хотя доработать всё же следует.