Pull to refresh

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

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


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

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, очень интересная штука, позволяющая добавлять в пакет установки любые действия.
Tags:
Hubs:
+5
Comments 10
Comments Comments 10

Articles