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

от автора

Несколько лет назад, программируя еще на Delphi, лично для себя соорудил некий код автоматического обновления, который в последствии стал незаменимым при разработке любой программы, где есть обновление. В настоящий момент этот код полностью переписан на c# и я хочу с Вами им поделиться.

image

Вначале определим цели этой реализации:

  1. При обнаружении новой версии обновление должно происходить автоматически;
  2. После обновления программа должна автоматически перезапускаться;
  3. После обновления имя программы должно остаться прежним.

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

Этапы

Этап 1: Проверка версии

В силу своей лени искать оптимальный вариант, на сайте было выложено 2 файла:

  • myprogram.exe
  • version.xml

Да, именно XML-формат использую. Забегая вне темы скажу, что в файле version.xml у меня находится список нескольких версий файлов, но мы рассмотрим только одну.

Идем дальше. Структура файла версий выглядит следующим образом:

<version>      <myprogram>1.0.2.37</myprogram> </version>

На форму добавлен компонент backgroundWorker (для реализации фоновой загрузки файла) со следующим кодом внутри обработчика DoWork:

try 	{ 		double versionRemote = Convert.ToDouble(doc.GetElementsByTagName("myprogram")[0].InnerText.Replace(".", "")), 				thisVersion = Convert.ToDouble(Application.ProductVersion.Replace(".", ""));  		if (thisVersion < versionRemote) 		{ 			MessageBox.Show(this, "Обнаружена новая версия (" + doc.GetElementsByTagName("myprogram")[0].InnerText + ")" + Environment.NewLine + 				"Приложение будет автоматически обновлено и перезапущено.", Application.ProductName + " v" + Application.ProductVersion, MessageBoxButtons.OK, MessageBoxIcon.Information);  			var client = new WebClient(); 			client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(download_ProgressChanged); 			client.DownloadFileCompleted += new AsyncCompletedEventHandler(download_Completed); 			client.DownloadFileAsync(new Uri(@"http://mysite/myprogram.exe"), "temp_myprogram"); 		} 	} catch (Exception) { }

Что мы видим в коде выше:
Так как версия у нас может иметь большое число, используем тип переменной double. Для сравнения версий мы удаляем все точки и конвертируем версию из строки в число (в примере получится число 10237).
Точно также мы поступим и с версией самого файла, присвоенной переменной thisVersion.

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

Для отслеживания статуса загрузки на форму был добавлен компонент progressBar, и в код добавлена функция:

private void download_ProgressChanged(object sender, DownloadProgressChangedEventArgs e) { 	try 		{ 			progressBar1.Value = e.ProgressPercentage; 		} 	catch (Exception) { } } 

Функция отображает в прогрессбаре статус загрузки файла. Это нужно лишь для наглядного отображения.
Итак, мы загрузили наш файл и что делать дальше? А дальше вступает в бой функция download_Completed, содержащая код:

private void download_Completed(object sender, AsyncCompletedEventArgs e) { 	try 	{ 		Process.Start("updater.exe", "temp_myprogram myprogram.exe"); 		Process.GetCurrentProcess().Kill(); 	} 	catch (Exception) { } } 

Здесь все просто: запускаем файл updater.exe с параметрами, о которых расскажу в следующем этапе.
Второй строкой указываем о необходимости принудительного завершения работы приложения.

Этап 2: Обработка обновления

Далее на помощь приходит утилита updater.exe, функциональной особенностью которой является проверка завершения работы основного приложения и обработка обновления.
Ну да не будем вдаваться в текст и сразу перейдем к коду:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.IO; using System.Threading;  namespace Updater {     class Program     {         static void Main(string[] args)         {             try             { 				string process = args[1].Replace(".exe", ""); 			                 Console.WriteLine("Terminate process!");                 while (Process.GetProcessesByName(process).Length > 0)                 {                     Process[] myProcesses2 = Process.GetProcessesByName(process);                     for (int i = 1; i < myProcesses2.Length; i++) { myProcesses2[i].Kill(); } 					 					Thread.Sleep(300);                 }  				if(File.Exits(args[1])){ File.Delete(args[1]); } 				 				File.Move(args[1], args[0]); 				                 Console.WriteLine("Starting "+args[1]);                 Process.Start(args[1]);             }             catch (Exception) { }         }     } } 

Так как нам не нужны формы, проект собран как обычное консольное приложение, действия которого довольно просты.
Задаем цикл, который проверяет запущен ли процесс, указанный во 2-ом параметре. Если процесс найден, то ему будет передана команда Kill() для принудительного завершения, после чего выжидаем 300 миллисекунд и повторяем. Цикл будет работать до тех пор, пока процесс не завершится.

Далее удаляем старый файл. Для устранения некоторых ошибок (скорее ошибок в мозгу) добавляем функцию проверки существования файла.
После удаления переименовываем имя файла, заданного в 1-ом параметре на имя, заданное во 2-ом параметре. В нашем случае произойдет переименовывание файла temp_myprogram в myprogram.exe, после чего процесс myprogram.exe будет запущен, а окно данного апдейтера закрыто.
Также хочу сказать, что файл программы «updater» я использую во всех своих проектах, где он требуется, так как у него нет привязки к какому-то конкретному приложению.

И переходим к следующему этапу:

Этап 3: Завершение

И вот мы видим, что обновленный файл версии успешно запустился, а окно «апдейтера» закрылось. Profit!

Статья написана на основании лаунчера для модпака «PROТанки» к игре «World of Tanks» с оригинальными скриншотами приложения. Для тех, кто скажет «нет там этого функционала» сразу скажу, что данный лаунчер находится на бета-тесте и доступен ограниченному количеству лиц.

Если кому будет нужен файл updater.exe, то Вы всегда сможете скачать его актуальную версию ЗДЕСЬ, на моем официальном сайте. В настоящий момент актуальной версией является 1.0.0.2.

И на этой строчке наш код автоматического обновления подходит к концу.

ссылка на оригинал статьи http://habrahabr.ru/post/217325/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *