Создание msi инсталлятора с использованием Wix

В этом посте я хотел бы описать свой опыт использования инструментария Wix для создания инсталляционных пакетов.

Wix - это библиотека которая упрощает создание MSI пакетов. Wix интегрируется в Visual Studio и содержит несколько типов проектов. Я не хочу полностью описывать процесс создания инсталляционного пакета, а кратко опишу сложности с которыми я столкнулся.

Полный туториал для изучения Wix можно легко найти например здесь: http://www.tramontana.co.hu/wix/

1. Deploy Database project output SQL script
Несмотря на то что что Wix содержит готовое расширение для установки БД в задаче пришлось делать по своему. Причиной тому послужило использование Visual Studio Database Project.

Итак сам процесс занял следующие шаги:

1.Настройка Database Project

По умолчанию Database Project самостоятельно задаёт переменную БД, в результате чего даже если передать название БД в параметры sqlcmd в скрипте она будет переопределена. Для того чтобы это предотвратить необходимо задать в настройках Database Project (Database.sqlDeployment) CommentOutSetVarDeclarations в false.

2.Создание CustomAction-а

Database project генерирует sql скрипт, для выполнения которого необходимо пользоваться утилитой sqlcmd. Поэтому для установки БД  необходимо вызывать sqlcmd из CustomAction-а, передавая в качестве параметра имя создаваемой БД.

Сам CustomAction всего лишь запускает процесс sqlcmd.exe через Process.Start. Аргументы получаются через CustomActionData.

В качестве аргументов задаются параметры подключения к SQL Server, а также имя БД.
Более полную информация о параметрах sqlcmd можно найти на http://msdn.microsoft.com/en-us/library/ms162773.aspx

3.Использование CustomAction-а

Добавим компонент который будет содержат сгенерированный SQL скрипт. Для того чтобы скрипт автоматически подтягивался из Database Project-а последний добавим в References.

<Component Id="SomeDatabase.sql" DiskId="1" Guid="E1B7D00A-D6D8-4694-B4E0-E9AF67877119">
 <File Id="SomeDatabase.sql" Source="$(var.SomeDatabase.TargetDir)SomeDatabase.sql" KeyPath="yes" />
Component>

Для того чтобы использовать созданный CustomAction добавляем в References в Setup Project, после чего обьявляем CustomAction в коде Wix


<Binary Id="DeploySQLDll" SourceFile="$(var.DeploySQL.TargetDir)DeploySQL.CA.dll"/>

<CustomAction 
         Id="action"
         BinaryKey="DeploySQLDll"
         DllEntry="Deploy"
         Return="ignore" Execute="deferred" />
 
<CustomAction Id="SetDeploySQLArguments" Return="check" Property="action" 
 Value='-U [SQLLOGIN] -P [SQLPASSWORD] -S [SQLSERVER] -i "[#SomeDatabase.sql]" -v DatabaseName="[DATABASENAME]" ' />
 

Естественно переменные SQLLOGIN, SQLPASSWORD, SQLSERVER должны быть обьявлены ранее в файле проекта Wix.

Последний штрих - вызов CustomAction в InstallSequence

<Custom Action="SetDeploySQLArguments" After="InstallFiles">NOT InstalledCustom>
      <Custom Action="action" After="SetDeploySQLArguments">NOT InstalledCustom>
 
NOT Installed говорит о том, что данные действия будут запущенны только при установке программы. В случае удаления действия будут пропущены. Если же после удаления программы необходимо удалить БД, можно пользоваться уже созданным CustomAction-ом, но вместа скрипта создания БД выполнять скрипт удаления. Например приведённый ниже:


EXEC msdb.dbo.sp_delete_database_backuphistory @database_name = N'$(DatabaseName)'
GO
 
USE [master]
GO
ALTER DATABASE $(DatabaseName) SET  SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
USE [master]
GO
DROP DATABASE $(DatabaseName)
GO

2. Сохранить значение Property для использования во время Uninstall-а

Допустим в процессе установки пользователь ввёл настройки подключения к БД для её установки,а во время удаления программы необходимо ими воспользоваться, чтобы БД удалить. К сожалению MSI не сохраняет данные значения, поэтому это придётся сделать самостоятельно.

Для этого необходимо:
1. Добавить сохранение свойства в реестр при установке:
<RegistryValue Root='HKLM' Key='SOFTWARE\OurApp\SetupUninstall' Name='SQLSERVER' Value='[SQLSERVER]' Type='string' />

2. При старте установщика искать в реестре значение свойства:
<Property Id="SQLSERVER" Value="localhost">
 <RegistrySearch Id='RememberProperty1' Root='HKLM'  Key='SOFTWARE\OurApp\SetupUninstall' Name='SQLSERVER' Type='raw' />
Property>

Теперь установщик будет сохранять введённые параметры установки. Не самый чистый способ, т.к. мусорит реестр, однако быстрый и надёжный.

3. Deploy Web-Site

Т.к. Web-Site добавить в Reference нельзя ( в отличии от Web-Application),  и к тому же он обычно содержит много файлов удобнее пользоваться входящий в комплект Wix инструментом heat. Данная тулза позволяет собирать из папки все файлы и организовать их как Wix Fragment File.

В моём случае тулза запускалась чтобы собрать сайт в файл фрагмента и далее добавить файлы в проект установщика. Для автоматической сборки в Pre-Build event добавлено событие:

"$(WIX)bin\heat.exe" dir "$(SolutionDir)\ABC\UI" -gg -ke -template Fragment -out "$(ProjectDir)UI.wxs" -sfrag -srd -sreg -dr UIRef -cg UI -wixvar -var  wix.UI

$(SolutionDir)\ABC\UI - путь к сайту

Файл с фрагментом создаётся в файле UI.wxs, для использования его в проекте Wix следует
 добавить файл в проект, а в главный Wix файл продукта ссылку на созданную директорию

<Directory Id="UIRef">Directory>
 
Кроме того необходимо определить Wix-переменную

<WixVariable Id="UI" Value="$(var.SolutionDir)\ABC\UI"/>

Как можно заметить Id директории задаётся в параметрах heat.exe. Более подробную информацию о параметрах
heat можно найти на http://wix.sourceforge.net/manual-wix3/heat.htm

Для создания веб-сайта в IIS можно воспользоваться готовыми расширениями Wix (WebApplication, WebDirectory и др.)

Плюсы:

  • Легко интегрируется в Visual Studio, проект установщика хранится в TFS
  • Возможна интеграция в MSBuild. Удобно для continuous integration.
  • Имеет множество возможностей для конфигурации
  • Open-source
Минусы:
  • Процесс написания инсталляционного пакета занимает больше времени чем для автоматических утилит ( InnoSetup например). 
В целом Wix очень удобен для инсталляции приложений, требующих сложной конфигурации и настройки ( deploy DB, Website, etc.).

Популярные сообщения из этого блога

Backbone.JS + ASP.NET WebAPI

Работа с LiqPay в ASP.NET и не только

2D Физика для игр - Separate Axis Theorem