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

    В этот раз мы создадим кое-что посложнее, чем установочный пакет из первой статьи. Научимся вносить изенять шаги установки и создавать собственные диалоги установщика.


    Наш новый инсталлятор будет включать в себя следующие шаги:

    1. Приветствие

    2. Выбор способа установки



    3. Выбор компонентов для установки



    4. Создание ярлыков



    5. Все готово к установке
    6. Процесс установки
    7. Финальный диалог

    Будут устанавливаться две программы: Блокнот и Калькулятор.

    Установщик будет предлагать три вида установки: Обычная, Выборочная, Полная. Зададим условие: если пользователь выбрал обычную установку Калькулятор не должен устанавливаться, при полной должны устанавливаться и блокнот и калькулятор, при выборочной то, что выбрал пользователь.

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

    В прошлой статье я указал на необходимось включения в проект файла WixUI_ru-ru.wxl, на самом деле такой необходимости нет, т.к. файл с русской локализацией уже включен в текущую версию WiX. Не доглядел, каюсь.

    В качестве набора диалоговых окон в этот раз мы будем использовать набор WixUI_Mondo. Добавим ссылку на этот набор в конце файла Product.wxs в разделе <Product>.
    <Property Id="WIXUI_INSTALLDIR" Value="INSTALLLOCATION"></Property>
    <UIRef Id="WixUI_Mondo"/>


    Далее разделим наш проект на несколько файлов. Вынесем описание <Feature> в отдельный файл Features.wxs. Так же создадим отдельный файлы для описания устанавливаемых файлов Files.wxs, создаваемых ярлыков Shortcuts.wxs и вынесем переменные в файл Variables.wxi как было описано во второй статье.

    Теперь добавим файлы для установки: блокнот и калькулятор. Блокнот будет являться обязательным элементом, калькулятор — опциональным и будет входить в состав компонента блокнот как показано на скриншоте Выбор компонентов для установки

    В файл Files.wxs добавим следующий код:
    <DirectoryRef Id="INSTALLLOCATION" FileSource="C:\WINDOWS\system32\" DiskId="1">
     <Component Id="ComponentNotepad" Guid="{7A8E49AD-DDE6-4f82-BC1D-389E2AF2B1CB}">
      <File Id='Notepad' Name='notepad.exe'/>
     </Component>

     <Component Id="ComponentCalc" Guid="{0564AAFC-0AC9-4b0b-8ED9-452147CCEFFA}">
      <File Id='Calc' Name='calc.exe'/>
     </Component>
    </DirectoryRef>


    * This source code was highlighted with Source Code Highlighter.


    Мы создали описание двух разных компонентов с тем, чтобы можно было эти компоненты включить в разные опции (<Feature>) установки. Добавим опции установки, для этого в файл Features.wxs добавим код:
    <Feature Id="FeatureNotepad" Title="Блокнот" Description="Описание блокнота" Level="1" ConfigurableDirectory="INSTALLLOCATION">
     <ComponentRef Id="ComponentNotepad" />

     <Feature Id="FeatureCalc" Title="Калькулятор" Description="Описание калькулятора" Level="1">
      <ComponentRef Id="ComponentCalc" />
     </Feature>
    </Feature>


    * This source code was highlighted with Source Code Highlighter.


    Как видно опция установки FeatureCalc входит в состав FeatureNotepad, т.е. является зависимой и, соответственно, отображается как дочерний элемент в окне выбора компонентов для установки.
    В параметрах ключа <Feature> мы видим такие параметры как Title и Description. Данные параметры отвечают за название компонента в дереве выбора компонентов для установки и его описание, отображающееся в правой части окна при выборе компонента. Еще один интересный момент это параметр ConfigurableDirectory — задает идентификатор директории, путь которой будет изменяться при нажатии кнопки Обзор. В данном случае это директория INSTALLLOCATION, т.е. та, в которую будет устанавливаться наш продукт. Если Вы не укажете значение параметра ConfigurableDirectory, то пользователь не сможет изменить путь установки.

    Добавим ссылку на опции установки в файл Product.wxs:
    <FeatureRef Id="FeatureNotepad"/>

    Осталось добавить ярлыки в меню Пуск и можно собирать первую версию пакета. Для добавления ярлыков в файл Shortcuts.wxs вставим код:
    <DirectoryRef Id="ApplicationProgramsFolder">
     <Component Id="ShortcutNotepad" Guid="{29EB41BB-FCFA-4f71-B31A-9B265DA5C05D}">
      <Shortcut Id="ShortcutNotepad"
           Name="Блокнот"
           Description="$(var.ProductName)"
           Target="[INSTALLLOCATION]Notepad.exe"
           WorkingDirectory="INSTALLLOCATION"/>
      <RemoveFolder Id="ApplicationProgramsFolder" On="uninstall"/>
      <RegistryValue Root="HKCU"
            Key="Software\$(var.Manufacturer)\$(var.ProductName)"
              Name="installed"
              Type="integer"
              Value="1"
              KeyPath="yes"/>
     </Component>

     <Component Id="ShortcutCalc" Guid="{C050C54C-F1E9-4fb8-9179-666305ADF489}">
      <Shortcut Id="ShortcutCalc"
           Name="Калькулятор"
           Description="$(var.ProductName)"
           Target="[INSTALLLOCATION]Calc.exe"
           WorkingDirectory="INSTALLLOCATION"/>
      <RegistryValue Root="HKCU"
            Key="Software\$(var.Manufacturer)\$(var.ProductName)"
              Name="installed"
              Type="integer"
              Value="1"
              KeyPath="yes"/>
     </Component>
    </DirectoryRef>


    * This source code was highlighted with Source Code Highlighter.


    Описание ярлыков добавили, теперь необходимо добавить ярлыки в соответствующие опции установки:
    <Feature Id="FeatureNotepad" Title="Блокнот" Description="Описание блокнота" Level="1" ConfigurableDirectory="INSTALLLOCATION">
     <ComponentRef Id="ComponentNotepad" />
     <ComponentRef Id="ShortcutNotepad"/>

     <Feature Id="FeatureCalc" Title="Калькулятор" Description="Описание калькулятора" Level="1">
      <ComponentRef Id="ComponentCalc" />
      <ComponentRef Id="ShortcutCalc"/>
     </Feature>
    </Feature>


    * This source code was highlighted with Source Code Highlighter.


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

    Начнем вносить изменения в порядок отображения окон матсера установки. Для этого необходимо либо создать собственный набор дилоговых окон, либо взять за основу готовый и внести свои изменения. Чтобы сократить время скачаем архив с исходниками WiX, распакуем, найдем файл WiXUI_Mondo.wxs (src\ext\UIExtension\wixlib), переименуем в WiXUI_Wizard.wxs. Далее открываем этот файл находим строку:
    <UI Id="WixUI_Mondo">
    и изменяем на
    <UI IdWixUI_Wizard>

    Сделано для того, чтобы при сборке мы не получили ошибку "Duplicate symbol 'WixUI:WixUI_Mondo' found." Т.к. элемент с идентификатором WixUI_Mondo уже имеется в библиотеке WixUIExtension ссылку на которую мы добавили в наш проект.

    Включаем этот файл в наш проект. Меняем ссылку:
    <UIRef Id="WixUI_Mondo"/>
    на
    <UIRef Id="WixUI_Wizard"/>

    Уберем из пакета лицензионное соглашение, для этого удалим из файла WixUI_Wizard.wxs строки:
    <Publish Dialog="LicenseAgreementDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
    <Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="SetupTypeDlg" Order="2">LicenseAccepted = "1"</Publish>


    Эти строки определяли действия при нажатии кнопок Далее и Назад в диалоге лицензионного соглашения.

    Изменим строку:
    <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDlg">1</Publish>
    на
    <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="SetupTypeDlg">1</Publish>

    Данная строка задвала действие при нажатии кнопки Далее в окне приветствия. Мы изменили действие и назначили отображение окна выбора типа установки (SetupTypeDlg) при нажатии кнопки Далее.

    Строку:
    <Publish Dialog="SetupTypeDlg" Control="Back" Event="NewDialog" Value="LicenseAgreementDlg">1</Publish>
    изменим на
    <Publish Dialog="SetupTypeDlg" Control="Back" Event="WelcomeDlg" Value="LicenseAgreementDlg">1</Publish>

    Данная строка задвала действие при нажатии кнопки Назад в окне выбора типа установки. Мы изменили дествие и назначили возврат к окну приветсвия при нажатии кнопки Назад.

    Все, что мы сделали это удалили упоминание об окне с лицензионным соглашением и «перескочили» через него, переопределив реакцию кнопок Назад и Далее. Строки, определяющие реакцию кнопок Назад И Далее в окне лицензионного соглашения можно было бы и не убирать, переопределив действия кнопок Далее и Назад в диалогах приветствия и выбора способа установки мы и так исключили диалог с лицензией из процесса установки.

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

    Создадим собственное диалоговое окно создания ярлыков. Добавим в проект новый файл WixUI_Shortcuts.wxs в котором определим внешний вид нового диалогового окна:
    <?xml version="1.0" encoding="UTF-8"?>

    <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
     <Fragment>
      <UI>
       <Dialog Id="ShortcutsDlg" Width="370" Height="270" Title="!(loc.WelcomeDlg_Title)">
        <Control Id="Next" Type="PushButton" X="248" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)" />
        <Control Id="Back" Type="PushButton" X="192" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)" />
        <Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
         <Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
        </Control>

        <Control Id="ShortcutDesktop"
             Type="CheckBox"
             Height="18"
             Width="295"
             X="26" Y="58"
             Text="Создать ярлык на рабочем столе"
             Property="SHORTCUT_DESKTOP"
             CheckBoxValue="1" />
        <Control Id="ShortcutProgramMenu"
             Type="CheckBox"
             Height="18"
             Width="295"
             X="26" Y="79"
             Text="Создать ярлык в меню Пуск"
             Property="SHORTCUT_PROGRAMMENU"
             CheckBoxValue="1" />

        <Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.CustomizeDlgBannerBitmap)" />
        <Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="2" />
        <Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="2" />
        <Control Id="Title" Type="Text" X="15" Y="6" Width="210" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.CustomizeDlgTitle)" />
       </Dialog>
      </UI>
     </Fragment>
    </Wix>


    * This source code was highlighted with Source Code Highlighter.


    Создавать диалоги можно двумя способами: создавать самостоятельно путем редактирования кода, либо можно воспользоваться визуальным редактором, например Sharp Develop. Я так и сделал, взяв за основу диалог SetupTypeDlg.wxs (src\ext\UIExtension\wixlib) удалил лишнее, добавил недостающее.

    Включим наш новый диалог в процесс установки. Новый диалог должен отображаться вслед за диалогом выбора компонентов для установки, либо вслед за диалогом выбора типа установки если будут нажаты кнопки Обычная или Полная. Откроем файл WixUI_Wizard.wxs и добавим следующее:
    <Publish Dialog="ShortcutsDlg" Control="Back" Event="NewDialog" Value="CustomizeDlg">1</Publish>
    <Publish Dialog="ShortcutsDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>


    * This source code was highlighted with Source Code Highlighter.


    Теперь необходимо переопределить реакцию на нажатия кнопок в диалогах SetupTypeDlg, CustomizeDlg, VerifyReadyDlg. Все вместе будет выглядеть так (я не привожу не измененные строки):
    <!-- Если выбрана Обычная установка перескакиваем на диалог создания ярлыков -->
    <Publish Dialog="SetupTypeDlg" Control="TypicalButton" Event="NewDialog" Value="ShortcutsDlg">1</Publish>
    <!-- Если выбрана Полная установка перескакиваем на диалог создания ярлыков -->
    <Publish Dialog="SetupTypeDlg" Control="CompleteButton" Event="NewDialog" Value="ShortcutsDlg">1</Publish>

    <Publish Dialog="CustomizeDlg" Control="Next" Event="NewDialog" Value="ShortcutsDlg">1</Publish>

    <!-- Если нажата кнопка Назад и была выбрана Выборочная установка вернуться к диалогу выбора компонентов установки -->
    <Publish Dialog="ShortcutsDlg" Control="Back" Event="NewDialog" Value="CustomizeDlg" Order="1">
     WixUI_InstallMode = "InstallCustom"
    </Publish>
    <!-- Если нажата кнопка Назад и была выбрана Полная или Обычная установка вернуться к диалогу выбора вида установки -->
    <Publish Dialog="ShortcutsDlg" Control="Back" Event="NewDialog" Value="SetupTypeDlg" Order="2">
     WixUI_InstallMode = "InstallTypical" OR WixUI_InstallMode = "InstallComplete"
    </Publish>

    <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="ShortcutsDlg">1</Publish>
    <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="CustomizeDlg" Order="1">
     WixUI_InstallMode = "Change"
    </Publish>
    <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2">
     WixUI_InstallMode = "Repair" OR WixUI_InstallMode = "Remove"
    </Publish>


    * This source code was highlighted with Source Code Highlighter.


    Собираем, запускаем, видим новое окно. Одна проблема, стоят чекбоксы или не стоят — ярлык на рабочем столе не создается, в меню Пуск создается в любом случае. Т.е. никакой реакции. А все потому, что мы к этим чекбоксам ничего не привязали. Для начале в файл Product.wxs добавим два новых свойства:
    <Property Id="SHORTCUT_PROGRAMMENU">1</Property>
    <Property Id="SHORTCUT_DESKTOP">1</Property>


    Одноименные свойства есть и у наших чекбоксов: Property=«SHORTCUT_DESKTOP» и Property=«SHORTCUT_PROGRAMMENU», отсюда и связь. Если мы будем менять состояние чекбоксов в диалоговом окне будут меняться и значение Property. Теперь надо увязать создание ярлыков и значения свойств. Делается это очень просто. В компонент добавляется условие:
    <Condition>SHORTCUT_PROGRAMMENU</Condition>

    Если выражение внутри ключа <Condition> истинно, то компонент будет устанавливаться.

    Все вместе выглядит так:
    <Component Id="ShortcutNotepad" Guid="{29EB41BB-FCFA-4f71-B31A-9B265DA5C05D}">
     <Shortcut Id="ShortcutNotepad"
          Name="Блокнот"
          Description="$(var.ProductName)"
          Target="[INSTALLLOCATION]Notepad.exe"
          WorkingDirectory="INSTALLLOCATION"/>
     <RemoveFolder Id="ApplicationProgramsFolder" On="uninstall"/>
     <RegistryValue Root="HKCU" Key="Software\$(var.Manufacturer)\$(var.ProductName)" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
     <Condition>SHORTCUT_PROGRAMMENU</Condition>
    </Component>


    * This source code was highlighted with Source Code Highlighter.


    Не забудем добавить <Condition> и для компонента ShortcutCalc.

    Все, что осталось сделать, это добавить создание ярлыков на рабочем столе. Добавим новую директорию в файл Products.wxs
    <Directory Id="DesktopFolder" Name="Desktop"/>


    Описание ярлыков в файл Shortcuts.wxs:
    <DirectoryRef Id="DesktopFolder">
     <Component Id="DesktopShortcutNotepad" Guid="{9746557B-59B1-46de-B369-5F454A946698}">
      <RegistryKey Root="HKCU" Key="YourAppKey\PossibleSubKey" Action="createAndRemoveOnUninstall">
       <RegistryValue Name="AnyValueName" Value="1" Type="integer" KeyPath="yes"/>
      </RegistryKey>
      <Shortcut Id="DesktopShortcut" Directory="DesktopFolder" Name="Блокнот" Target="[INSTALLLOCATION]Notepad.exe"/>
      <Condition>SHORTCUT_DESKTOP</Condition>
     </Component>

     <Component Id="DesktopShortcutCalc" Guid="{B4908FF0-96C6-4f12-8E64-BC366E1147E1}">
      <RegistryKey Root="HKCU" Key="YourAppKey\PossibleSubKey" Action="createAndRemoveOnUninstall">
       <RegistryValue Name="AnyValueName" Value="1" Type="integer" KeyPath="yes"/>
      </RegistryKey>
      <Shortcut Id="DesktopShortcut" Directory="DesktopFolder" Name="Калькулятор" Target="[INSTALLLOCATION]Calc.exe"/>
      <Condition>SHORTCUT_DESKTOP</Condition>
     </Component>
    </DirectoryRef>


    * This source code was highlighted with Source Code Highlighter.


    Добавим ссылки на ярлыки в файл Features.wxs:
    <Feature Id="FeatureNotepad" Title="Блокнот" Description="Описание блокнота" Level="1" ConfigurableDirectory="INSTALLLOCATION">
     <ComponentRef Id="ComponentNotepad" />
     <ComponentRef Id="ShortcutNotepad"/>
     <ComponentRef Id="DesktopShortcutNotepad"/>

     <Feature Id="FeatureCalc" Title="Калькулятор" Description="Описание калькулятора" Level="1">
      <ComponentRef Id="ComponentCalc" />
      <ComponentRef Id="ShortcutCalc"/>
      <ComponentRef Id="DesktopShortcutCalc"/>
     </Feature>
    </Feature>


    * This source code was highlighted with Source Code Highlighter.


    Почти закончили. Осталось выполнить еще одно условие, при обычной установке устанавливаться должен только блокнот. Для выполнения этого условия добавим <Condition> в <Feature>, отвечающую за установку калькулятора.
    <Condition Level="0">INSTALLLEVEL=3</Condition>

    Параметр Level при использовании <Condition> внутри <Feature> является обязательным. Он задает Level самой </Feature> если выполняется условие. В данном случае это INSTALLLEVEL=3 значение 3 устанавливается свойству INSTALLLEVEL если пользователь выбрал Обычную установку. Как я это узнал? Посмотрел исходники диалога выбора вида установки SetupTypeDlg.wxs (там, кстати сказать, вообще очень много интересного)
    <Publish Event="SetInstallLevel" Value="3">1</Publish>

    Значение 0 параметра Level означает, что опция установки будет отключена.

    Собираем, запускаем, устанавливаем.

    Исходники проекта можно скачать здесь

    На этом все. В следующий раз я расскажу о CustomActions, очень интересная штука, позволяющая добавлять в пакет установки любые действия.
    Поделиться публикацией

    Комментарии 10

      +1
      Огромное спасибо, учитываю малое количество тьюориалов по виксу Ваши статьи ОЧЕНЬ полезны.
      Вас не затруднит, когда будете рассказывать про custom actions, упомянуть как в них использовать visual basic script? С .dll там более-менее все просто, а вот как бейсик в викс добавить — практически вообще не освещено ^_^.
        0
        Обязательно затронем и VBscript и DLL
        +2
        NSIS наше всё.
        за консольный билдер сто плюсов им в карму.
          0
          Давайте не будем сравнивать. Я использую и WiX и NSIS, и мне ни разу не приходило в голову их сравнивать. Каждый хорош по своему. Выбор инструмента (WiX или NSIS) определяют условия поставленной задачи.
            0
            пример установки Side-by-side assembly при помощи NSIS встудию!
              0
              Извините, если не правильно вас понял, но инсталляционные пакеты с помощью WiX можно собирать и с помощью командной строки.
                0
                ооооооооооооооооооооо!
              0
              Большое спасибо за обзор. Если будет возможность, расскажите пожалуйста про создания пакета установки с возможностью инсталляции его в нескольких версиях на одной рабочей станции (Installing Multiple Instances of Products and Patches) — уж очень интересная тема, особенно в связке с WiX.
                0
                Интересно было прочесть как добавить «правильную» автозагрузку программы при старте системы.
                Я вот пока не разобрался.
                Кстати, может кто наблюдал такое: попробовал запускать её через планировщик задач и программа стартует, но её настройки хранятся в текстовых файлах в директории программы.
                Когда я стартую её руками — всё хорошо и она их читает. Но когда через скрипт или планировщик, она упорно капризничает и не хочет читать эти файлы.
                Есть у кого мысли по этой теме?
                  0
                  Про автозагрузку можно найти здесь см.Providing automatic program start via the Registry

                  Про файл с настройками — скорее всего вы открываете файл на чтение передавая его название, а не полный путь к файлу. При запуске вручную у вас текущая директория = директории где расположена программа и файл открывается. При запуске планировщиком — текущая что-то вроде c:\windows\system32 (не уверен, но это не важно). Вам необходимо открывать файл передавая полный (абсолютный) путь.

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

                Самое читаемое