Pull to refresh

Comments 28

Для своих программ использую несколько другой подход — Во первых — сама программа НЕ проверяет обновления, там просто нет такой функции, всё делается внешним апдейтером.
Во вторых — пользователь должен иметь возможность контролировать и процесс загрузки, и процесс обновления, то есть загружаться только после подтверждения пользователя, так же желательно чтобы была возможность вообще отключить обновления.
В третьих — если вы хотите реализовать проверку корректности, то не проще в ваш серверный xml добавить CRC код и потом после скачивания просто его проверить?

PS: А вообще (если не нравиться windows installer и ClickOnce) есть разных библиотеки для обновления. Из первого попавшегося с кодеплекса — autoupdaterdotnet.codeplex.com
Здесь уже зависит от назначения самой программы — где-то уместно будет использовать сторонний апдейтер, а где-то — нет

так же желательно чтобы была возможность вообще отключить обновления.

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

По «в-третьих» — CRC-код добавлял, но либо у меня руки кривые, либо одно из двух — суммы не совпадают. Над этим вариантом я еще думаю где ошибка.
Действительно, у меня были кривые руки. В комментарии ниже приведен пример рабочего кода.
Зря вы написали вторую часть статьи. Сейчас вам еще листьев накидают в карму. Самое главное, что вы упустили, читая комментарии к вашей предыдущей статье, так это то, что описанный вами опыт практически невозможно использовать в других решениях. Также совершенно не понятно почему вы решили только часть описанных проблем. И что это за метод проверки целостности файла? (методом чтения заголовков и метаданных!?)
Не ради кармы пишу статьи, а с целью донести информацию до начинающих пользователей, не исключая комментирование более опытных.
По поводу использования описанного метода в других приложениях, можно сам код апдейтера полностью вынести во внешний файл и, для универсальности, при запуске добавлять параметры где лежат обновления и какой файл заменять.
Часть проблем описал потому, что именно на них акцентировали внимание комментарии, некоторые же проблемы (вроде предложения BITS, windows installer и ClickOnce) нецелесообразны в моем проекте.
Да, возможно при создании универсального кода они найдут место, но, ведь, пример-то не на универсальность мной приводился, а на наглядное представление принципа автоматического обновления ПО.
Метод проверки целостности файла довольно примитивен: берем файл и считываем из него версию, если файл поврежден — это неизбежно вызовет ошибку. Легко и просто, нежели добавлять дополнительный код проверки CRC-суммы. Во всяком случае, в моем проекте нет нужды использовать более сложные методы, а так да, Вы правы, нужно использовать проверку контрольной суммы.
>>Метод проверки целостности файла довольно примитивен: берем файл и считываем из него версию, если файл поврежден — это неизбежно вызовет ошибку. Легко и просто, нежели добавлять дополнительный код проверки CRC-суммы.

Хочется взять и свернуть вам шею
Вестимо, был не прав? Я угадал?

Раз так, то и нижеприведенную функцию оцените, если желание будет:

        private bool checksum(string filename, string summ)
        {
            try
            {
                using (var md5 = MD5.Create())
                {
                    using (var stream = File.OpenRead(filename))
                    {
                        return md5.ComputeHash(stream).ToString() == summ ? true : false;
                    }
                }
            }
            catch (Exception)
            {
                return false;
            }
        }

В параметре функции передается имя файла, для которого необходимо посчитать CRC-сумму, а во втором контрольная сумма, полученная с сайта (размещена в файле version.xml как дополнительный аттрибут).
Суть проста — считываем CRC-сумму файла и сравниваем ее с полученной с сайта. Если суммы совпадают, выводится true, если нет — false.
Вот только проблема в том, что сумма рассчитывается как-то не так — еще ни разу она не совпала. Уже голову сломал в чем проблема…
На сайте файл хранится под именем launcher.exe, локально — launcher.update.
Пробовал локально посчитать суммы обоих файлов — они одинаковы. Уже голова не варит что может быть. Может, подопнете в нужном направлении? Буду рад :)
Нашел свою кривость рук!

Вот так код работает стабильно:

private bool Checksumm(string filename, string summ)
{
	using (FileStream fs = File.OpenRead(filename))
	{
		System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
		byte[] fileData = new byte[fs.Length];
		fs.Read(fileData, 0, (int)fs.Length);
		byte[] checkSumm = md5.ComputeHash(fileData);
		return BitConverter.ToString(checkSumm).Replace("-", String.Empty) == (summ).ToUpper() ? true : false;
	}
}
Вестимо, был не прав? Я угадал?

Да. Потому что файл из которого можно прочитать версию не обязательно рабочий. (простейший пример — это заражение вирусом, версия читается, файл запускается, но уже не то :) ).
Навскидку:
1. Некорректное название. С# используется для написания софта под совершенно разные платформы, от WP7 до винды. Под версией .NET, которая на Windows Phone, ваш код работать не будет.

2.
catch (Exception) { }


Не стыдно такое выкладывать?

3.
public void checkUpdates(){


private void Download()


У вас какой naming convention вообще? В C# методы начинаются с большой буквы.
Совсем не стыдно.

При поиске информации очень часто добавляют язык C# в запросе, дабы найти нужное и, поверьте, если ввести в любом поисковике запрос «автоматическое обновление C#», то вряд ли человек будет иметь ввиду Windows Phone.

Кто сказал, что самостоятельно придуманные методы начинаются с большой буквы? Это все глупый стереотип! Как метод ни назови — главное, чтобы он вызывался. Вот если бы в одном месте было написано «download», а в другом — «Download», тогда да, а в данном случае не пойму почём звон.

И по Вашему комментарию в целом: Вы сможете лучше? Примеры приведете? Думаю, многие были бы рады взглянуть на мастер-класс от профи…

P.S.: Если бы данный код был абсолютно совершенен, то вряд ли я стал его выкладывать на всеобщее…
>>Кто сказал, что самостоятельно придуманные методы начинаются с большой буквы? Это все глупый стереотип! Как метод ни назови — главное, чтобы он вызывался. Вот если бы в одном месте было написано «download», а в другом — «Download», тогда да, а в данном случае не пойму почём звон.

В .Net есть строго оговоренный naming convention. Следует соблюдать его.

>>И по Вашему комментарию в целом: Вы сможете лучше? Примеры приведете? Думаю, многие были бы рады взглянуть на мастер-класс от профи…

Сперва добейся, да?
В таком случае не пойму почему обработчик на ту же кнопку выглядит так:
private void button1_Click(object sender, EventArgs e) { }

С маленькой же буквы начинается…

Исходя из этого могу предположить, что в документации лишь рекомендуемые понятия, а не принудительно выполняемые.
Ну это исключение из правил, т.к. метод генерируется автоматически. А с маленькой буквы начинается, потому что имя кнопки тоже с маленькой буквы.
В таком разе логично предположить, что и созданные разработчиком функции также могут начинаться с маленькой буквы.
Я хоть и новичок в этой среде, но уже успел понять, что неважно с какой буквы функция начинается, главное — чтобы она при вызове была 1 в 1 с названием.

А раз функция, начинающаяся с маленькой буквы, работает, то и ее можно отнести к исключениям?
А раз функция, начинающаяся с маленькой буквы, работает, то и ее можно отнести к исключениям?
Нет, потому что такая функция нарушает general naming conventions (я пофиксил ссылку, которую вам дали ниже).

Если хотите,
private void button1_Click(object sender, EventArgs e) { }
— это тоже naming convention, только для обработчиков событий. Паттерн легко заметен: %Control%_%Event%. Если %Control% назван с маленькой буквы, фактически имя функции-обработчика тоже будет начинаться с маленькой буквы, и это правильно.
Я б плюс поставил, да кармы мало.
Спасибо за объяснение!
Для дотнета есть официальный naming convention:
msdn.microsoft.com/en-us/library/ms229045(v=vs.110).aspx

В дотнете имена методов и свойств должны называться с большой буквы и не иметь подчеркиваний.
Пока вы пишете код, который никто не будет читать, можете забить на любые требования, но как только этот код ктото захочет посмотреть, он будет долго плеваться. Я, читая чей-то код, из стиля имени идентикатора всегда делаю предположение о том, что это за идентификатор — локальная переменная/параметр, метод, свойство, приватное поле и т.д.
Несоблюдение правил именования — очень большая проблема, испытано на личном опыте.
Ага! Вот где собака зарыта! Теперь понял для чего такие различия. Спасибо за подробное объяснение!
Также по пункту 2 (try/catch).
Best practice не применять код вида
try { //Код } catch(Exception e) { //Код }
Тем более не делать, как у вас — с пустой обработкой ошибок — это нужно ОЧЕНЬ редко. При вашем коде вообще не понятно выполнился ли код или нет. И если была ошибка, то какая она.

Исключения стоит ловить при:

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

Также очень желательно прописывать finally

Для работы с базой данных/файлами/другими IDisposable вещами лучше использовать конструкцию using. Это тот же try/catch/finally, с корректно написанным блоком finally.

Это в примере код пустой, а так там обработчик стоит, у которого принцип работы довольно прост: передаем 3 переменные, 1-я из которых является текстовой строкой функции, вторая местом вызова (так как в функции может быть несколько обработчиков) и третья — текст ошибки.
Все это дело сохраняется в файл и по завершении работы программы архивируется. Архив в последствии пользователь может сам передать разработчику, так как функции автоматической отправки я не делал.
В WebClient есть Dispose, не стесняйтесь обернуть его в using.
>>люди добрые из числа минусующих — будьте добры в комментариях пишите почему

Наверное минусующие по колено в коде :)
Мне тоже так кажется, и вылезают лишь посмеяться над другими да вернуться обратно дебажить свой код. :)
Вы учитесь — это хорошо.
У вас есть так или иначе работающий обновлятор — это хорошо.
В качестве учебного пособия по написанию обновляторов — ваш код плох.

Желаю вам выпустить этот обновлятор в широкие массы и понаступать на различные грабли — встречаясь с реальными ошибками и учась их решать, вы обретёте нужный опыт.
Спасибо на добром слове!
«Ученье — свет, а неученье — чуть свет и на работу» (с)
Как быть в ситуации: Скачали новую версию, в которой были внесены изменения в функцию автообновления, но оказалось что при внесении изменений были допущены ошибки и функция больше не работает. Можете рассуждать о недостаточном тестировании, но что есть то есть.

Мне приходят два варианта:
1) Вынести функцию апдейт в отдельный исполняемый файл, и никогда его не модернизировать, все должно быть просто и надежно как АК.
2) Добавить спасательную функцию, если текущая версия объявляется неудачной, то запускается дублирующая функция принудительного апдейта или предлагается обновить версию руками.
Озвученная Вами проблема является актуальной, так как не так давно каким-то случайным образом в 3 часа ночи я умудрился добавить символ лишний в путь файла на сайте для скачивания обновлений, в следствие чего, несколько тестеров не смогли обновиться и запрашивали у меня ссылку для ручного обновления.

И здесь мне пришла мысль о том, что если ПО не может скачать файл исполняемой программы, то она будет выводить уведомление об этом (не каждый раз, а, скажем, раз в 5 дней) и открывать прямую (или не прямую) ссылку в браузере для скачивания данного файла.

А прочитав Ваш комментарий, возникло желание добавить функцию резервного обновления по следующему принципу:
— Основной апдейтер полностью вынести во внешний файл (как писалось в других комментариях не раз), а в теле основной программы оставить функцию резервного обновления.
Таким образом можно решить проблему с некорректным обновлением.
Sign up to leave a comment.

Articles