Помощь - Поиск - Пользователи - Календарь
Полная версия: Перезапись exe во время выполнения
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Делфи
Unconnected
Привет всем.

Делаю механизм самообновления для проги, может ли exe скачивать обнову и на лету себя ею перезаписывать? Качаю в TMemoryStream. Делал с сохранением обновления на диск, замещением и т.д., но это не очень стабильно работало.
IUnknown
Цитата
может ли exe скачивать обнову и на лету себя ею перезаписывать?
Размечтался... smile.gif Что именно "не очень стабильно" работало у тебя, расскажешь?

Кстати, лучше бы обновляемые части программы хранить в DLL. Скачал новую DLL-ку, выгрузил старую, заменил старый файл новым, загрузил новую библиотеку. С EXE-шником так не получится. В любом случае его надо будет выгружать. То есть, после того, как скачал обновление - перезапускать программу.
Unconnected
С DLL заморачиваться не хочу, не тот левел) Ну, я качал обновление в C:\windows\temp (даже не C:\, а букву диска получал), запускал его и закрывал работающий старый процесс. В обнове была пауза в несколько секунд, чтобы старый успел закрыться, и перемещение с заменой, это делал средствами cmd. Вот думаю, там потоки были работающие, а я перед уходом их не гасил, а тупо halt-ил.. может, они там как-то в памяти оставались через раз и не давали заместить exe.. процентов 30 проваленных обновлений было стабильно.
IUnknown
Цитата
Вот думаю, там потоки были работающие, а я перед уходом их не гасил
Ты б делал правильно, проблем бы было меньше smile.gif

Цитата
С DLL заморачиваться не хочу, не тот левел)
В таком случае, кроме BAT-файла вариантов просто нет. Раньше были, но теперь (начиная с WinXP) они не проходят. Корректно завершай потоки - будет работать.
IUnknown
А нет. Только что попробовал еще один способ - работает. Итак: есть основная программа, и программа-обновлялка. Нет, я не предлагаю при запуске проверять, есть ли обновления, и если есть - то вытягивать. Все проще: проверяет твоя программа, которая загружена и работает.

А вот когда она обнаружила, что обновление есть, и его надо качать - она запускает обновлялку. Та сама, в автономном режиме, скачивает обновление, и как только скачала - дает сигнал основной программе, "неплохо бы завершиться" (реализация этого взаимодействия может быть любой. Я сделал так, что при запуске обновлялки ей параметром передается Handle главного окна приложения, на который потом и отсылается WM_CLOSE. Тут все зависит от твоей фантазии). Основная программа завершается (корректно, без спешки, чтоб не убивать потоки, а завершить как положено), возможно - перед закрытием основного потока дает сигнал обновлялке, возможно - просто обновлялка ждет какое-то время, убирает старый EXE-шник, подменяет его новым и запускает его. После чего ее функция закончена - она может самоликвидироваться. Я надеюсь, как сделать, чтоб EXE-файл после завершения работы самоудалился - знаешь? У Рихтера есть рецепт.

Да, да, я знаю, какой вопрос ты сейчас задашь... (Показать/Скрыть)
Unconnected
А я то думал, что память, выделенная под потоки, автоматом освобождается и распределяется после завершения процесса.. что интересно, в 100% тестов на моей вирте всё было нормально)
На данный момент у меня практически второй способ - исходный файл качает обнову, после запускает её и завершается. И скачанный заменяет старый. Непонятно, где здесь слабое место - как нужно правильно гасить свет, уходя? Или move /y не всегда срабатывает..
-TarasBer-
> В обнове была пауза в несколько секунд, чтобы старый успел закрыться

Когда будешь писать многопоточные и прочие такие вещи, НИКОГДА не делай "паузу, чтобы успело сделать что-то там", всегда делай "подождать, пока не придёт сигнал, что что-то успело сделать что-то там"
Unconnected
Хочешь сказать, что в моем случае после halt-а программа как бы не закрывается и не дает себя заместить?
IUnknown
Ты сам это сказал. Вот тут:
Цитата
я качал обновление в C:\windows\temp (даже не C:\, а букву диска получал), запускал его и закрывал работающий старый процесс. В обнове была пауза в несколько секунд, чтобы старый успел закрыться, <cut> процентов 30 проваленных обновлений было стабильно.
Закрывалась бы программа - не было б 30% провалов.

P.S. ВременнЫе интервалы для ожидания закрытия программы - "Не наш метод" (С). Только сообщения/другие средства межпроцессного взаимодействия.
Unconnected
Короче алгоритм я понял такой.. скачали новый, старый запускает его с параметром-своим хэндлом (или pid-ом, идентефикатором, в общем), и начинает аккуратно закрываться, а новый мониторит по ид-у, работает старый или нет, и когда его не станет - устанавливается, так? И что нужно ещё сделать при закрытии проги, кроме освобождения потоков (thread.free)? Глобально описанных и динамически создаваемых объектов нет.
IUnknown
Цитата
скачали новый, старый запускает его с параметром-своим хэндлом
Кого это "его"? Я тебе что выше писал? Как только обновление доступно - запускается Updater, который и производит скачку. Скачал - послал сообщение твоей основной программе. Она его приняла, аккуратно, без спешки закрыла все потоки (не Halt-ами, а /Terminate + FreeOnTerminate/ или /Terminate + WaitFor/, заодно отслеживая, что нет спящих потоков), и потом, перед самым своим завершением, послала Updater-у сообщение: "Все, все потоки удалены, все файлы закрыты, я никого не держу и делаю харакири". Твой Updater, приняв это сообщение, понимает, что основная программа уже вот-вот отбросит коньки, и, выждав контрольный промежуток времени (уже все закрыто, тормозов при выходе не будет, ага, тут можно и подождать), удаляет старый EXE-шник, заменяет его новым, и запускает его на выполнение. После чего завершается сам (его EXE-файл при этом удаляется автоматически, спасибо Дж. Рихтеру). Итог: обновленная программа работает, имя EXE-шника не изменилось, установщик исчез. Все как и было.

В твоем случае опять будет проблема. Скачал ты файл, запустил НОВОЕ приложение, старое закрылось. И что? Новое работает под новым именем. Чего ты добился? Нет уж, "разделяй и властвуй". Каждый делает то, что ему положено делать. Обновлялка качает обновления и занимается, собственно, его установкой, приложение ей в этом не мешает. А только помогает.
Unconnected
В смысле "Новое работает под новым именем"? После запуска оно копируется на место старого exe, с заменой. При новом обновлении повторяется всё, как встарь.. По-моему, что я описал, это то же самое, только без доп. программы.
IUnknown
Цитата
После запуска оно копируется на место старого exe, с заменой.
Ты из работающей программы его запускаешь, и оно тебе на место работающей программы же и устанавливается? Ну-ну. Удачи.

Без доп. программы не выйдет.
Unconnected
Цитата
Ты из работающей программы его запускаешь, и оно тебе на место работающей программы же и устанавливается?


Ну да, говорю же, запущенное обновление мониторит, жив ли старый exe, и когда узнает, что уже закрылся, ждет несколько секунд и ставится с замещением, почему бы и нет..
IUnknown
Это "почему бы и нет" у тебя уже было. Ну, реализуй это, попробуй. Соберешь статистику отказов - приходи.

С отдельным приложением - все просто, как хозяйственное мыло: поскольку никакого "с замещением" делать не надо, то спокойно дожидаемся завершения головной программы, удаляем ее файл, так же спокойно сбрасываем содержимое TMemoryStream на диск под тем же именем, и перезапускаем приложение. Никаких мониторингов, пустых разбазариваний ресурсов, никаких "ух ты, опять 30% сбоев. Почему это, интересно? Может, действительно, надо было послушать, когда говорили про _несколько_ приложений?".

Да ладно, делай как знаешь, мне что, надо очень переубеждать тебя? Только зачем спрашивать, если все равно изначально решил делать по-своему?
Unconnected
Ок, совет понял) Закрывание

loaderthread:=Tread2.create(false);
loaderthread.FreeOnTerminate:=true;//в конце OnExecute потока вызывается terminate
loaderthread.waitfor;
exitprocess(0);


И ещё один поток создается BeginThread-ом, его гашу TerminateThread(trhNotify,0);.
IUnknown
У тебя, я надеюсь, не совершенно секретная разработка? Можешь исходники своей программы показать? (да знаю я, знаю, что ты на KOL пишешь, у меня Лазарус есть, там KOL работает). Может, нашли бы совместными усилиями, где поток держится, и как это исправить... Можно на почту, адрес у тебя есть...
Unconnected
Спасибо, но в принципе уже разобрался, сделал так (где-то нашел, что freeonTerminate:=true может мешать WaitFor-у. До этого у меня на нём вылетала ошибка №6, неверный дескриптор, как-то так).
  try
loaderl:=dcrypt(b64decode(getintxt(loaderlink+b64encode(crypt(mac,pas)))),pas);
loaderthread:=Tread2.create(false);
loaderthread.FreeOnTerminate:=false;
loaderthread.WaitFor;
if length(updatel)>5 then exitprocess(0);
except end;


В самом потоке в секции finally выполняется loaderthread.terminate;

И такой код в начале обновления:

repeat
deletefile(writepath+nname);
sleeping(2000);
until not(fileexists(writepath+nname));
//тут уже перемещение нового



Обновилось 100% (ну может без 1-2), это из 120 обновлений. На днях больший объем попробую.. Кстати, в KOL для работы с потоками достаточно так же унаследовать от TThread, как и делаю, хотя там и спецкомпонент есть.
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.