Pull to refresh

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

Reading time 6 min
Views 25K
В прошлый раз мы научились создавать простой инсталлятор. Прежде чем двинуться дальше, от простого к сложному, давайте научимся управлять этим самым сложным. А именно — научимся разбивать проект на части с тем, чтобы упростить сопровождение и внесение изменений.



Первый проект у нас состоял из одного единственного файла, в котором были прописаны все инструкции для установщика. Устанавливался один файл (калькулятор). Теперь представим проект посерьезнее, включающий, например, 10, 20, а то и 100 файлов. Если мы включим описание этих файлов в файл проекта, то потом, когда нам понадобится что-то изменить мы поймем, что искать в этой куче что-то определенное очень сложно.

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

<?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
 
 <Fragment>

  <DirectoryRef Id="INSTALLLOCATION">
    <Component Id="ProductComponent" Guid="b11556a2-e066-4393-af5c-9c9210187eb2">
        <File Id='Calc' DiskId='1' Source='C:\WINDOWS\system32\calc.exe'/>
    </Component>
  </DirectoryRef>

 </Fragment>
</Wix>

* This source code was highlighted with Source Code Highlighter.



Удалим описание компонента из файла Product.wxs, чтобы не получить сообщение об ошибке в процессе сборки проекта.

Было
<Directory Id="INSTALLLOCATION" Name="$(var.ProductName)">
  <Component Id="ProductComponent" Guid="b11556a2-e066-4393-af5c-9c9210187eb2">
    <File Id='Calc' DiskId='1' Source='C:\WINDOWS\system32\calc.exe'/>
  </Component>
</Directory>

* This source code was highlighted with Source Code Highlighter.



Стало
<Directory Id="INSTALLLOCATION" Name="$(var.ProductName)"/>
* This source code was highlighted with Source Code Highlighter.



В коде появились два новых ключа <Fragment> и <DirectoryRef>.

<Fragment> содержит внутри фрагмент кода, который будет включен в проект при сборке.
<DirectoryRef> ссылка на раздел Directory. Позволяет вынести, например, описание компонентов за пределы ключа <Directory>.

Кстати сказать, ключи вида <*Ref> упрощают процесс разделения проекта на фрагменты, и помогают упорядочить код. Помимо <DirectoryRef> существуют ссылки <FeatureRef>,<ComponentRef> и другие.

Чтобы немного усложнить проект добавим в него установку блокнота. Модифицируем код следующим образом:

<DirectoryRef Id="INSTALLLOCATION">
  <Component Id="ProductComponent" Guid="b11556a2-e066-4393-af5c-9c9210187eb2">
    <File Id='Calc' DiskId='1' Source='C:\WINDOWS\system32\calc.exe'/>
    <File Id='Notepad' DiskId='1' Source='C:\WINDOWS\system32\notepad.exe'/>
  </Component>
</DirectoryRef>

* This source code was highlighted with Source Code Highlighter.



Добавили. Что смущает? Путь к каждому файлу. А что если файлы будут перенесены? А что если их будет штук 100? Поиск с заменой поможет, но это не наш метод. Знакомимся с новым параметром ключа <DirectoryRef>FileSource. Данный параметр позволяет задать базовый путь. Перепишем с учетом новых знаний, заодно и DiskId='1' вынесем в определение компонента. Зачем его каждый раз указывать если все дочерние элементы используют одно и то же значение?

<DirectoryRef Id="INSTALLLOCATION" FileSource="C:\WINDOWS\system32\">
  <Component Id="ProductComponent" Guid="b11556a2-e066-4393-af5c-9c9210187eb2" DiskId='1'>
    <File Id='Calc' Name='calc.exe'/>
    <File Id='Notepad' Name='notepad.exe'/>
  </Component>
</DirectoryRef>

* This source code was highlighted with Source Code Highlighter.



Обратите внимание, что параметр Source ключей <File> был изменен на Name. Т.е. мы указали искать по имени файла внутри указанной директории, а не по абсолютному пути файла. Если этого не сделать то мы будем получать сообщение об ошибке «не удается найти указанный файл».

Блокнот добавили, добавим и ярлык на него. Находим создание ярлыка для калькулятора и видим кашу. Компонент для создания ярлыка описан внутри директории. Вынесем создание ярлыков в отдельный файл. Для этого добавим к проекту файл Shortcuts.wxs:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Fragment>

    <DirectoryRef Id="ApplicationProgramsFolder">
      <Component Id="ApplicationShortcutCalc" Guid="4CEBD68F-E933-47f9-B02C-A4FC69FDB551">

        <Shortcut Id="ShortcutCalc"
          Name="Calc"
          Description="$(var.ProductName)"
          Target="[INSTALLLOCATION]Calc.exe"
          WorkingDirectory="INSTALLLOCATION"/>

        <Shortcut Id="ShortcutNotepad"
          Name="Notepad"
          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>
    </DirectoryRef>

  </Fragment>
</Wix>

* This source code was highlighted with Source Code Highlighter.



Если мы сейчас попытаемся собрать проект, то получим сообщение об ошибке: Undefined preprocessor variable '$(var.ProductName)'

Все верно, при обработке файла Shortcuts.wxs ничего не известно о переменных, объявленных в файле Product.wxs. Что делать?

Помимо фрагментов WiX позволяет использовать включаемые файлы (include). Содержимое такого файла может быть определено следующим образом:

<?xml version="1.0" encoding="utf-8"?>
<Include>
  <!-- Содержимое файла -->
</Include>

* This source code was highlighted with Source Code Highlighter.



В отличие от фрагмента, инклуд, не будет включен в сборку автоматически до тех пор, пока вы не включите его в какой-либо файл самостоятельно. Для того, чтобы понять, что такое инклуд давайте вынесем определение переменных из файла Product.wxs в отдельный файл. Для этого добавим к проекту новый файл Variables.wxi, и перенесем в него объявление переменных:

<?xml version="1.0" encoding="utf-8"?>
<Include>
 
 <?define ProductName="SetupProject2" ?>
 <?define ProductVersion="1.0.0.0" ?>
 <?define ProductCode="b7bc7c6f-9a4e-4973-be84-eca8e3427c97"?>
 <?define UpgradeCode="06a81104-1e30-463d-87e1-e8a79b4c682a"?>
 <?define Manufacturer="MyCompany"?>
 
</Include>

* This source code was highlighted with Source Code Highlighter.



А определение переменных в файле Product.wxs заменим на:

<?include Variables.wxi?>
* This source code was highlighted with Source Code Highlighter.



Так же не забудем добавить эту же строку в файл Shortcuts.wxs. После этого пакет установки должен собраться без ошибок.

Был один файл Product.wxs стало четыре файла. Для простого пакета установки, возможно, такое разделение на фрагменты не имеет смысла, особенно если этот пакет пишется на один раз. Для крупного проекта, включающего установку десятков файлов, создание ярлыков — разделение жизненно необходимо. Иначе, в таком проекта, как говорится — черт ногу сломает. Еще один плюс использования фрагментов — возможность повторного использования готового кода.

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

Исходники и проект для Visual Studio можно скачать здесь
Tags:
Hubs:
+2
Comments 7
Comments Comments 7

Articles