- PVSM.RU - https://www.pvsm.ru -

Автоматическое обновление программ на C#. Часть 2

Несколько дней назад мной была написана статья о реализации автоматического обновления программного обеспечения [1] на языке C#.

Приняв во внимание конструктивную критику комментаторов, было принято решение улучшить тот код, добавив несколько новых возможностей, включая улучшение «старых»:

  • Автоматическая проверка, скачивание и установка обновлений;
  • Предоставление пользователю возможности выбора момента обновления (новое);
  • Улучшен механизм проверки версии файла;
  • Проверка целостности файла обновления (новое)

Дабы не перепечатывать текст прошлой статьи [1], в этой акцентирую внимание лишь на переработанных частях кода.

Основные замечания

Комментаторами предыдущей статьи были выявлены следующие недочеты кода (указываю только те, на которые опирался):

  • Только лучше сначала скачать новый файл, чтобы не получилась ситуация, когда посередине загрузки, по каким-либо причинам, приложение завершается и пользователь остаётся с .bak файлов и неполным .exe, т.е. без рабочего приложения. (DarkByte [2]);
  • Только не увидел проверки на контрольную сумму. (naum [3]);
  • Показывать пользователю модальное (скорее всего) информационное окно с единственной кнопкой «Ok», не оставляя выбора — не самая хорошая практика. Нужно делать всё тихо и незаметно, либо ненавязчиво предложить обновиться с возможностью сделать это как-нибудь потом, когда пользователю будет удобнее. (iroln [4]);
  • Сравнение версий у вас неправильное. Проблемы будут, например, при обновлении с 9.12.2 до 10.0.0. (eyeless_watcher [5])
  • А что мешает сравнивать непосредственно типы Version? (teleavtomatika [6]).

Были, конечно, и другие комментарии схожие с этими, но не стал их все приводить и предлагаю приступить к рассмотрению проблем.

Внесение поправок

Так как при скачивании файла может возникнуть какая-либо ошибка, в следствие чего файл будет поврежден, в код были внесены следующие поправки:

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 для проведения операций по замене основного файла программы. Расписывать подробнее не стану, так как это уже было раннее [1].

Таким образом, мы убедимся в целостности файла обновления.

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 варианта развития событий:

  1. Соглашаясь на обновление, программа моментально перезапускается, произведя все необходимые действия;
  2. Отказываясь от обновления программа хранит файл рядом с исполняемым файлом, куда и было скачано. В данном варианте процесс обновления произойдет при следующем запуске программы не выводя никаких уведомлений.

Заключение

Переписанный код получился более совершенным по сравнении с предыдущим вариантом, а также исключает вероятность применения обновления на «битом» файле, а также отсутствие реализации функции проверки контрольной суммы скачанного файла с версией на сервере.

И отдельное спасибо хотелось бы сказать следующим людям за их конструктивную критику: DarkByte [2], naum [3], iroln [4], eyeless_watcher [5], teleavtomatika [6], wire [7].

С уважением, Андрей Helldar!

P.S.: люди добрые из числа минусующих — будьте добры в комментариях пишите почему Вы так решили. Интересно же знать где я не прав.

Автор: Helldar

Источник [8]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/58095

Ссылки в тексте:

[1] автоматического обновления программного обеспечения: http://habrahabr.ru/post/217325/

[2] DarkByte: http://habrahabr.ru/users/darkbyte/

[3] naum: http://habrahabr.ru/users/naum/

[4] iroln: http://habrahabr.ru/users/iroln/

[5] eyeless_watcher: http://habrahabr.ru/users/eyeless_watcher/

[6] teleavtomatika: http://habrahabr.ru/users/teleavtomatika/

[7] wire: http://habrahabr.ru/users/wire/

[8] Источник: http://habrahabr.ru/post/217633/