Вверх ↑
Этот топик читают: Гость
Ответов: 321
Рейтинг: 10
#1: 2018-03-24 21:47:24 ЛС | профиль | цитата
Привет ребята, поделитесь пожалуйста опытом, кто как реализует обновление программы ?!
Приложение небольшое, и без записей в реестре, без ini/cfg файлов, dll и т.д, и не хотелось бы к нему делать отдельный updater.
Хочется чтобы оно обновляло само себя, посредством замены нового файла на старый. Это решит проблему, на случай если пользователь использует ярлык для запуска.

Вот мой способ:
1. Загрузка строки с базы mysql с информацией о последней версии в формате [версия;размер файла;md5 сумма файла].
2. Проверяем версию текущей программы с версией последней доступной программы, полученную в первом пункте.
3. Загрузка последней программы на компьютер со статистической ссылкой при необходимости (http://site.ru/latest_version.exe).
4. Проверяем целостность загруженной версии с помощью md5 суммы на всякий случай.
5. Генерация .bat файла.
6. Сохранение .bat файла.
7. Запуск .bat файла.

taskkill /f /im %1 - убиваем процесс основной программы.
del /q %1 - удаляем файл основной программы.
ren latest_version.exe %1 - переименовываем скаченную новую версию на имя старой.
del %0 ›nul - самоудаляем .bat файл.

Add(MainForm,2953706,161,280)
{
Width=641
Height=388
Color=16777215
link(onCreate,1644418:doStart,[])
}
Add(HTTPClient,7265496,763,252)
{
URL="http://ff.zzz.com.ua/latest_version.exe"
FileName="latest_version.exe"
Point(SaveTo)
link(onFinish,15746390:doFileHashsum,[])
link(onDownloadProgress,16118793:doPosition,[(807,272)(807,293)(751,293)(751,307)])
link(SaveTo,2483618:Value,[])
}
Add(Memory,2483618,777,203)
{
Default=Integer(2)
}
Add(ProgressBar,16118793,763,301)
{
Left=10
Top=85
Width=400
Max=0
Point(doMax)
}
Add(FormatStr,2949678,1204,252)
{
DataCount=1
Mask="taskkill /f /im %1\r\ndel /q %1\r\nren latest_version.exe %1\r\ndel %0 ›nul"
link(onFString,15045884:doText,[(1248,258)(1248,296)(1143,296)(1143,335)])
}
Add(Application,13385210,1155,203)
{
}
Add(FilePartElm,11262745,1155,252)
{
Point(Part)
link(onPart,2949678:doString,[])
link(FileName,13385210:AppFileName,[])
}
Add(Hub,8722152,1085,252)
{
OutCount=3
link(onEvent1,11262745:doPart,[])
link(onEvent2,15045884:doSave,[(1127,265)(1127,349)])
link(onEvent3,16078576:doShellExec,[(1120,272)(1120,384)])
}
Add(WinExec,16078576,1155,371)
{
FileName="update.bat"
}
Add(HTTPClient,2548297,273,294)
{
URL="http://ff.zzz.com.ua/update.php"
Point(SaveTo)
link(onFinish,13316639:doSearch,[])
}
Add(Thread,1644418,217,294)
{
Delay=0
FastStop=0
link(onExec,2548297:doLoadString,[])
}
Add(MultiStrData,8339473,385,301)
{
Count=3
link(onPart1,14327245:doCompare,[(436,307)(436,251)])
link(onPart2,16118793:doMax,[])
link(onPart3,11516841:doValue,[])
}
Add(If_else,14327245,455,245)
{
Type=1
link(onTrue,7488172:doEvent1,[(513,251)(513,209)])
link(onFalse,14691263:doData,[])
link(Op1,6228548:Caption,[])
}
Add(Button,5069766,651,161)
{
Left=10
Top=110
Width=150
Visible=1
Caption="Обновить"
Point(doVisible)
link(onClick,7265496:doExecute,[(698,167)(698,279)])
}
Add(DoData,3599545,574,210)
{
Data=String(Доступна новая версия программы, требуется обновление!)
link(onEventData,10264279:doWork2,[])
}
Add(Label,13229877,651,210)
{
Left=10
Top=60
Width=13
Height=17
Font=[MS Sans Serif,12,0,0,1]
Transparent=0
Caption="..."
}
Add(Hub,7488172,539,203)
{
link(onEvent1,12740353:doData,[(563,209)(563,167)])
link(onEvent2,3599545:doData,[])
}
Add(DoData,12740353,574,161)
{
Data=Integer(1)
link(onEventData,5069766:doVisible,[])
}
Add(DoData,14691263,574,252)
{
Data=String(У Вас последняя версия программы, обновление не требуется.)
link(onEventData,10264279:doWork3,[(627,258)])
}
Add(HubEx,10264279,623,210)
{
link(onEvent,13229877:doText,[])
}
Add(Hashsum,15746390,833,252)
{
link(onResult,11288209:doCompare,[])
link(Data,9506228:Value,[])
}
Add(StrList,15045884,1155,308)
{
FileName="update.bat"
}
Add(Memory,9506228,833,203)
{
Default=String(latest_version.exe)
}
Add(GlobalVar,6017712,889,224)
{
Name="md5"
}
Add(If_else,11288209,889,252)
{
link(onTrue,8722152:doEvent1,[])
link(onFalse,15693563:doMessage,[])
link(Op1,6017712:Var,[])
}
Add(Message,15693563,959,259)
{
Message="Ошибка. Сумма md5 не соответствует."
Caption="md5"
}
Add(BlockFind,13316639,329,301)
{
IncludeBlock=1
StartBlock="‹s›"
EndBlock="‹e›"
link(onSearch,8339473:doSplit,[])
}
Add(GlobalVar,11516841,448,315)
{
Name="md5"
}
Add(Label,5077860,406,161)
{
Left=10
Top=5
Width=85
Height=24
Color=16777215
Font=[MS Sans Serif,12,1,0,1]
Caption="program v"
AutoSize=1
}
Add(Label,6228548,455,161)
{
Left=95
Top=5
Width=29
Height=24
Color=16777215
Font=[MS Sans Serif,12,1,0,1]
Caption="1.0"
}


Если запустить второй раз приложение, то должно написать, что обновление не требуется. Запускать второй раз естественно надо не через hiasm
Кто проверит отпишитесь пожалуйста получается ли обновится ?! win7 x64 - получается вроде

______________________________________________________
Вот пару вопросиков...
Можно ли в ProgressBar в Max задавать 508416 байт например, для отображения прогресса загрузки обновлений, или лучше уменьшать кол-во, округляя до Кб хотя бы ?!
Если программу запускать не от имени администратора и на вин10 например, сработает ли .bat и насколько он актуален в 2018 или лучше в memoryStream засунуть updater и запускать из программы...
Еще интересно как антивирусы отреагируют на .bat, не хочу пугать пользователей.
И никто не в курсе md5 починили под разные разрядности ОС ?! Недавно видел где-то тему, что md5 не корректно считается.
Еще пару лет назад видел у кого то способ запуска программы от имени администратора, если есть возможность в ЛС получил бы, если конечно по вашему мнению это нужно для нормального обновления без осложнений.
Вообщем ребята дайте советов пожалуйста, чтобы вы изменили, просто важно в принудительном порядке заставлять обновляться и при этом чтобы не было проблем ;)

Редактировалось 6 раз(а), последний 2018-03-24 22:06:31
карма: 1

0
vip
#1.1контекстная реклама от партнеров
Ответов: 497
Рейтинг: 16
#2: 2018-03-24 23:43:57 ЛС | профиль | цитата
Master4eG писал(а):
1. Загрузка строки с базы mysql с информацией о последней версии в формате [версия;размер файла;md5 сумма файла].

зачем размер файла? его при скачивании получить можно.
Master4eG писал(а):
5. Генерация .bat файла.6. Сохранение .bat файла.7. Запуск .bat файла.

это или ручками наверное лучше. + я когда то для этого в главном приложении запускал сервер и он обрабатывал запрос на закрытие с сохранением данных.
Master4eG писал(а):
Можно ли в ProgressBar в Max задавать 508416 байт например, для отображения прогресса загрузки обновлений, или лучше уменьшать кол-во, округляя до Кб хотя бы ?!

можно но чесно говоря а смысл? если так мало то даже через модем 2г очень быстро скачает
Master4eG писал(а):
Если программу запускать не от имени администратора и на вин10 например, сработает ли .bat и насколько он актуален в 2018 или лучше в memoryStream засунуть updater и запускать из программы...

срабботал но не очень красиво мягко говоря. или руками или если так охота то уже через WinExec с консолькой работай. и проблем не будет тот факт что ты не можешь обработать ошибки
Master4eG писал(а):
Еще интересно как антивирусы отреагируют на .bat, не хочу пугать пользователей.

не уверен но если за свою робочую папку не убегать и не юзать чего то системного не думаю что им будет интересно
Master4eG писал(а):
Еще пару лет назад видел у кого то способ запуска программы от имени администратора, если есть возможность в ЛС получил бы, если конечно по вашему мнению это нужно для нормального обновления без осложнений.

есть и не один но не думаю что это надо. просто установить в "свободную" папку и админ не нужен.
еще от себя
все лучше делать в своей программе.
когда то делал также но вместо просто загрузки главной программы я загружал архив в котором минимум для запуска программы.
потом понял что это не вариан и начал загружать все по отдельности и только потом доработал(на сервер отправлял что есть а уже сервер давал список что грузить.
дальше уже лет 5 назад начал юзать сервис. при установке свой апдейт сервис запускался с правами текущего пользователя. а там уже карты руки.
как уже упоминал просто принудительно завершать это не вариант. я отправлял главному приложению команду приложение спрашивает у юзера если да сохранить завершить продолжить установку после открыть все с чем юзер работал если нет напомнить через 30 минут. по завершению приложения юзером принудительное обновление.
и было такое когда сохраняли несколько копий приложения в папках(как хром и опера например)
карма: 1
        ]  
0
Ответов: 321
Рейтинг: 10
#3: 2018-03-25 00:38:39 ЛС | профиль | цитата
RAWY_EX писал(а):
зачем размер файла? его при скачивании получить можно.

Нужно до скачивания узнать размер файла, что бы извиняюсь, всунуть в ProgressBar (Max). Или можно как-то до скачивания узнать размер через HTTPClient ?! Я не нашел такого.
RAWY_EX писал(а):
это или ручками наверное лучше. + я когда то для этого в главном приложении запускал сервер и он обрабатывал запрос на закрытие с сохранением данных.

Что такое сервер ?! Можно вкратце как это и что это ?!
RAWY_EX писал(а):
можно но чесно говоря а смысл? если так мало то даже через модем 2г очень быстро скачает

Нет, тут я испугался, что значение max в ProgressBar слишком большое, я никогда больше 1000 не использовал для подсчета строк каких-нибудь при выборке таблиц mysql и т.д., ну по мелочи, а тут сотни тысяч
RAWY_EX писал(а):
срабботал но не очень красиво мягко говоря. или руками или если так охота то уже через WinExec с консолькой работай. и проблем не будет тот факт что ты не можешь обработать ошибки

А как тогда приложение закроет и удалит само себя ?! Надо в Param ввести то что у меня в .bat файле и выполнить doConsoleExec ?! Пробовал не получилось вроде (в filename указал cmd.exe).

Редактировалось 2 раз(а), последний 2018-03-25 00:41:25
карма: 1

0
Ответов: 497
Рейтинг: 16
#4: 2018-03-25 03:38:09 ЛС | профиль | цитата
Master4eG писал(а):
всунуть в ProgressBar (Max).

вот еще как можно

Add(HTTP_Get,5338951,336,357)
{
link(onStatus,5455157:doPosition,[(412,363)(412,370)])
link(onURLSize,5455157:doMax,[])
}
Add(Hub,9184195,273,371)
{
link(onEvent1,5338951:GetURLSize,[])
link(onEvent2,5338951:doDownload,[(311,384)(311,363)])
}
Add(HTTPClient,6834211,294,413)
{
link(onDownloadProgress,14029498:doEvent1,[])
}
Add(ProgressBar,16296899,441,427)
{
Left=455
Top=420
Point(doMax)
}
Add(Hub,14029498,364,427)
{
link(onEvent1,16296899:doPosition,[])
link(onEvent2,4173233:doData,[])
}
Add(DoData,4173233,392,434)
{
link(onEventData,16296899:doMax,[])
link(Data,6834211:ContentLength,[(398,422)(346,422)(346,457)(307,457)])
}
Add(ProgressBar,5455157,455,364)
{
Left=455
Top=360
Point(doMax)
}


Master4eG писал(а):
Что такое сервер ?! Можно вкратце как это и что это ?!

то что будет принимать запросы. ТСР или какой удобно. лучше MailSlot
Master4eG писал(а):
Нет, тут я испугался, что значение max в ProgressBar слишком большое

Max значение integer = 2147483647 можно смело бросать обновы чуть меньше 2ГБ
Master4eG писал(а):
как тогда приложение закроет и удалит само себя ?! Надо в Param ввести то что у меня в .bat файле и выполнить doConsoleExec ?! Пробовал не получилось вроде (в filename указал cmd.exe).

а зачем вообще удалять приложение? ардейтер просто взял и скачал обновление и заменил старый файл. все. что еще надо?
doConsoleExec в конце запроса просто надо ентер отправлять
карма: 1
        ]  
0
Ответов: 4612
Рейтинг: 746
#5: 2018-03-26 10:47:31 ЛС | профиль | цитата
Master4eG писал(а):
Или можно как-то до скачивания узнать размер через HTTPClient ?! Я не нашел такого.
В папке примеров в архиве компонента пример "Скачивание файлов.sha".

Master4eG писал(а):
1. Загрузка строки с базы mysql с информацией о последней версии в формате [версия;размер файла;md5 сумма файла].
Проще написать PHP-скрипт, который будет перебирать каталог с файлами обновления и выдавать имя+дату изменения(+куда сохранять у клиента). Обновлятор получает список файлов и даты, сравнивает со своими. Если отличаются - скачивает файл, сохраняет и проставляет дату сохраненного файла согласно полученной с сервера.
Master4eG писал(а):
5. Генерация .bat файла.
Можно, конечно и так, если хватает. А лучше сделать собственную программку, которая будет при получении ошибки сохранения файла ждать завершения основной программы и обновлять после закрытия. А можно извернуться - если файл занят, размещать новый файл возле него под другим именем. Программа перед стартом проверяет наличие этого другого файла, если есть - запускает его, а сама закрывается; этот другой файл в свою очередь при запуске копирует себя на место оригинальной программы.

Редактировалось 3 раз(а), последний 2018-03-26 10:56:05
карма: 26

0
Ответов: 321
Рейтинг: 10
#6: 2018-03-26 22:19:13 ЛС | профиль | цитата
RAWY_EX, спасибо громадное за варианты и за подсказки, очень было интересно почитать!
Netspirit писал(а):
В папке примеров в архиве компонента пример "Скачивание файлов.sha".

Да я помню, что было, просто обновление было насколько знаю компонента, думал может порезали.
ContentLength - думал что-то типа длины строк ответа, не думал, что учитывает поток-данных, ну тут заблуждение мое короче виновато

Netspirit писал(а):
Проще написать PHP-скрипт, который будет перебирать каталог с файлами обновления и выдавать имя+дату изменения(+куда сохранять у клиента). Обновлятор получает список файлов и даты, сравнивает со своими. Если отличаются - скачивает файл, сохраняет и проставляет дату сохраненного файла согласно полученной с сервера.

Ну в этом и вся соль, что вся программа это один .exe файл. Это не какой-то глобальный updater
Иными словами самообновление требуется. Не думаю что кто-то для экранной лупы какой-нибудь делает updater, у меня очень простая программка для работы с базой данных. Но программка рассчитана на очень большое кол-во пользователей. И требуется в случае чего, заставить их обновится, например при переезде на другой хостинг и т.д. ;)

Netspirit писал(а):
А можно извернуться - если файл занят, размещать новый файл возле него под другим именем. Программа перед стартом проверяет наличие этого другого файла, если есть - запускает его, а сама закрывается; этот другой файл в свою очередь при запуске копирует себя на место оригинальной программы.

Изначально так и планировалось, но показалось, что .bat лучше ввиду того, что...
taskkill /f /im %1 - убиваем процесс основной программы.
del /q %1 - удаляем файл основной программы.
ren latest_version.exe %1 - переименовываем скаченную новую версию на имя старой.
del %0 ›nul - самоудаляем .bat файл.
К этому можно добавить строчку для запуска (start name.exe) уже нового экземпляра с новым именем и готового к работе.
То есть скачалось, md5 совпадает и все, выполняем .bat и получается с этой строчкой, как будто программа просто перезапустилась.

Редактировалось 2 раз(а), последний 2018-03-26 22:28:26
карма: 1

0
Ответов: 4612
Рейтинг: 746
#7: 2018-03-27 10:57:11 ЛС | профиль | цитата
Master4eG писал(а):
taskkill /f /im %1 - убиваем процесс основной программы.
А если запущено несколько копий программы? А если пользователь в этот момент что-то делает и не сохранил изменений? Для закрытия программы лучше отправлять всем окнам (или окнам с классом как у программы) сообщение WM_CLOSE (а лучше - своё собственное сообщение с предопределенными параметрами, а в программе - ожидать получения). Ожидание завершения, если требуется - на основе мютекса (ну, или пытаться заменять файл до тех пор, пока не получится).
карма: 26

0
Ответов: 321
Рейтинг: 10
#8: 2018-03-27 19:13:38 ЛС | профиль | цитата
Netspirit писал(а):
А если запущено несколько копий программы?

TASKKILL должен по идее убить все процессы с именем программы, хоть 100 копий.
Можно закрыть еще по pid, ну это так к сводке, просто изначально хотел, чтобы так закрывало и как раз, если много копий программы то лучше по имени, если я правильно Вас понял, вы имеете ввиду, если один exe запущен несколько раз ?!.
/PID процесс - Идентификатор процесса, который требуется завершить. Используйте TaskList, чтобы получить PID.
/T - Завершение указанного процесса и всех его дочерних процессов.
/F - Принудительное завершение процесса.

Netspirit писал(а):
А если пользователь в этот момент что-то делает и не сохранил изменений?

Обновление проверяется при запуске программы. И если есть обновление, то предлагает обновится принудительно, да-обновится, нет-закрыть программу.

Netspirit писал(а):
Для закрытия программы лучше отправлять всем окнам (или окнам с классом как у программы) сообщение WM_CLOSE (а лучше - своё собственное сообщение с предопределенными параметрами, а в программе - ожидать получения). Ожидание завершения, если требуется - на основе мютекса (ну, или пытаться заменять файл до тех пор, пока не получится).

Спасибо большое за мысли, то есть все таки делать второе приложение ?! Я чуть-чуть не понял.
___________________________________________________
Еще был вариант сделать sfx архив самораспаковывающийся, запускать его по окончанию закачки на пк, через onClose. Ну не знаю, успеет ли программа закрыться, до распаковки архива. Ну или опять же запускать .bat, который запустит sfx с задержкой.
Удобно, что sfx можно запускать с интересными параметрами:
MiscFlags="4" - готовый sfx, при запуске, будет требовать права Администратора.
OverwriteMode=... - режим перезаписи файлов.
RunProgram= - запуск указанного файла, после распаковки.
Но опять же не хочется оставлять мусор, а само удаления архива вроде как нет, опять придется распаковать еще .bat который удалит архив и себя, это второй вариант)

Редактировалось 10 раз(а), последний 2018-03-27 19:42:53
карма: 1

0
8
Сообщение
...
Прикрепленные файлы
(файлы не залиты)