Еще раз о ClickOnce

Еще одна статья о ClickOnce. На мой взгляд очень и очень хорошо описывает технологию. Приятного чтения.

“На сладкое” остается совсем уж не control и не компонент, а технология, имеющая прямое отношение к WinForms. Дело в том, что разработка, тестирование и отладка приложения – это совсем не весь жизненный цикл программного обеспечения. Не менее ответственной и сложной задачей является естественное продолжение этих усилий – развертывание (deployment) и регулярное обновление (updating) поставляемого ПО. Общепринятой практикой развертывания как раз таки .NET-WinForms приложений является сегодня создание установочных пакетов в рамках старой (не в смысле устаревшей, а в смысле существующей уже не первый месяц) технологии – Windows Installer (MSI). И все бы ничего, но при таком подходе к распространению приложения возникают три проблемы:

Трудность обновления. В общем случае любое обновление (даже мелкое) – это новый MSI. Таким образом, пользователь должен скачать (если обновление выложено, допустим, на домашней странице разработчика) и переустановить все приложение.
Влияние на пользовательский компьютер. Приложения, развертываемые по этой методологии, зачастую полагаются на общие сборки/компоненты с потенциальной миной замедленного действия, известной под кодовым названием “конфликт версий”.
Чтобы вообще инициировать установку, пользователь должен обладать администраторскими полномочиями на данной машине. Простым смертным процедура установки недоступна в принципе.

Понятное дело, эти проблемы были подмечены не вчера. И в существующей версии .Net Framework 1.0/1.1 уже была сделана первая робкая попытка справиться с ними. Я говорю о так называемом no-touch deployment, доступном уже в этих версиях. Собственно, если вы осведомлены об этом подходе к вопросу, считайте, что базовые знания по новой технологии у вас имеются, поскольку ClickOnce является прямым потомком и продолжателем дела no-touch развертывания. Правда, ребенок пошел куда дальше своего родителя, и даже грозится отобрать пальму первенства у старого MSI. Строго говоря, эти две технологии – MSI и ClickOnce – безусловно, являются конкурентами, поскольку отдавая в данной конкретной ситуации предпочтение одной, вы автоматически отказываетесь от другой. А вот для правильной оценки ситуации и выбора подходящей технологии требуется знание сильных и слабых сторон обоих вариантов. Пока еще есть (и будут) ситуации, когда MSI является безальтернативным вариантом. Но учтите, что в следующей версии OS (известной большинству под кодовым именем Longhorn) ClickOnce будет еще значительно усилена и расширена, становясь, по видимому, стандартом “де-факто” в инсталляции приложений. Разберем, как в текущей реализации ClickOnce решаются три проблемы, перечисленные в начале раздела:

Трудность обновления: ClickOnce предлагает готовую (не требующую дополнительного кодирования) схему автообновления. При этом пользователю передаются только необходимые изменения, т.е. только модифицированные файлы.
Влияние на пользовательский компьютер: при ClickOnce-установке каждое приложение (и даже каждая версия одного и того же приложения) является изолированным от внешнего мира “черным ящиком”. Конфликт версий исключен.
Административные права: возможна установка любым пользователем, поскольку приложения, развернутые по технологии ClickOnce, запускаются в т. н. “песочнице” с ограниченными привилегиями. Чем меньше доверия к источнику установки (Web, локальная сеть, CD-ROM), тем жестче ограничения на права приложения.

Уже неплохо. Но основной идеей рождения ClickOnce приложений было даже не решение этих хотя и важных, но все же локальных проблем. В целом идея куда как глобальнее: предоставить конечному пользователю богатый интерфейс обычного приложения, но в тоже время обеспечить распространение такого приложения посредством Web. Или, проще говоря, создать у пользователя иллюзию, что он запускает Web-приложение (одним щелчком по ссылке в браузере) в то время, как запускается полноценное Windows-приложение. Коротко и официально такое “двуличное” приложение зовется умным клиентом (smart client). Подобное приложение обычно обладает еще целым рядом признаков, вроде использования централизованной базы данных, но здесь я сосредоточусь на двух важнейших: распространении и обновлении.

Как же данная идея технически реализована в новой технологии и насколько плотно она интегрирована в VS2005 и в сам Framework? В начале введу понятие “публикация”. Публикация – это набор файлов (*.exe, *.dll, файлы ресурсов и т.д.), необходимых для нормальной работы приложения, и ряд файлов, добавляемых самой инфраструктурой ClickOnce (о них будет сказано ниже). В терминах старого MSI публикация является аналогом выходного *.msi-файла, только здесь файлы не пакуются в один архив. Далее, производится размещение публикации. Размещение – это указание VS места, где должны быть сгенерированы файлы публикации. И, наконец, есть адрес инсталляции. Это адрес странички в интернете, на которую конечный пользователь должен проследовать, и на которой имеется ссылка, инициирующая установку ClickOnce-приложения. Разумеется, размещение и адрес инсталляции могут и совпадать (тогда второе поле в опциях проекта можно оставить пустым). Но это не всегда возможно. Вот пример из практики: при создании этой статьи я использовал известный хостинг narod.ru как место расположения тестовых файлов для их последующей установки на свой компьютер с помощью новой технологии. Если указать размещение как xxx.narod.ru (xxx – учетная запись на “ Народе”) и не заполнять адрес инсталляции, то VS будет пытаться разместить файлы публикации прямо на моей домашней странице. Разумеется у нее ничего не получится – для административного доступа к страничке нужны логин/пароль. Поэтому я указываю размещение как локальную директорию на диске C: а вот уже адрес инсталляции как xxx.narod.ru. Теперь студия генерирует публикацию на локальном диске и не пытается соединиться с интернетом. После окончания этого процесса я могу (например, по FTP) скопировать уже подготовленные файлы по адресу инсталляции и раздать этот адрес своим клиентам.
СОВЕТ
В процессе обсуждения стати на форуме участник vhonest совершенно справедливо заметил, что даже если целевой сервер требует авторизации (как в примере с narod.ru), то и в этом случае возможно автоматическое Размещение без нашего вмешательства. Для этого в поле publishing location (о нем речь пойдет в следующем разделе) необходимо ввести данные авторизации по шаблону:

<протокол>://<логин>:<пароль>@<целевой_сервер>/<директория_целевого_сервера>

Конкретный пример приведенный самим участником:

ftp://AccountName.nm.ru:password@ftp.nm.ru/ProjectName/

Собственно, данная процедура переноса сгенерированных файлов с локального диска в интернет в любом случае неизбежна. Даже если указать размещение как адрес web-страницы, все равно сначала будет создана локальная копия публикации, а потом уже VS перенесет ее по указанному адресу. Впрочем, как это и полагается в статьях, имеющих касательство к сетевым технологиям, для дальнейших экспериментов будем исходить из того, что на вашем локальном компьютере установлен Internet Information Services (IIS), именно он примет готовую публикацию, и именно с него она будет устанавливаться. Вопросы размещения публикаций на удаленных серверах, предоставляющих бесплатный/платный хостинг, выходят за рамки этого обзора. С целью сжатости изложения сосредоточусь на публикациях именно в их Internet-варианте, однако публикации могут быть размещены и в локальной сети (network file share), и даже (что значительно менее интересно, но возможно) на обычном CD/DVD. Введу еще два понятия из мира ClickOnce: устанавливаемое приложение и запускаемое приложение. Разница в следующем: когда конечный пользователь открывает в браузере страницу с публикацией, то, если последняя предлагает устанавливаемое приложение, он видит кнопку Install. Во втором случае он видит кнопку Run. Соответственно, в первом случае после щелчка задается вопрос о разрешении установки, а во втором – просто происходит запуск приложения, как если б оно было Web-приложением. Если некоторые читатели в этом месте сильно удивились тому моменту, что полноценное WinForms-приложение вдруг чудесным образом сконвертировалось в Web-приложение, то спешу их успокоить: чудес, как и прежде, в этом мире не бывает. :) Разумеется, это не более чем иллюзия Web-приложения. Технически в обоих вариантах загружаются одни и те же файлы, одинаковым образом устанавливаются и даже занимают одни и те же места на локальном HDD пользователя. Но пара различных черт все же имеется: устанавливаемое приложение добавляет свой ярлык в меню “Пуск”, делая возможным повторный запуск; запускаемое приложение ярлыка не добавляет и может быть повторно запущено только со страницы публикации. Далее, первый тип приложения добавляет себя в группу “Установка/удаление программ” панели управления. Отсюда конечный пользователь может либо удалить его полностью, либо сделать откат до предыдущей версии (об этом будет сказано ниже). Второй тип приложения ничего подобного не делает и не дает возможности конечному пользователю “цивилизованно” удалить свои файлы с жесткого диска – ведь оно продолжает “прикидываться” полноценным Web-приложением и делает вид, что никаких его следов на HDD не остается. Вообще, согласно документации, файлы, переносимые на пользовательский компьютер, попадают в специальный “кэш приложений”, подобный Web-кэшу в том плане, что система сама вычистит файлы из него. Но как, когда это происходит, и можно ли периодичностью такой чистки управлять, не говорится ничего. Могу лишь отметить, что за время многочисленных экспериментов файлы запускаемых приложений оставались совершенно нетронутыми. Что тому виной – бета самого Framework-а, слишком малое количество времени с момента запуска приложения и до момента его насильственного удаления самим автором или элементарное непонимание принципов работы этого загадочного “чистящего” механизма – судить не берусь. Вот краткое резюме по различиям этих двух типов приложений: помимо наличия ссылки в меню “Пуск” и ссылки в группе “Установка/удаление программ”, это два абсолютно идентичных варианта. Конечно, есть различия, так сказать, логического уровня. Например, устанавливаемое приложение может быть запущено, даже если у пользователя имеются локальные проблемы с доступом в интернет. А во втором случае это у него не получится – ссылки-то на запуск нет, и нет доступа к странице публикации. Или например, если выложить новую версию своего приложения, то в первом варианте (обычный сценарий) пользователь просто извещается об этом (и то только в том случае, если в момент запуска нашего приложения он подключен к сети) и имеет возможность (но не обязан!) дать разрешение на обновление существующей у него версии. И более того, даже если обновление произведено по прошествии некоторого времени, пользователь может произвести откат к предыдущей версии программы. Во втором же случае схема куда как примитивнее: при запуске (а это автоматически означает наличие рабочего соединения с интернетом) сравниваются версии в локальном “кэше приложений” и на странице публикации. Если да – запускается приложение из кэша, нет – сначала скачивается, а потом запускается новая версия. Все – пользователь гарантированно работает с последней (и только с последней) версией. Итак, чтобы выбрать, какое приложение предложить своим клиентам – устанавливаемое или запускаемое – надо учесть немало нюансов, но при этом с чисто технической точки зрения у того и другого больше похожих черт, нежели различий. Вот в связи с крайней похожестью обоих типов я буду говорить далее только об устанавливаемых приложениях, помня при этом, что часть их особенностей недоступна во втором типе приложений, а часть (к примеру, все что касается обновлений) просто не актуальна.
Как это выглядит

Пример к данному разделу оформлен в виде отдельного решения (solution), никак не связанного с предыдущими примерами. Точнее, это целый набор решений, позволяющий наблюдать различные аспекты установки, запуска и, самое важное и интересное, обновления устанавливаемого приложения. В качестве подопытного материала используется элементарная WinForms-программа, состоящая из одной формы. Откройте исходную версию данного решения по адресу <каталог_проектов>\ClickOnce\ver_1_0\ver_1_0.sln. Отмечу, что для перевода приложения в славную когорту ClickOnce-приложений в общем случае не требуется никакого дополнительного кодирования, или наследования от неких базовых классов, или реализации специфичных интерфейсов. Достаточно правильно выставить опции проекта, о чем и пойдет речь ниже. В данном случае C#-код проекта настолько элементарен, что вряд ли может представлять серьезный интерес. Вместо этого откройте подлежащие настройке свойства самого проекта (в Solution Explorer выберите Properties из контекстного меню проекта). В открывшемся окне свойств проекта целых три закладки слева посвящены именно работе с технологией ClickOnce: Signing, Security, Publish. Откроем последнюю из них.


Publishing Location – это размещение, а Installation URL – это, соответственно, адрес инсталляции. И то и другое обсуждалось выше, и, надеюсь, нет необходимости еще раз повторять, почему в данном случае мы вполне можем оставить второе поле пустым. Далее с помощью переключателя Install Mode and Settings выберите, как и договорились, вариант инсталлируемого приложения. В самом низу идет исключительно важное поле Publish Version (версия публикации). Важность его заключается в том, что, во-первых, оно никак не связано с версией самого исполняемого файла (т.е. с версией сборки), и во-вторых, когда в технологии ClickOnce принимается любое решение по вопросу обновления текущей версии приложения, в расчет берется только эта версия – версия публикации. Именно так – версия самой сборки в вопросах обновления не играет решительно никакой роли, движок технологии ClickOnce учитывает только это поле. Наконец, последний флажок может заставить студию увеличивать последний номер (Revision) после каждой удачной публикации. Поскольку в моем примере каждая новая версия приложения – это отдельное решение с отдельными настройками проекта, я эту галочку снимаю. Теперь нажимите первую из четырех кнопок “Application Files…”. Здесь перечислены все файлы решения и их статус публикации. Решение состоит из трех файлов: основной сборки (SmartClient.exe) и двух маленьких картинок, фактически иконок в PNG-формате (ico_1.png, ico_2.png). Обратите внимание, что у первой картинки статус публикации изменен с используемого по умолчанию “Include” на “Data File”. Смысл и влияние данного изменения на процесс обновления (а связан он в первую очередь с ним) будет рассмотрен в свое время. Вторая кнопка “Prerequisites…” позволяет определить, что требуется клиентской машине для успешного запуска приложения, и, в случае отсутствия требуемого, где можно это взять. Подробный разбор всех возможных опций данного окна выходит за рамки данного обзора. Отмечу лишь, что можно довольно скрупулезно настраивать список необходимых компонентов и их местонахождение. Третья кнопка “Updates…” управляет процессом автообновления (а есть еще программный, кодируемый вариант, см. ниже). Самый главный в этом окне – первый флажок. Если его снять, то любой вариант обновления (хоть авто-, хоть программный) становится невозможным. Если он отмечен, то становится возможным (через переключатель) выбор двух принципиально различных подходов к процессу обновления: до старта приложения и после старта. При первом варианте запуск приложения через пункт меню “Пуск” означает совсем даже не запуск самого приложения, а запуск процедуры проверки обновления. Если оно найдено, следует его установка (с разрешения пользователя, разумеется) и запуск уже этой, новой версии. При втором варианте запуск приложения означает именно запуск самого приложения. Но в процессе работы запускается фоновый поток, который и выясняет, появилось новое обновление или нет. В любом случае текущая сессия работает со старой версией приложения. А вот при следующем запуске, если обновление найдено, следует запрос на установку. Понятно, что первый вариант несколько затягивает запуск самого приложения, но позволяет начать работу с новой версией приложения как можно быстрей и оперативнее. При втором варианте “по инерции” продолжается работа на старой версии приложения даже при наличии новой. Правда, второй вариант можно гораздо гибче настраивать: можно проверять наличие обновлений при каждом запуске, или каждые 5 дней, или каждые 12 часов и т.п. Еще одна крайне важная и интересная опция разбираемого окна – минимально требуемая версия приложения. К ней я обязательно вернусь в процессе экспериментов. Наконец последняя, четвертая кнопка “Options…”

Здесь нужно заполнить поля “Publisher name” и “Product name”. Первое – это название новой группы в меню “Пуск”, а второе – названием ярлыка в этой группе и пункта в панели управления “Установка/удаление программ”. Можем заполнить “Support URL” – это будет еще один ярлык в группе с именем “Publisher name” в меню “Пуск”. Далее я изменил название самой Web-страницы с информацией о публикации с publish.htm на index.htm просто с целью облегчения навигации. Оставшиеся опции более-менее самоочевидны и подробных пояснений не требуют. Закройте окно “Publish Options” и страницу свойств проекта и запустите процесс публикации, который сводится к выбору пункта меню “Build->Publish SmartClient”. Появляется “Publish Wizard”, повторяющий, по сути, некоторые важные опции из закладки Publish со страницы свойств проекта. Можно пройти его весь, нажимая кнопку “Next>” и проверяя каждую опцию, но поскольку ничего нового этот визард не скажет, можно сразу нажать “Finish”. После короткой работы в строке статуса появляется сообщение, что публикация успешно размещена. В данном случае она успешно размещена на локальном IIS с именем localhost. Ознакомимся вкратце с физическим содержимым публикации. Поскольку виртуальный путь к ней – http://localhost/SmClient/, то при настройках IIS по умолчанию физическим путем будет c:\Inetpub\wwwroot\SmClient\. Файлы, копируемые туда, перечислены в таблице 10.
Путь\файлКраткое описание
setup.exeУстановщик необходимых компонентов из списка “Prerequisites…” опций проекта. Если при щелчке по кнопке “Install” (см. ниже) в браузере система замечает, что на целевом компьютере отсутствует один из требуемых компонентов, то запускается именно этот файл, который и проводит пользователя по тернистому пути установки всяческих недостающих компонентов. В противном случае ссылка кнопки “Install” ведет прямо на файл <база>\SmartClient.application и данный setup.exe попросту не используется.
index.htmСама отображаемая в браузере страница. По умолчанию имеет имя publish.htm, но имя может быть изменено.
<база>\SmartClient.applicationОдин из двух важнейших для всей технологии файлов – т.н. манифест развертывания. В случае, если все недостающее уже установлено, щелчок по кнопке “Install” запустит именно этот файл. Кстати, на машине должно быть зарегистрировано новое расширение .application, иначе система не распознает, чем же его открыть. Обычно регистрируется автоматически при установке Framework-а, так что скорее всего волноваться нечего. Структурно это обычный XML-файл, описывающий важнейшие характеристики публикации: версия публикации (крайне важно при обновлении), месторасположение второго “очень важного файла” – манифеста приложения, выбранная для публикации стратегия обновления и т.д.
SmartClient_1_0_0_0.applicationСовершеннейшая копия предыдущего файла. Но при публикации следующей версии приложения (а ведь она пойдет в эту же самую директорию!) предыдущий файл будет затерт манифестом развертывания от новой версии, а этот останется в неприкосновенности. Нужен для отката публикации к предыдущей версии.
<база>\SmartClient_1_0_0_0\ (каталог)Подкаталог базы. Содержит текущую версию приложения (для следующей версии будет создан свой подкаталог) а также его манифест.
<база>\SmartClient_1_0_0_0\ico_1.png.deploy <база>\SmartClient_1_0_0_0\ico_2.png.deploy <база>\SmartClient_1_0_0_0\SmartClient.exe.deployКопии файлов, составляющих приложение. В чем сакральный смысл навешивания дополнительного расширения .deploy, которое при развертывании просто отбрасывается, могут объяснить разве что авторы новой технологии. Есть гипотеза, что планировалось публикуемые файлы ужимать каким-то архиватором, передавать их в сжатом состоянии, а после этого движок ClickOnce-а разархивировал бы их и писал на локальный HDD обычные распакованные файлы. В таком случае новое расширение было бы более чем оправданно. Но… то ли еще бета пока, то ли руки не дошли, то ли идея не вдохновила… :) Одним словом, на текущий момент это просто битовые копии соответствующих файлов.
<база>\SmartClient_1_0_0_0\SmartClient.exe.manifestВторой из важнейших файлов – манифест приложения. Структурно представляет из себя XML-файл, но в отличие от первого манифеста описывает не публикацию, а само приложение: сборки и файлы, из которых оно состоит, зависимости, а также крайне важную информации о доверии (trust information), в зависимости от которой целевой компьютер предоставит приложению те или иные полномочия.

Хорошо, какие же дивиденды мы имеем с новой публикации? Откройте браузер и перейдите по адресу инсталляции, который в данном случае совпадает с размещением:

На открывшейся страничке с краткой информацией о приложении имеется кнопка “Install”. Нажатие на нее запускает установку приложения. Поскольку на моей машине все необходимые компоненты установлены, установлены, запускается не файл setup.exe, а непосредственно SmartClient.application. Действительно, сразу по нажатию система стартует новый процесс dfsvc.exe (Distributed Files SerViCe?), который и является тем самым движком технологии ClickOnce – любое ClickOnce-приложение никогда не запускается напрямую. Впрочем, к движку я еще вернусь, а пока отмечу, что запускаемый файл этого процесса размещается по пути C:\WINDOWS\Microsoft.NET\Framework\v2.0.50215\dfsvc.exe для той версии beta2, что установлена на моей машине. Этот же факт отвечает на вопрос “является ли ClickOnce частью Framework и необходимо ли второе для работы первого?” Ответ на обе части вопроса – категорическое да. Разумеется, только ClickOnce-движок знает как разбирать манифест развертывания и какие шаги предпринимать в зависимости от его содержимого. В данном случае он выдает запрос на локальную установку приложения:

После предупреждения о том, что издатель неизвестен, разрешается нажать на еще одну кнопку “Install”. После этого начинается собственно установка, по окончании которой приложение автоматически запускается:

Данное приложение сообщает свою версию (версия публикации, конечно – к версии самой сборки это никакого отношения не имеет), отображает две маленьких иконки и имеет кнопку для проверки (программным путем) наличия обновлений. Пока использовать кнопку смысла нет – ведь это первая и единственная версия приложения. Поэтому можно просто закрыть приложение. Однако это устанавливаемое приложение и, по идее, есть возможность запускать его из меню “Пуск”. OK, в группе “SelfPublisher” действительно есть ярлык “Demo of Smart Client talents”.

Кстати говоря, ярлык – это не такой-то там банальный *.lnk-файл. На самом деле это файл еще одного нового типа – .appref-ms. И опять, как не сложно догадаться, это расширение должно быть зарегистрировано в системе, а работать с ним умеет только движок ClickOnce. И действительно, щелчок по ярлыку приводит к запуску движка, который первым делом производит подключение к серверу (это срабатывает запуск процедуры проверки обновления), после чего запускается та же самая форма – сборка SmartClient.exe.

Какие же изменения, помимо вполне очевидного создания в группе “Программы” новой подгруппы и нового ярлыка в ней, произвел процесс установки на локальном HDD? Прежде всего нужно отметить те изменения, которые могли бы произойти при использовании MSI, но не произошли в данном случае: в папку “Program Files” ничего добавлено не было, реестр системы также остался нетронутым, никаких новых иконок на рабочем столе не появилось. И не забудьте, что установить приложение можно, не обладая правами администратора на данной машине. А вот какие изменения вызвал новый способ инсталляции: в папке c:\Documents and Settings\<текущий_пользователь>\Local Settings\Apps\ была создана хитрая иерархическая структура с совершенно дикими именами папок вроде “smar..tion_033a11a49ab76479_0001.0000_d17bcb50e713c836” Полностью структуру папок я приводить не буду. Отмечу только, что непосредственно в каталоге Apps были созданы две папки: одна с именем Data, а другая – “9QR19VBW.H2M”. В глубинах первой (уровня еще на 4 ниже) нашел себе приют файл ico_1.png. В глубинах же второй разместились 2 оставшихся файла приложения – ico_2.png и сама сборка (плюс еще некоторые инфраструктурные файлы, которые здесь можно проигнорировать). Упрощая: появился каталог данных (…\Apps\Data\) и каталог приложения (..\Apps\9QR19VBW.H2M\). Основываясь на статусе публикации каждого файла (помните работу с окном “Application Files” на странице свойств проекта?) движок ClickOnce разнес их по соответствующим директориям. Файл со статусом “Data File” был отправлен в каталог данных, а со статусом “Include” – в каталог приложения. Но, конечно, только ради разделения файлов по каталогам не стоило и огород городить. Истинный смысл подобных телодвижений выяснится только при установке обновлений приложения. Пока же можно сделать следующий вывод: в силу своей архитектуры ClickOnce-приложение доступно только установившему его пользователю. Второй пользователь того же компьютера должен будет установить свою собственную копию в свой каталог, и эта копия будет полностью отделена и изолирована от первой. Вариант установки per-machine, доступный в случае MSI-инсталляции, в случае ClickOnce исключен.

Следующий логический шаг – попробовать функции обновления, о которых столько говорилось выше. Для этого закройте в VS2005 решение для приложения версии 1.0 и откройте <каталог_проектов>\ClickOnce\ver_1_1\ver_1_1.sln. Данное решение является совершеннейшей копией предыдущего за единственным исключением: на странице свойств проекта на закладке Publish поле Publish Version изменено на 1.1.0.0 (рисунок 26).

Этого единственного изменения достаточно, что бы ClickOnce посчитал данную копию новой версией приложения. Запустите процесс публикации (“Build->Publish SmartClient”) и сразу нажмите “Finish”. После сообщения об успешном завершении можно убедиться, что виртуальный сервер содержит обе версии публикации – 1.0 и 1.1. Для установки новой версии уже не требуется запускать браузер. Кстати говоря, если бы версия 1.0 не была установлена ранее, устанавливалась бы уже версия 1.1. Кнопка “Install” по-прежнему ссылается на манифест развертывания SmartClient.application, но это уже манифест новой версии.

При щелчке по уже знакомому ярлыку из меню “Пуск” вместо запуска формы движок ClickOnce выведет такое сообщение:

Что ж, согласимся сгрузить новую версию нажатием на “OK”. После секундной паузы запускается уже новая версия приложения, что подтверждается первой строчкой на форме:

А что же изменилось на локальном HDD? В каталоге приложения была создана еще одна папка для новой версии и туда сгружены ico_2.png и *.exe-сборка. Теперь этот каталог содержит обе версии приложения. А вот в каталоге данных новая папка была создан, но файл ico_1.png в нее был просто скопирован из версии 1.0. Это видно по времени создания обоих файлов, а также по времени создания второй папки. Так вот в чем смысл статуса публикации “Data File” и чем он отличается от “Include”! Файлы первого типа загружаются на локальный компьютер только если они изменились. Файлы второго типа безусловно скачиваются всякий раз при обновлении приложения. Таким образом, тип публикации “Include” хорошо подходит для небольших мобильных файлов и сборок приложения, с большой степенью вероятности меняющихся от версии к версии. Тип “Data File”, напротив, годится для “массивных” файлов данных и ресурсов, меняющихся только с выходом действительно уж капитальных обновлений. Короткое резюме: правильно подбирая тип публикаций для файлов вашего приложения, вы можете значительно повысить общее впечатление пользователя от скорости и удобства автоматического обновления. И кстати – каталог данных создается при установке всегда и безусловно, даже если ни один из файлов приложения не имеет статуса публикации “Data File”. Так что если вам нужна подходящая папка для динамически создаваемых файлов данных – каталог данных всегда тут и к вашим услугам. А вот как получить доступ к нему, я скажу чуть позже. При этом (заметьте!) обновление вызовет миграцию таких файлов из каталога данных предыдущей версии в каталог данных новой версии (так же, как в случае с файлом ico_1.png). Таким образом, все пользовательские данные сохраняются при обновлении версии приложения.

Но давайте посмотрим еще на один аспект этого процесса. Закройте решение версии 1.1 и откройте <каталог_проектов>\ClickOnce\ver_1_2\ver_1_2.sln. Здесь уже изменения посущественнее. Помимо вполне очевидного изменения Publish Version до 1.2.0.0, файл-иконка ico_1.png (имя то же, но содержимое другое; в 1.1 это был зеленый кружок с галочкой внутри, а в 1.2 – чашка кофе). Такое изменение позволит убедиться, действительно ли для файлов со статусом “Data File” автоматически распознается изменение их содержимого (согласно документации, ClickOnce должен рассчитывать хэш таких файлов и на этом основании делать вывод об их идентичности или различиях) и будут ли они сгружены на локальный компьютер в этом случае. При неизменном содержимом эти файлы просто копируются из одной версии в другую.

Запустите процесс публикации и по окончании опять выберите уже знакомый ярлык из меню “Пуск”. Появляется точно такое же окно, как при переходе от версии 1.0 к 1.1. Но сейчас мы поступим чуть хитрее и вместо “OK” выберем “Skip” (допустим, срочно надо работать и нет ни секунды на ожидание загрузки новой версии). Разумеется, после такого выбора запускается устаревшая (теперь уже) версия 1.1. Но что будет, если закрыть эту версию и запустить ее вновь? Появится ли вновь приглашение к обновлению? Ведь движок ClickOnce безусловно в курсе, что используется не последнюю версию! Оказывается, нет – приглашение больше не появляется. Логика понятна: пользователю дали знать, что появилось новое некритическое обновление (разницу с критическим я покажу ниже) и предложили его установить. Отказался – его дело. Может, человек обновлений как огня боится, что его постоянно вопросами донимать? Поэтому версию 1.2 пометили как “предложенную, но отклоненную”, и приглашений больше выдавать по этой версии не будут. Но вот с выходом следующей версии ClickOnce вновь напомнит о себе. Что же делать? А вот тут на помощь приходит обновление по запросу или программное обновление. Суть в том, что по какому-либо действию пользователя (в примере таким действием является щелчок по единственной кнопке на форме) программно выполняются те же действия, которые проводит движок ClickOnce при поиске наличия новой версии приложения. Итак, вернусь на короткое время к коду проекта, но прежде сделаю небольшое замечание. Хотя, казалось бы, программное обновление может при желании полностью заменить собой автоматическое и сделать его ненужным, не спешите снимать галочку “The application should check for updates” в окне “Application Updates”, вызываемого по кнопке “Updates…” на закладке Publish страницы свойств проекта. Эта галочка, конечно, отключит автообновление и ускорит загрузку приложения, но поступать так следует, только если вы действительно не планируете никакого обновления – ни автоматического, ни программного. Если же снять ее и затем попытаться из кода обратиться к методам ClickOnce-обновления, то будет выдано исключение System.InvalidOperationException с сообщением:

Application cannot be updated programmatically.

В данном случае обновление будет работать в кооперации с автоматическим обновлением, но ни в коем случае не подменять его. В разбираемом сценарии такая кооперация придется как нельзя более кстати. Ну а теперь – код. Чтобы начать использовать API движка ClickOnce, нужно включить в код директиву:

using System.Deployment.Application;

Именно в этом пространстве имен “проживает” класс ApplicationDeployment. Обратите внимание, что здесь имеет место быть забавный казус: во второй бете есть два класса с именем ApplicationDeployment: один в указанном пространстве имен, а второй – в пространстве имен System.Deployment. Однако класс из последнего пространства имен помечен забавным атрибутом:

[Obsolete("This is a dummy class. Switch to System.Deployment.Application.ApplicationDeployment instead.")]

Одним словом, директива, приведенная выше, верна, а директива “using System.Deployment;” была бы ошибочной. Все классы пространств имен, о которых здесь говорится, находятся в сборке system.deployment.dll. Не забывайте о соответствующей ссылке в проекте (в проектах эта ссылка уже имеется). Поскольку разбор программной формы обновления является лишь побочной темой разговора, я не стану детально разбирать свойства, методы и события данного класса. Но самое необходимое для достижения цели будет рассмотрено. Прежде всего, любые члены данного класса не должны применяться в программах, не установленных одним из возможных методов ClickOnce (Web, FTP, локальная сеть, CD/DVD). Если вы просто написали приложение в VS2005 и запустили его кнопкой F5, любое обращение к любому члену указанного класса вызовет исключение. Класс ApplicationDeployment предназначен только для ClickOnce-приложений. Единственный член класса, не подпадающий под это правило – статическое свойство IsNetworkDeployed (тип bool, только для чтения). К нему можно обратиться из любого приложения. Его предназначение, собственно, и заключается в том, чтобы сказать, является ли текущее приложение ClickOnce-приложением. В этом случае указанное свойство вернет true. Обратите внимание, что в своем примере я такой проверки не делаю, т.к. уверен, что приложение будет точно использоваться только в варианте ClickOnce. Но в общем случае такая проверка не помешает. Хорошо, теперь, когда точно известно, что это именно ClickOnce-приложение, и работа с разбираемым классом ему вполне показана, выясняется, что у данного класса нет публичного конструктора и создать объект этого типа самостоятельно нельзя. Зато второе статическое свойство CurrentDeployment (тип ApplicationDeployment, только для чтения) как раз вернет так нужный объект требуемого типа. Причем для каждого индивидуального развертывания будет создан свой объект. Свойство CurrentDeployment возвращает объект, ориентированный именно на текущую установку, т.е. ту, из которой данное приложение было запущено. После создания объекта становятся доступными экземплярные свойства/методы. Но прежде всего нужно отметить, что процесс обновления разбивается на две фазы: поверка наличия новой версии приложения (фаза 1) и, в случае наличия таковой, ее скачивание и установка (фаза 2). Разбираемый класс предлагает два пути выполнения обеих фаз: синхронный и асинхронный. Как несложно догадаться, первый путь приведет к невозможности работы пользователя с приложением до полного окончания процесса. Иногда это вполне оправдано – если, например, нужно, чтобы пользователь продолжил работу в новой и только в новой версии. Напротив, второй путь проводит всю работу в специально создаваемом для этой цели фоновом потоке, и пользователь может спокойно продолжать работу с текущей версией. Поскольку асинхронный путь представляется лучшим вариантом, сосредоточусь именно на нем. При таком подходе к вопросу общепринятый способ сообщить об окончании любой из фаз – сгенерировать событие. Два события, без которых определенно не обойтись при асинхронном обновлении: CheckForUpdateCompleted и UpdateCompleted. В конце первой фазы генерируется первое событие, в конце второй – второе. Поэтому первый шаг после создания объекта типа ApplicationDeployment – подписка на эти два события:

//_ad – объект типа ApplicationDeployment
this._ad.CheckForUpdateCompleted += _ad_CheckForUpdateCompleted;
this._ad.UpdateCompleted += _ad_UpdateCompleted;

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

// CheckForUpdate() запустил бы синхронный вариант проверки наличия
// новой версии
this._ad.CheckForUpdateAsync();

Теперь программа спокойно продолжает заниматься своими делами и ждет первого события CheckForUpdateCompleted. Замечу, что ApplicationDeployment настолько любезен, что вызывает обработчики обоих событий в “правильной”, UI-нити, так что можно тут же в обработчике работат
Несмотря на сложности, организация практического взаимодействия инновационна. Организация практического взаимодействия спорадически детерминирует направленный маркетинг, работая над проектом. Продуктовый ассортимент, анализируя результаты рекламной кампании, основан на тщательном анализе. Побочный PR-эффект, как принято считать, притягивает департамент маркетинга и продаж, повышая конкуренцию. Стимулирование сбыта индуцирует медиаплан, осознав маркетинг как часть производства. Привлечение аудитории, не меняя концепции, изложенной выше, изменяет инвестиционный продукт, оптимизируя бюджеты.
Dodex 2012 - 2020
Электронная почта: contact@dodex.org
Skype: dodexorg
Twitter: @dodexorg