понедельник, 30 июня 2014 г.

Создание инсталлятора с помощью WiX. Часть 1. Простой инсталлятор

При написании коммерческого софта часто встает вопрос о способе его поставки конечному пользователю. Тут можно рассмотреть несколько вариантов: мы можем поставлять программное обеспечение в виде набора файлов, копируя его на рабочие места пользователей и затем, настраивая вручную, можем разворачивать программное обеспечение с помощью облачных сервисов, например Windows Azure. Также мы можем предоставлять пользователю пакет программ в виде инсталлятора, который включает в себя этап настройки приложений.

Как показывает опыт, в данный момент мало заказчиков решаются использовать в своем рабочем процессе облачные сервисы. С этим связан ряд причин, начиная от обеспечения безопасности хранимой информации и заканчивая стоимостью готовых решений. Что касается поставки в виде набора файлов, то этот вариант также не является удобным. Представим, что мы разворачиваем сервер приложений. В данном случае при ручной поставке нам необходимо совершить множество рутинных ручных действий: скопировать файлы, настроить конфигурации, зарегистрировать и запустить службу Windows, настроить правила брандмауэра и т.д. При всем этом ручная настройка является чем-то сродни искусству, которое требует большого внимания и концентрации.

Исходя из всего выше сказанного, мне кажется, что самым удобным способом поставки готового приложения будет подготовка инсталлятора. Да, для этого понадобится приложить некоторые усилия и потратить время, но это решение позволит сэкономить время и нервы службы внедрения на этапе развертывания. 

Сейчас на рынке присутствует множество коммерческих решений для создания инсталляторов. Например, InstallShield, InstallAware, InnoSetup, NSIS и прочие. Мы на своем проекте решили остановиться на пакете Microsoft WiX по причине его бесплатности. Технология WiX (Windows Installer XML) – это набор инструментов для упрощения процесса создания дистрибутива на базе MSI.

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

Итак, мы будем писать инсталлятор для простого сервиса WCF, который будет хоститься как служба Windows. Исходники этого приложения входят в примеры к статье и располагаются в проекте InstallingApplication.

В данной статье мы подготовим простейший инсталлятор, который будет содержать следующий набор окон:
  • Приветствие
  • Лицензионное соглашение
  • Выбор папки установки
  • Начало установки.
Сначала скачиваем и устанавливаем WiX. После установки в
Visual Studio будут добавлены новые шаблоны проектов Windows Installer XML. Выбираем шаблон Setup Project. В моем примере проект будет называться ApplicationInstaller.


Изначально будет создан один файл Product.wxs. Это основной файл, в котором описывается поведение инсталлятора.

Настроим поддержку русского языка. Для этого в секции Product изменим значение атрибута Language на 1049. Это магическое число укажет, что инсталлятор должен использовать русскую локаль. Затем откроем свойства проекта и во вкладке Build укажем значение «ru-RU» для параметра Culture to build (без кавычек). На данном этапе этого будет достаточно, чтобы наш инсталлятор научился говорить по-русски.


После этого подчистим файл Product.wxs. Удалим все секции Fragment и MajorUpdate. Пока они нам не нужны.

Добавим описание директорий, куда будем инсталлировать наше приложение.

<Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="ProgramFilesFolder">
        <Directory Id="INSTALLLOCATION" Name="ApplicationServer" />
    </Directory>

    <Directory Id="ProgramMenuFolder">
         <Directory Id="ApplicationProgramsFolder" Name="ApplicationServer" />
    </Directory>
</Directory>

Тэг Directory определяет путь для установки. Directory Id=”TARGETDIR” – корневой элемент для всех папок, в которые будет инсталлироваться наше приложение. Directory Id=”ProgramFilesFolder» указывает на папку Program Files. Directory Id=”INSTALLLOCATION” – это папка с именем ApplicationServer в папке Program Files. Эта иерархия описывает расположение нашей программы по умолчанию.

Второе описание иерархии папок указывает расположение ярлыков к нашему приложению. Id=”ProgramMenuFolder” ссылается на папку меню Пуск. В меню пуск будет создана папка ApplicationServer.

Добавим файлы к папке установки. Для этого создадим файл Files.wxs и запишем в него следующее содержимое.

<DirectoryRef Id="INSTALLLOCATION" 
     FileSource="..\..\InstallingApplication\ApplicationWindowsService\bin\Debug\">
    <Component Id ="ProductComponents" 
        DiskId="1" 
        Guid="{B65CDCEE-7130-4759-A8E8-16D84717CCF2}">
        <File Id="ApplicationService.Common.dll" Name="ApplicationService.Common.dll"/>
        <File Id="ApplicationWindowsService.exe" Name="ApplicationWindowsService.exe"/>
        <File Id="ApplicationWindowsService.exe.config" Name="ApplicationWindowsService.exe.config"/>
        <File Id="log4net.dll" Name="log4net.dll"/>
    </Component>
</DirectoryRef>


Секция DirectoryRef ссылается на описанную ранее папку c идентификатором INSTALLLOCATION. Атрибут FileSource содержит путь, по которому необходимо искать все файлы, добавляемые в инсталлятор. В данном случае указан относительный путь до скомпилированных модулей нашей службы.

Секция Component включает описание устанавливаемых файлов. Для задания значения атрибуту Guid рекомендую использовать тулзу Create Guid из Visual Studio (Меню Tools Create GUID). Далее секция File описывает конкретный устанавливаемый файл. Секция для корректной работы должна содержать уникальный идентификатор Id (в моем случае он совпадает с именем файла). Атрибут Name указывает, что файл нужно искать по заданному имени в директории FileSource.

Теперь давайте опишем ярлыки, которые мы будем создавать. В меню Пуск мы создадим два ярлыка: для запуска приложения и для деинсталляции. На практике для серверных приложений не стоит указывать ярлык для запуска, и здесь я это привожу только для примера. Создадим файл Shortcuts.wxs и добавим в него следующее.

<DirectoryRef Id="ApplicationProgramsFolder">
    <Component Id="ApplicationShortcuts" Guid="{DFA97C7E-E565-44D2-8D1E-7AB1E52ADB01}">
        <Shortcut Id="StartShortcut"
            Name="Application Server"
            Description="Start application server"
            Target="[INSTALLLOCATION]ApplicationWindowsService.exe"
            WorkingDirectory="INSTALLLOCATION"/>
        <Shortcut Id="UninstallApplicationServer"
            Name="Uninstall application serrver"
            Description="Uninstall application serrver"
            Target="[System64Folder]msiexec.exe"
            Arguments="/x [ProductCode]"/>

        <RemoveFolder Id="ApplicationProgramsFolder" On="uninstall"/>
        <RegistryValue Root="HKCU"
            Key="Software\MyCompany\ApplicationServer"
            Name="installed"
            Type="integer"
            Value="1"
            KeyPath="yes"/>        
    </Component>
</DirectoryRef>

Секция Shortcut указывает на создание ярлыка. Первая секция создает ярлык для нашего приложения с указанием в качестве рабочей директории папки установки. Вторая секция Shortcut немного интересней. Она создает ярлык для деинсталляции нашего приложения. Для этого используется программа msiexec с ключом /x.

Секция RemoveFolder говорит о том, что при деинсталляции необходимо удалить папку с ярлыками из меню пуск. Оставшаяся секция RegistyValue необходима для того, чтобы удаление заработало. Я сильно не вдавался в подробности, но без создания этого ключа деинсталляция работать не будет.

На этом почти все. Осталось описать все наши компоненты в секции Feature файла Product.wxs. Этот элемент может быть использован, когда нам необходимо дать пользователю возможность выбора, что устанавливать, а что нет. В условиях нашей задачи ничего не говорилось о возможности выбора, но, несмотря на это нам необходимо привязать описанные секции Component к одной единственной Feature.
<Feature Id="ProductFeature" Title="ApplicationInstaller" Level="1">
    <ComponentRef Id="ProductComponents" />
    <ComponentRef Id="ApplicationShortcuts"/>
</Feature>
Напоследок допишем еще две строки для подключения графического интерфейса к нашему инсталлятору. К проекту инсталлятора необходимо добавить ссылку на сборку WixUIExtension из поставки WiX. Эта библиотека расширений позволит подключить интерфейс пользователя.

<Property Id="WIXUI_INSTALLDIR" Value="INSTALLLOCATION"></Property>
<UIRef Id="WixUI_InstallDir"/>
Все! Теперь после компиляции мы получим файл ApplicationInstaller.msi, который и является нашим готовым инсталлятором.

На этом все. Все исходники можно взять здесь.

Дополнительные источники:





2 комментария: