Помогите плиз! Хочется делать всё по-взрослому :) Подскажите пожалуйста как это можно сделать, чтобы прога заменила сама себя при запуске, когда выяснит, что версия устарела. Особенно меня интересует, как можно выкачать новый экзешник проги или разных компонентов приложения (ocx-ы всякие, dll-ки), из базы MS SQL2000; зарегистрить их и запустить уже обновлённый экзешник. И ещё вопрос можно ли это сделать всё в той проге, которую юзерь запустил, не зная, что она устарела, а не писать дополнительно прогу обновления, которая запускается из шела и всё это делает
Если есть примеры кодов буду очень признатаелен. Или подскажите где есть в инете.
Итак! Подробнейшее описание программы самообновления для .Net Framework 2.0 (Dot Net 2.0)
Язык - C# (C Sharp)
Так как я давно мучился такой проблемой, то пришлось перепробовать многое)) В первую очередь никому не хочется довешивать свои программы сторонними dll'ками. Хочется или встроенного решения от родной библиотеки или исходники проги, чтобы нагло вставить их к себе и жить счастливо.
Встроенное решение для .Net 2.0 - ClickOnce меня не впечатлило. Самая плохая идея у них в том, что необходимо устанавливать приложение в систему =( . Вместо того чтобы просто заменять нужные файлы у вас в папке - она полностью переселяет вашу прогу на сайт. И даёт вам лишь жалкий файлик с расширением ".application" через который вы обречены каждый раз выкачивать свою программу. Конечно тут есть и плюсы: у пользователя всегда самая последняя прога. Все dll'ки не выкачиваются, а подгружаются по мере надобности.
Но у большинства людей проги выполнены в моно варианте. Один exe'шник и всё!
Вариант с bat'ником тоже не вдохновляет. И выглядит слишком устарело и кустарно. Перед пользователем обязательно промелькнёт окно консоли и ваша прога станет ему напоминать либо вирь, либо кряк)) что практически одно и то же)))
Я решил самообновлялку написать полностью своими руками и используя только язык C#.
Поэтому вот мой вариант самообновлялки:
Самообновление состоит из 2х файлов. Первый файл - это собственно ваша основная программа. Будем считать что это Windows Application. Код для самообновления я вставил прямо в класс Program, перед кодом который отвечает за запуск формы. Ведь незачем грузить форму, если окажется, что прога устарела. Пожалейте свой компьютер)))
Для понимания того, что написано ниже нужно залить на ваш сайт txt файл следующего содержания (у нас это вымышленный http://mysite.ru/myprogram/update.txt):
1.0.0.1
http://mysite.ru/myprogram/updater.exe.gz
http://mysite.ru/myprogram/myprogram.exe.gz
Первая строчка - номер самой свежей версии
Вторая строчка - ссылка на программу самообновления
Третья строчка и все последующие - это файлы для обновления.
using System;
using System.Net;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using System.Diagnostics;
namespace SelfUpdate
{
static class Program
{
[STAThread]
staticvoid Main(string[] args)
{
if (File.Exists("updater.exe")) //Проверяем на остатки от процесса обновления
{
System.Threading.Thread.Sleep(2000); //Спим 2 секунды, пока обновлялка закроется
File.Delete("updater.exe"); //Удаляем обновлялку
}
WebClient wc = new WebClient(); //Через ВебКлиент мы скачиваем файлы
conststring updatestring = "http://mysite.ru/myprogram/update.txt"; //Это фиксированная ссылка на сайт, где лежит txt-файл с данными для обновления
conststring updatername = "updater.exe"; //Тоже фиксированное имя обновлялки
Stream stream = null;
try{stream = wc.OpenRead(updatestring);} //Пытаемся скачать txt'шник. Он считывается не в файл, а обрабатывается прямо в памяти.
catch{ goto normalstart; } //Если не удалось типа инет лагает, то просто грузим текущую версию проги
StreamReader read = new StreamReader(stream); //Читалка текста в потоках
string newversion = read.ReadLine(); //Читаем первую строчку
string updater = read.ReadLine(); //Читаем вторую строчку
string[] separator = { "\r\n" }; //Enter - на самом деле выглядит так =)
string[] fileblowfiles = read.ReadToEnd().Split(separator, StringSplitOptions.RemoveEmptyEntries); //Получаем все остальные строчки разделенные Enter'ами
read.Close(); //Закрываем потоки
string version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); //Этот метод получает версию текущей сборки. Т.е. версию вашей программы (Допустим она 1.0.0.0)
if (newversion != version) // Если версии не совпадают - значит прога устарела. Обновляем!
{
string file = updatername;
stream = wc.OpenRead(updater); //Скачиваем и распаковываем ссылку на updater.exe
unzip.UnZip(stream, file); stream.Close();
foreach (string fileblowfile in fileblowfiles) //Далее просто выкачиваем все остальные файлы. Думаю необходимости проверки их доступности нет. Ведь мы уже проверили update.txt на доступность через интернет. Но можно проделать то же самое и с каждый файлом при необходимости.
{
file = fileblowfile.Substring(fileblowfile.LastIndexOf('/') + 1); // превращает "http://mysite.ru/myprogram/myprogram.exe.gz" в "myprogram.exe.gz"
Далее необходимо пояснить, что все файлы на сайте, кроме update.txt запакованы GZip'ом. Им можно сжать, программой 7zip или WinRar. Я просто поставил там все настройки на максимум и всё работает. Дело в том, что Dot Net 2.0 умеет работать с GZip архивами. Так почему бы не съэкономить на трафике. И лишний раз не раздражать пользователя долгой закачкой файлов самообновления.
Вот метод UnZip, который распаковывает данные из потока в файл Я запихнул его в отдельный файл-класс. Комменторовать влом, поэтому просто немного msdn почитайте и про потоки файловые немного. И у вас всё получится . Вот он:
using (FileStream write = File.Create(outputFile))
using (GZipStream unzip = new GZipStream(inputStream, CompressionMode.Decompress))
{
CopyStream(unzip, write);
}
}
}
}
Теперь когда наша прога успешно выкачала все нужные компоненты и распаковала updater.exe, то самое время разобраться что же будет выполняться, когда он запустится.
updater.exe выполнен в виде Windows Application, удалены все строчки отвечающие за загрузку формы. И сама форма из проекта тоже удалена. Короче у вас должен остаться только один Program.cs и unzip.cs, в котором всё тот же метод для распаковки архивов.
К слову стандартные Dot Net 2.0 проги сжимаются GZip'ом чуть ли не 5-7 раз.
using System;
using System.Net;
using System.IO;
using System.Diagnostics;
namespace SelfUpdate
{
static class Program
{
[STAThread]
staticvoid Main(string[] args)
{
if (!File.Exists("myprogram.exe")) return; //Если рядом нет нашей основной проги, то возможно кто-то просто случайно её запустил))) выходим =)
for (int i = 0; i < 5; i++) //5 раз пытаемся удалить основной файл
string[] files = Directory.GetFiles(Environment.CurrentDirectory, "*.gz", SearchOption.TopDirectoryOnly); //Получаем все файлы в текущей папке с расширением ".gz"
foreach (string file in files) //Для каждого такого файла делаем...
{
Stream stream = File.OpenRead(file); //Открываем файл для чтения
unzip.UnZip(stream, Path.ChangeExtension(file, null)); //Распаковываем в файл с таким же именем, только убираем расширение ".gz"
stream.Close(); File.Delete(file); //Закрываем поток и удаляем уже ненужный архив.
} //После того как все архивы распаковали. В том числе и основной файл, то...
Process.Start("myprogram.exe"); //Запускаем уже новую версию нашей основной программы.
}
}
}
Вот и всё!
Если вдуматься, то ничего сложного)))
Плюсы данной технологии самообновления:
- Файлы передаются в сжатом виде. Экономится время обновления и трафик пользователя
- Просто проверка версии занимаем в реале около 2-4 секунд. А трафик как вы уже поняли составляет размер того самого txt-файла, а значит это около 1 КБ (плюс-минус расходы на соединение и ошибки на линии )
- Обновление идёт в скрытом режиме. Пользователь ничего не замечает и просто ждёт.
- Обновлять можно целые списки файлов. А если немного дописать updater.exe, то можно даже структуру каталогов сохранять при распаковке.
Минусы:
- Всё таки тратится время на обновление. И у пользователя совсем не спрашивает разрешения))) Скорее всего фаерволы и касперские всякие заверещат о сетевой угрозе))) но это не так страшно.
- Нужно надыбать где-то GZip архиватор для подготовки файлов (7zip, WinRar)
- И самый огромный минус по сравнению с ClickOnce - нужно вручную готовить каждый новый релиз обновления. Заливать update.txt на сайт... Проверять работу всего этого...
Если вы не боитесь всего этого, то этот метод самообновления для ВАС! Удачи и до новых встреч!
Written by Mozgoed for .Net 2.0
Mozgoed пишет:
Встроенное решение для .Net 2.0 - ClickOnce меня не впечатлило. Самая плохая идея у них в том, что необходимо устанавливать приложение в систему =( . Вместо того чтобы просто заменять нужные файлы у вас в папке - она полностью переселяет вашу прогу на сайт. И даёт вам лишь жалкий файлик с расширением ".application" через который вы обречены каждый раз выкачивать свою программу. Конечно тут есть и плюсы: у пользователя всегда самая последняя прога. Все dll'ки не выкачиваются, а подгружаются по мере надобности.
Вы похоже плохо разобрались с технологией ClickOnce.
Приложение может работать как полностью в "онлайновом" режиме (при запуске оно каждый раз скачивается с сайта), так и в "офлайновом" (все файлы приложения, в том числе и exe устанавливаются на жестком диске и оттуда же запускаются).
Автоматическое обновление также имеет определенные настрйоки (его можно вообще отключить, можно сделать чтоб оно выполнялось по расписанию, и что выполнялось в фоновом режиме - после запуска не нужно будет ждать ответа сервера).
Эй, умник.. Твой код, конечно впечатляет.. я написал почти такой же на VB.NET (потом твой увидел)
wc.DownloadFile(fileblowfile, file);
А ЧТО ЕСЛИ .EXE весит >6 Mb ?!
Я написал на этот случай такой код:
Private WithEvents webClient As New System.Net.WebClient
Private Sub DownloadFile()
webClient.DownloadFileAsync(New Uri("ftp://USERNAME:PASSWORD@ftp.HOST.org/PROGRAM.EXE", CurDir() & "\PROGRAM.exe"
End Sub
Private Sub webClient_DownloadProgressChanged(ByVal sender As System.Object, ByVal e As System.Net.DownloadProgressChangedEventArgs) Handles webClient.DownloadProgressChanged
ProgressBar1.Value = e.ProgressPercentage
End Sub
У меня система основана на обновлении основной проги с помощью рядом лежащей (кусок которой приведён выше). Т.е. основная прога только проверяет, есть ли обновления (кстати, почти также, как у тебя, Mozgoed, только покруче (у меня в шестнадцатиричном виде)). А если есть, то уже вызывает обновляльщика.
Но вот только обновляльщик очень-очень медленно качает и прогрессбар не движется, хотя должен.
Кстати, чтоб ничего не мелькало, как говорит Mozgoed, можно, чтоб прога генерировала не bat-ник, а vbs (Visual Basic Script).. Но проблема в том, что не на абсолютно любом компе эти скрипты работают. (я сталкивался с такой фиговиной)
Не знаю с чем это связано..
Да провайдер-то вроде норм.. И остальное всё качается норм...
А вот у моего обновляльщика скорость что-то совсем маленькая.. (~3Kбайт\сек)
не понятно что ты хочешь
Я хочу, чтоб качалось быстрее и прогрессбар двигался =D
Т.к.
e.BytesReceived
увеличивается (сколько байт скачано), а прогрессбар не движется.
И ещё я хотел сказать, что WebClient.DownloadFile() неприемлем для больших файлов, т.к. он блокирует весь поток (окно соответственно будет '(Не отвечать)') и у юзера может начаться паника..
Так что разумнее использовать WebClient.DownloadFileAsync()
Ха-ха-ха-ха.... я же не совсем идиот, чтоб не проверить вызывается ли евент...
Эвент-то вызывается, и в евенте даже
e.BytesReceived
(показывает скачанные байты) меняется.
А вот e.ProgressPercentage не меняется..
Хотя в оригинальном примере, по которому я и научился делать такую штуковину всё работает.
А уж если я сделаю PerformStep, то прогрессбар вообще уйдёт в небытие (т.к. там качать ого-го сколько) и вызовет исключение, о том что
значит, во-первых, не используй ProgressPercentage, он может быть в этой эвенте для других целей, а во-вторых, ПерформСтеп не должен вызывать ничего критичного. юзай его один раз, после каждого PBProgess.Value = ...