Несколько дней назад мной была написана статья о реализации автоматического обновления программного обеспечения на языке C#.
Приняв во внимание конструктивную критику комментаторов, было принято решение улучшить тот код, добавив несколько новых возможностей, включая улучшение «старых»:
- Автоматическая проверка, скачивание и установка обновлений;
- Предоставление пользователю возможности выбора момента обновления (новое);
- Улучшен механизм проверки версии файла;
- Проверка целостности файла обновления (новое)
Дабы не перепечатывать текст прошлой статьи, в этой акцентирую внимание лишь на переработанных частях кода.
Основные замечания
Комментаторами предыдущей статьи были выявлены следующие недочеты кода (указываю только те, на которые опирался):
- Только лучше сначала скачать новый файл, чтобы не получилась ситуация, когда посередине загрузки, по каким-либо причинам, приложение завершается и пользователь остаётся с .bak файлов и неполным .exe, т.е. без рабочего приложения. (DarkByte);
- Только не увидел проверки на контрольную сумму. (naum);
- Показывать пользователю модальное (скорее всего) информационное окно с единственной кнопкой «Ok», не оставляя выбора — не самая хорошая практика. Нужно делать всё тихо и незаметно, либо ненавязчиво предложить обновиться с возможностью сделать это как-нибудь потом, когда пользователю будет удобнее. (iroln);
- Сравнение версий у вас неправильное. Проблемы будут, например, при обновлении с 9.12.2 до 10.0.0. (eyeless_watcher)
- А что мешает сравнивать непосредственно типы Version? (teleavtomatika).
Были, конечно, и другие комментарии схожие с этими, но не стал их все приводить и предлагаю приступить к рассмотрению проблем.
Внесение поправок
Так как при скачивании файла может возникнуть какая-либо ошибка, в следствие чего файл будет поврежден, в код были внесены следующие поправки:
public void checkUpdates(){
try
{
if (File.Exists("launcher.update") && new Version(FileVersionInfo.GetVersionInfo("launcher.update").FileVersion) > new Version(Application.ProductVersion))
{
Process.Start("updater.exe", "launcher.update "" + Process.GetCurrentProcess().ProcessName + """);
Process.GetCurrentProcess().CloseMainWindow();
}
else
{
if (File.Exists("launcher.update")) { File.Delete("launcher.update"); }
Download();
}
}
catch (Exception)
{
if (File.Exists("launcher.update")) { File.Delete("launcher.update"); }
Download();
}
}
Вначале мы проверяем существует ли файл обновлений launcher.update, а также проверяем версию файла, так как нам не важно его расширение. В случае, если файл поврежден, сработает обработчик исключений try catch, выполнив код по удалению поврежденного файла с последующим запуском функции проверки и скачивания (если найдены) обновлений на сайте.
Если же файл окажется целым и его версия будет выше текущей, то запускается дополнительная утилита updater.exe для проведения операций по замене основного файла программы. Расписывать подробнее не стану, так как это уже было раннее.
Таким образом, мы убедимся в целостности файла обновления.
private void Download()
{
try
{
XmlDocument doc = new XmlDocument();
doc.Load(@"http://mysite/version.xml");
remoteVersion = new Version(doc.GetElementsByTagName("version")[0].InnerText);
localVersion = new Version(Application.ProductVersion);
if (localVersion < remoteVersion)
{
if (File.Exists("launcher.update")) { File.Delete("launcher.update"); }
WebClient client = new WebClient();
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
client.DownloadFileAsync(new Uri(@"http://mysite/launcher.exe"), "launcher.update");
}
}
catch (Exception) { }
}
Далее претерпела изменения функция процесса проверки версии файла, ключевым моментом которого является реализация сравнения версий файла при помощи встроенных средств System.Version, в следствие чего была устранена проблема корректной сверки версий, скажем, «9.12.2» и «10.0.0».
В случае, когда обнаружена более новая версия, действия происходят по следующему сценарию: программа автоматически в фоновом режиме скачивает файл обновления, затем выдает пользователю сообщение о доступности этого обновления и предлагает на выбор 2 варианта развития событий:
- Соглашаясь на обновление, программа моментально перезапускается, произведя все необходимые действия;
- Отказываясь от обновления программа хранит файл рядом с исполняемым файлом, куда и было скачано. В данном варианте процесс обновления произойдет при следующем запуске программы не выводя никаких уведомлений.
Заключение
Переписанный код получился более совершенным по сравнении с предыдущим вариантом, а также исключает вероятность применения обновления на «битом» файле, а также отсутствие реализации функции проверки контрольной суммы скачанного файла с версией на сервере.
И отдельное спасибо хотелось бы сказать следующим людям за их конструктивную критику: DarkByte, naum, iroln, eyeless_watcher, teleavtomatika, wire.
С уважением, Андрей Helldar!
P.S.: люди добрые из числа минусующих — будьте добры в комментариях пишите почему Вы так решили. Интересно же знать где я не прав.
Автор: Helldar