Автоматическая проверка обновлений программы [C#]

0

Когда-то писал статью, как сделать автоматическую проверку программы. Там был описан способ с использованием связки Delphi + PHP. В этой же статье опишу, как сделать тоже самое, только без использования PHP. Достаточно будет простого сервачка.
Кстати, в примере будет использоваться Inno Setup для создания инсталлятора. С его помощью намного удобнее как устанавливать программу, так и обновлять ее (будет использоваться «тихий» режим установки при обновлении).

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

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

1.0.0;http://site.com/program.exe

Апдейтер будет получать эти параметры, переходить по ссылке, парсить содержимое файлика, сверять версию в файлике с версией, которую ему передала программа и если версия в файлике больше, то скачивать последнюю версию программы (инсталлятор) и запускать с ключем /VERYSILENT.
Как видите, ничего сложного 

Собственно, ниже приведен код класса Updater:

internal class Updater
{
	private readonly Version _currentVersion;
	private readonly string _fileLink;
	private readonly WebClient _webClient;
	private bool _isDownloaded;
 
	public int CurrentUpdProgress { get; private set; }
 
	public Updater( Version currentVersion, string fileLink )
	{
		this._currentVersion = currentVersion;
		this._fileLink = fileLink;
		this._webClient = new WebClient { Encoding = Encoding.UTF8 };
		this._webClient.DownloadProgressChanged += this._webClient_DownloadProgressChanged;
		this._webClient.DownloadFileCompleted += this._webClient_DownloadFileCompleted;
	}
 
	/// <summary>
	/// Срабатывает, когда файл скачался
	/// </summary>
	/// <param name="sender"></param>
	/// <param name="e"></param>
	private void _webClient_DownloadFileCompleted( object sender, System.ComponentModel.AsyncCompletedEventArgs e )
	{
		this._isDownloaded = true;
	}
 
	/// <summary>
	/// Срабатывает во время изменения прогресса скачивания
	/// </summary>
	/// <param name="sender"></param>
	/// <param name="e"></param>
	private void _webClient_DownloadProgressChanged( object sender, DownloadProgressChangedEventArgs e )
	{
		this.CurrentUpdProgress = e.ProgressPercentage;
	}
 
	/// <summary>
	/// Ассинхронное скачивание строки
	/// </summary>
	/// <param name="url">Ссылка</param>
	/// <returns></returns>
	private async Task<string> DownloadStringAsync( string url )
	{
		return await TaskEx.Run( () =>
		{
			string result;
			try
			{
				result = this._webClient.DownloadString( url );
			}
			catch
			{
				result = "";
			}
			return result;
		} );
	}
 
	/// <summary>
	/// Получает ссылку на скачивание последней версии
	/// </summary>
	/// <returns>Возвращает ссылку на последнюю версию или пустую строку, если обновление не требуется</returns>
	private async Task<string> GetReleaseLink()
	{
		string fileContent = await this.DownloadStringAsync( this._fileLink );
		if ( string.IsNullOrEmpty( fileContent ) )
		{
			return string.Empty;
		}
		// парсим строку текстового файла
		string[] dataArr = fileContent.Split( ';' );
		var fileVersion = new Version( dataArr[ 0 ] );
		string releaseLink = dataArr[ 1 ];
		return this._currentVersion >= fileVersion ? "" : releaseLink;
	}
 
	/// <summary>
	/// Проверяет обновления
	/// </summary>
	/// <returns></returns>
	public async Task CheckForUpdates()
	{
		// получаем ссылку на последнюю версию
		string releaseLink = await this.GetReleaseLink();
		if ( string.IsNullOrEmpty( releaseLink ) )
		{
			return;
		}
		// получаем путь для сохранения инсталлятора
		string fileName = Path.GetTempPath() + "updater_example_setup.exe";
		this._isDownloaded = false;
		try
		{
			this._webClient.DownloadFileAsync( new Uri( releaseLink ), fileName );
		}
		catch
		{
			return;
		}
		Process[] waControllers = Process.GetProcessesByName( "Program" );
		// закрываем экземпляры нашей программы
		Array.ForEach( waControllers, p => p.CloseMainWindow() );
		while ( !this._isDownloaded )
		{
			await TaskEx.Delay( 100 );
		}
		// запускаем инсталлятор с параметром /VERYSILENT
		Process.Start( fileName, "/VERYSILENT" );
	}
}

И пример использования:
Конструктор главной формы апдейтера:

public FrmMain( string[] args )
{
	this.InitializeComponent();
	if ( args.Length == 2 )
	{
		Version version = null;
		string fileLink = string.Empty;
		try
		{
			version = new Version( args[ 0 ] );
			fileLink = args[ 1 ];
		}
		catch ( Exception )
		{
			Application.Exit();
		}
		this._updater = new Updater( version, fileLink );
	}
}

в нем берем аргументы командной строки и передаем их в конструктор класса Updater.
В обработчике события Load главной формы следует вызвать метод CheckForUpdates, код которого представлен ниже:

private async Task CheckForUpdates()
{
	try
	{
		await this._updater.CheckForUpdates();
	}
	finally
	{
		Application.Exit();
	}
}

В этом методе вызывается метод CheckForUpdates класса Updater. В нем реализован алгоритм, о котором я писал выше: вызываем метод GetReleaseLink (получение ссылки для скачивания инсталлятора), который возвращает либо строку со ссылкой, либо пустую строку (это значит, что обновление не требуется). Если была возвращена ссылка, то скачиваем инсталлятор во временную папку, закрывает главное окно программы и запускаем инсталлятор с ключем /VERYSILENT. Инсталлятор автоматически обновит программу и запустит ее после обновления, при этом не будет выдавать никаких окон и т.п. (по крайней мере, не должен).

Теперь перейдем к программе.
В программе нужно объявить несколько переменных:

private readonly Version _version = new Version( 1, 0, 0 );
private const string VersionFileLink = "https://github.com/dredei/UpdaterExample/raw/master/version.txt";
private const string UpdaterName = "Updater";

первая хранит в себе текущую версию программы (ее следует изменять, после выпуска новой версии), вторая константа содержит в себе ссылку на тот самый файлик, в котором хранится последняя версия программы и ссылка на нее и третья — название файла апдейтера (чисто для удобства).
В обработчике Load главной формы программы следует вызвать метод CheckForUpdates, код которого представлен ниже:

/// <summary>
/// Запускает программу для проверки обновлений
/// </summary>
/// <returns></returns>
private async Task CheckForUpdates()
{
	string updaterFileName = Application.StartupPath + "\\" + UpdaterName + ".exe";
	if ( !File.Exists( updaterFileName ) )
	{
		return;
	}
	// 
	Process.Start( updaterFileName, this._version + " " + VersionFileLink );
	await TaskEx.Delay( 1500 );
	Process[] updater = Process.GetProcessesByName( UpdaterName );
	if ( updater.Length == 0 )
	{
		return;
	}
	// Ожидаем завершения Updater'а
	updater[ 0 ].WaitForExit();
}

Этот метод запустить апдейтер с нужными параметрами (текущей версией и ссылкой) и будет ждать завершения работы апдейтера.
Вот и всё 

Исходники:

Кстати, в репозитории можно скачать рабочий пример и посмотреть наглядно, как это выглядит.

Кстати, в репозитории можно скачать рабочий пример и посмотреть наглядно, как это выглядит.
Скачивать нужно файл updater_example_setup_1.0.0.exe , установить и запустить программу. После запуска появится окно, в котором можно увидеть текущую версию:

через несколько секунд программа обновится и версия будет уже другой

0
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
Авторизация
*
*
Регистрация
*
*
*
Пароль не введен
*
Генерация пароля
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x
()
x