...

пятница, 28 марта 2014 г.

[Из песочницы] Автоматическое обновление программ на 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.


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий