Несколько лет назад, программируя еще на Delphi, лично для себя соорудил некий код автоматического обновления, который в последствии стал незаменимым при разработке любой программы, где есть обновление. В настоящий момент этот код полностью переписан на c# и я хочу с Вами им поделиться.
Проблема состоит в том, что программа не может саму себя удалить, заменить и вновь запустить. И, казалось бы, как решить этот вопрос? Здесь нам поможет второй файл, отвечающий за переименование и перезапуск программы, так как мы не гонимся за целью хранить все коды в 1 файле.
В силу своей лени искать оптимальный вариант, на сайте было выложено 2 файла:
Да, именно XML-формат использую. Забегая вне темы скажу, что в файле version.xml у меня находится список нескольких версий файлов, но мы рассмотрим только одну.
Идем дальше. Структура файла версий выглядит следующим образом:
На форму добавлен компонент backgroundWorker (для реализации фоновой загрузки файла) со следующим кодом внутри обработчика DoWork:
Что мы видим в коде выше:
Так как версия у нас может иметь большое число, используем тип переменной double. Для сравнения версий мы удаляем все точки и конвертируем версию из строки в число (в примере получится число 10237).
Точно также мы поступим и с версией самого файла, присвоенной переменной thisVersion.
После этого нам нужно сравнить локальную версию с удаленной и если наша версия меньше удаленной, то сперва выводим сообщение, информирующее о дальнейшем обновлении. После этого программа начинает скачивать файл в ту же папку, откуда она запущена. Файлу присваивается имя temp_myprogram.
Для отслеживания статуса загрузки на форму был добавлен компонент progressBar, и в код добавлена функция:
Функция отображает в прогрессбаре статус загрузки файла. Это нужно лишь для наглядного отображения.
Итак, мы загрузили наш файл и что делать дальше? А дальше вступает в бой функция download_Completed, содержащая код:
Здесь все просто: запускаем файл updater.exe с параметрами, о которых расскажу в следующем этапе.
Второй строкой указываем о необходимости принудительного завершения работы приложения.
Далее на помощь приходит утилита updater.exe, функциональной особенностью которой является проверка завершения работы основного приложения и обработка обновления.
Ну да не будем вдаваться в текст и сразу перейдем к коду:
Так как нам не нужны формы, проект собран как обычное консольное приложение, действия которого довольно просты.
Задаем цикл, который проверяет запущен ли процесс, указанный во 2-ом параметре. Если процесс найден, то ему будет передана команда Kill() для принудительного завершения, после чего выжидаем 300 миллисекунд и повторяем. Цикл будет работать до тех пор, пока процесс не завершится.
Далее удаляем старый файл. Для устранения некоторых ошибок (скорее ошибок в мозгу) добавляем функцию проверки существования файла.
После удаления переименовываем имя файла, заданного в 1-ом параметре на имя, заданное во 2-ом параметре. В нашем случае произойдет переименовывание файла temp_myprogram в myprogram.exe, после чего процесс myprogram.exe будет запущен, а окно данного апдейтера закрыто.
Также хочу сказать, что файл программы «updater» я использую во всех своих проектах, где он требуется, так как у него нет привязки к какому-то конкретному приложению.
И переходим к следующему этапу:
И вот мы видим, что обновленный файл версии успешно запустился, а окно «апдейтера» закрылось. Profit!
Статья написана на основании лаунчера для модпака «PROТанки» к игре «World of Tanks» с оригинальными скриншотами приложения. Для тех, кто скажет «нет там этого функционала» сразу скажу, что данный лаунчер находится на бета-тесте и доступен ограниченному количеству лиц.
Если кому будет нужен файл updater.exe, то Вы всегда сможете скачать его актуальную версию ЗДЕСЬ, на моем официальном сайте. В настоящий момент актуальной версией является 1.0.0.2.
И на этой строчке наш код автоматического обновления подходит к концу.
UPD. Мной написана вторая статья, содержащая часть внесенных поправок.
Убедительная просьба, у кого еще имеются мысли по поводу «кривых рук», «кривого кода» и пр., пишите в комментариях хотя бы что не так. Опираясь на Вашу конструктивную критику я улучшу свою работу, тем самым научившись писать более качественный код.
Заранее благодарен!
С уважением, Андрей Helldar!
Вначале определим цели этой реализации:
- При обнаружении новой версии обновление должно происходить автоматически;
- После обновления программа должна автоматически перезапускаться;
- После обновления имя программы должно остаться прежним.
Проблема состоит в том, что программа не может саму себя удалить, заменить и вновь запустить. И, казалось бы, как решить этот вопрос? Здесь нам поможет второй файл, отвечающий за переименование и перезапуск программы, так как мы не гонимся за целью хранить все коды в 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.
И на этой строчке наш код автоматического обновления подходит к концу.
UPD. Мной написана вторая статья, содержащая часть внесенных поправок.
Убедительная просьба, у кого еще имеются мысли по поводу «кривых рук», «кривого кода» и пр., пишите в комментариях хотя бы что не так. Опираясь на Вашу конструктивную критику я улучшу свою работу, тем самым научившись писать более качественный код.
Заранее благодарен!
С уважением, Андрей Helldar!