Обновлние своей программы
Поделитесь как можно это сделать, желательно без дополнительных программ, у кого какие мысли?
Поделитесь как можно это сделать, желательно без дополнительных программ, у кого какие мысли?
Сильно сомневаюсь, что это можно будет сделать без дополнительных программ. Скорее всего Windows не позволит редактировать файл, который в настоящее время исполняется.
Ну, или можно специальное приложение вместо батника предусмотреть, которое будет обновлять программу.
Обновление проверяется по дате модификации файла.
Если кому надо могу выложить.
Не всегда можно обойтись DLL. Гораздо более универсальным способом будет держать в памяти 2 процесса. Один является рабочим процессом, другой - загрузчиком. Проверку обновлений возлагаем на загрузчика. Если в некоторый момент времени загрузчик обнаруживает обновление, он его качает, завершает рабочий процесс (сигнализируя ему или аварийно), накатывает апдейт и запускает его по новой. Кроме того, если задача критичная, загрузчик может выступать как "сторожевой процесс" и перезапускать рабочий процесс в случае его аварийного завершения.
А еще загрузчик можно использовать для периодического рисования ромашек на рабочем столе :) Как бы, коллега поставил конкретную задачу без надобности всяких "сторожевых процессов". Для одной программы держать в памяти два процесса - роскошь. А вот вынос функционала в DLL - элементарная задача: в самом EXE остается логика программы и средства взаимодействия с пользователем, а вся расчетная часть, которая применяется в программе, выносится в DLL. При обновлении DLL не требуется завершения работы программы, почему этот метод и является более красивым.
А вообще, было бы понятнее, если бы YouROK поведал, что это за программа.
К "не завершая работы" стремиться, мне кажется, не стоит. Да так никто и не делает. Максимум - отложенное обновление.
Всё, что не связано с UI, выносится в DLL, ссылки на соответствующие функции определяются в файле заголовка. При инициализации программы вызывается LoadLibrary, FindResource и LoadResource, задавая адреса функциям в заголовке. При обновлении делается FreeLibrary, копируется новая библиотека, после чего процесс загрузки повторяется.
Ну а что, если тов. YouROK пишет, скажем, антивирус? Критично к обновлениям программы? Да. Чувствительно к перезагрузкам программы? Тоже да. Так что, естественно, решать нужно для конкретного случая, но всё-таки с точки зрения пользователя обновление без необходимости совершения манипуляций со стороны пользователя было бы более приемлемо.
C++
class C
{
private:
int counter;
public:
C() : counter(0)
{
}
C(int counter) : counter(counter)
{
}
int Next()
{
return counter++;
}
};
-->
class C
{
// ...
int Next()
{
int retval = counter;
counter += 2;
return retval;
}
}
// Интерфейс
int main()
{
C c;
while (true)
{
std::cout << c.Next() << std::endl;
Sleep(10000);
}
}
Даже для управляемого кода это утверждение выполняется далеко не всегда.
Продолжайте, пожалуйста.
Однажды загруженную в контекст исполнения сборку можно отгрузить (и освободить дескриптор ос) только отгрузив домен приложения, который ее использует. Дефолтный (нулевой) домен отгрузить нельзя. Т.е. дефолтный домен придется делать доменом-загрузчиком другого домена - уже того, который будет гонять нужный нам код. Между ними ессно нужно организовывать взаимодействие, в зависимости от сложности системы, можно будет обойтись парой семафоров или же придется настраивать ремотинг.
Над всем этим понятное дело довлеет система безопасности .NET и ОС.
Классический пример: нельзя из управляемого кода моментально остановить поток, находящийся в неуправляемом коде. Поток должен сперва выйти из неуправляемой части, и только потом он будет остановлен CLR. Эта особенность может серьёзно повлиять на остановку рабочего домена приложения и отгрузку его кода.
Вывод: возможность обновления "на лету" нужно закладывать в дизайн сразу, а не прикручивать ее потом.
Да, есть задачи действительно критичные к обновлению, но таких меньшинство. В большинстве же случаев обновляемое приложение завершается и особых неудобств это обычно не вызывает.
Можно было сделать проще, используя полиморфизм. Ну да ладно, суть не в этом.
Пример я выбрал максимально простым неспроста. Твоё решение запросто вылетает с ошибкой доступа к памяти в тот момент, когда исходная библиотека удалена, а новая не помешена на её место, либо ссылки на неё ещё не обновлены, и приложение пытается вызвать из неё функцию. То есть, помимо вызова метода, создающего экземпляр класса Counter, синхронизировать необходимо также каждый из методов класса Counter. И то, этого будет достаточно лишь в том случае, если каждый открытый метод Counter представляет собой законченную логическую операцию.
Кроме того, не ясно, что произойдёт в том случае, если состав инкапсулируемых классом Counter данных изменится. В принципе, вопрос иногда решаем применением полиморфизма (то есть, приложение импортирует интерфейс ICounter, который реализуется закрытым классом Counter1 старой библиотеки; в новой версии есть класс Counter2, также реализующий ICounter, и, кроме этого, имеющий конструктор Counter2(Counter1 & rhs)), но легко можно привести инциденты невозможности и этого шага (просто мы еще не подошли к взаимодействию объектов).
Однако и это - не главное.
Основных же довода два и они взаимосвязаны.
1. При твоём подходе невозможно выделить систему обновления в отдельный логический пакет и обозначить зависимость от него других пакетов, возможна лишь тесная интеграция подверженной обновлению бизес-логики с системой, обеспечивающей это обновление. А это влечёт за собой ряд следствий:
- бессмысленное засерание предметной области совершенно не связанными с ней деталями;
- если мы исчерпывающим образом не определили механизм работы системы обновления на стадии логического дизайна, то прикрутить её впоследствии минимум - нерентабельно, максимум - невозможно.
2. Тебе бы Фаулера почитать... Существует понятие нестабильности пакетов. Пакет тем более нестабилен, чем больше количество импортируемых им классов из других пакетов по отношению к суммарному количеству импортируемых и экспортируемых им классов. Наибольшему изменению подвержены нестабильные пакеты, наименьшему - стабильные. Таким образом, самым нестабильным пакетом становится пользовательский интерфейс, и чем больше мы углубляемся в бизнес-логику, тем она должна становиться стабильнее. Стабильность, в свою очередь, подразумевает следующее: изменение самого стабильного пакета в системе повлечёт за собой изменение большинства остальных пакетов. Иными словами, при подходе "один пакет - один исполняемый модуль" (подходит?) взять и заменить одну дээлэлку не получится - менять придётся сразу большую пачку. И, скорее всего, пользовательский интерфейс будет участвовать где-то в начале титров этой Санта-Барбары. Я это к тому, что подход "меняем бизнес-логику и оставляем интерфейс", скорее всего, неверен в корне.
А мьютекс в counterUpdater я для красоты поставил.
Приведите пример, когда это может произойти при обновлении программы.
Что Вы понимаете под "отдельным логическим пакетом"?
Ни буквы не прочту. В предложенном Вами примере у меня всё работает идеально и попробуйте с этим поспорить.
А вообще, согласен с nikitozz.
Если пишите якобы на C++, то почему бы не использовать C++ ?
А мьютекс в counterUpdater я для красоты поставил.
Der Meister прав. Твой мьютекс не отрабатывает все варианты:
<---------- А ВОТ ЗДЕСЬ ВСЕ УПАДЕТ -------------->
UpdateDLLProcs(); <- загружается DLL только здесь
Приведите пример, когда это может произойти при обновлении программы.
Например, новый метод Next должен инкрементировать счетчик на величину равную порядковому номеру созданного экземпляра. Т.е. в программе несколько экземпляров счетчиков:
c2 = cu.GetInstance();
c3 = cu.GetInstance();
while (true)
{
cout << c1->Next() << endl; // Идет с шагом 1
cout << c2->Next() << endl; // Идет с шагом 2
cout << c3->Next() << endl; // Идет с шагом 3
Sleep(1000);
}
Советую разобраться с такими понятиями, как инкапсуляция.
Ты странным образом обновляешь обработчики данных, при этом структура самих данных у тебя всегда одна и та же, а ведь она тоже может (и будет) изменяться.
В данном случае нельзя разрывать данные и обработчики. Посмотри, как устроены COM-объекты.
Твой класс Counter должен был бы выродиться до интерфейса:
{
public:
virtual int Next() =0;
};
Больше ничего оставлять в основном модуле нельзя. Все остальное должно быть в твоем counter.dll
Ни буквы не прочту. В предложенном Вами примере у меня всё работает идеально и попробуйте с этим поспорить.
Правильно, ни в коем случае неичего не читай и не учись.
А если у пользователя твоя программа упадет (а она упадет, из-за того, что показано выше), то смело отвечай ему: "у меня всё работает идеально и попробуйте с этим поспорить" :D
<---------- А ВОТ ЗДЕСЬ ВСЕ УПАДЕТ -------------->
UpdateDLLProcs(); <- загружается DLL только здесь
Хорошо, моя ошибка. Перенесём UpdateDLLProcs в критическую секцию. Всё будет работать, не так ли?
Для данного случая всё работает. Хотя согласен, что создание классов можно было бы поручить dll, но тем не менее...
Не всё:
Потому что не так. В ту же критическую секцию придётся заключить и Counter::Next(). И каждый из остальных методов класса Counter (сейсас ты синхронизировал пока только "конструктор").
У тебя всё работает потому, что вызов Sleep() в main(), по-видимому, в большенстве тестируемых тобой случаев перекрывает по времени выполнение первой итерации updateWatcher(). У меня же в ста процентах случаев пошагового выполнения программа сыплется с ошибкой 5. Если убрать задержку в main(), крэш получим почти наверняка.
Пакет - группа связанных элементов решения. Грубо говоря, это - единица повторного использования набора связанных классов (впрочем, пакет может быть составным, т. е. содержать подпакеты). Пакет может быть выполнен в виде исполняемого модуля, пространства имён, сборки... Может быть вообще никак не обозначен, в конце концов. Главное здесь то, что пакет - способ логически связанные элементы сгруппировать и некоторым образом от остальных элементов системы изолировать. Так вот, при твоей реализации, сказать что "вот тут у нас всё то, что связано конкретно со счётчиком" сказать ну никак не получится - везде придётся тесно взаимодействовать с системой обновления.
Чем не нравится реально это решение?
Не считая конечно замечания что это "роскошь", комментариев по этому поводу особо не увидел.
Чем не нравится реально это решение?
Не считая конечно замечания что это "роскошь", комментариев по этому поводу особо не увидел.
Нормально. Предложенную hardcase схему можно улучшить планированием запуска загрузчика обновлений (используя, например, планировщик задач Windows, если речь идёт об этой ОС).
Если установить подсистему обновления как Windows-service, то можно и собственный шедулинг организовать. Хотя, нужно ли это - решать топикстартеру.
Есть же специальные решения в виде application-wrapper, которые позволяют разнообразные приложения устанавливать как Windows-сервисы с дополнительными примочками (типа перезапуск в случае креша, отслеживание лога на появление подозрительных записей и т.п.).
Сам по специализации знаю такое решение на Java - http://wrapper.tanukisoftware.org/doc/english/download.jsp. Но думаю что и для других платформ такие должны быть.
Есть же специальные решения в виде application-wrapper, которые позволяют разнообразные приложения устанавливать как Windows-сервисы с дополнительными примочками (типа перезапуск в случае креша, отслеживание лога на появление подозрительных записей и т.п.).
Сам по специализации знаю такое решение на Java - http://wrapper.tanukisoftware.org/doc/english/download.jsp. Но думаю что и для других платформ такие должны быть.
Действительно, идея выполнить систему обновления в виде службы весьма неплоха. Стандартный планировщик задач - тоже служба (schedule), но твой вариант, кажется, гораздо гибче :)