Вначале определим цели этой реализации:
- При обнаружении новой версии обновление должно происходить автоматически;
- После обновления программа должна автоматически перезапускаться;
- После обновления имя программы должно остаться прежним.
Проблема состоит в том, что программа не может саму себя удалить, заменить и вновь запустить. И, казалось бы, как решить этот вопрос? Здесь нам поможет второй файл, отвечающий за переименование и перезапуск программы, так как мы не гонимся за целью хранить все коды в 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/
Добавить комментарий