» Создание своего инсталлятора на NSIS. Секции и выбор компонентов

K.A.V.
www.oszone.net
22.02.2010
Автор: hb860
Последнее обновление: 22.02.2010

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

Реализация выбора папки установки
Итак, для того, чтобы у пользователя была возможность выбрать папку назначения, необходимо в код скрипта вставить макрос, формирующий соответствующую страницу:
!insertmacro MUI_PAGE_DIRECTORY


Этот макрос работает с системной переменной $INSTDIR, грубо говоря, он читает её значение перед отображением страницы пользователю, и записывает в неё путь к папке, выбранной пользователем после закрытия этой страницы.
Таким образом, чтобы при запуске инсталлятора пользователь видел папку по умолчанию, необходимо задать начальное значение $INSTDIR. Делается это специальной командой:
InstallDir "$PROGRAMFILES\My Super App"


Теперь, когда пользователь запустит инсталлятор, на странице выбора папки установки он увидит путь: C:\Program Files\My Super App
Давайте внесем изменения в наш скрипт по автоматической установке браузера Opera.

Приведем его к следующему виду:
!define PRODUCT_NAME "Opera"
!define PRODUCT_VERSION "10.10"
!define pkgdir "d:\package"

!include "MUI.nsh"
SetCompressor /SOLID lzma

!define MUI_ABORTWARNING
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE "Russian"

Name "${PRODUCT_NAME}"
Caption "Установка ${PRODUCT_NAME} ${PRODUCT_VERSION}"
InstallDir "$PROGRAMFILES\${PRODUCT_NAME}"
OutFile "${PRODUCT_NAME} ${PRODUCT_VERSION}.exe"
ShowInstDetails show

Section 
SetOutPath "$TEMP" 
File"${pkgdir}\opera\1049.MST" 
File"${pkgdir}\opera\opera installer.msi" 
ExecWait "msiexec.exe /i $\"$TEMP\opera installer.msi$\" /qb INSTALLDIR=$\"$INSTDIR$\" ALLUSERS=1 CREATE_DESKTOP_ICON=1 CREATE_QUICKLAUNCH_ICON=1 CREATE_STARTMENU_ICONS=1 MULTI_USER_SETTING=1 TRANSFORMS=$\"$TEMP\1049.MST$\""
Delete "$TEMP\opera installer.msi" 
Delete "$TEMP\1049.MST" 
 
File"${pkgdir}\opera\install_flash_player.exe" 
ExecWait "$TEMP\install_flash_player.exe /S" 
Delete "$TEMP\install_flash_player.exe" 
 
SetOutPath "$PROGRAMFILES\opera\Skin" 
File"${pkgdir}\opera\*.zip" 
SectionEnd


Мы добавили страницу выбора папки установки сразу после страницы приветствия инсталлятора, и задали значение по умолчанию равным $PROGRAMFILES\${PRODUCT_NAME}, то есть C:\program files\Opera. Также изменению подверглась строка установщика Windows, мы передали в неё дополнительный параметр INSTALLDIR=$\"$INSTDIR$\", таким образом передав из папку установки из NSIS в MSI.

Реализация выбора компонентов приложения. Секции.

Для организации возможности выбора пользователя, что ему ставить из вашего приложения, а что нет, сначала следует подключить макрос, который сформирует соответствующую страницу инсталлятора.
!insertmacro MUI_PAGE_COMPONENTS


Вставим её между страницей выбора папки и страницей хода выполнения процесса установки, тогда набор страниц инсталлятора примет вид:
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH


Затем необходимо изменить опции команды Section.
В общем случае эта команда может иметь следующий вид:
Section “Это моя программа”
- в данном виде секция отображается как опция с чекбоксом(или попросту, квадратик, где можно поставить или снять галочку) и с именем, указанным в кавычках.


Section “!Это моя программа”
- название секции будет жирным


Section/o “Это моя программа”
- по умолчанию опция будет не выбрана


Section "-Эту секцию не видно"

или
Section # не видно
— секция скрыта и не отображается в компонентах инсталятора

Кроме того, для секции может задан идентификатор, по которому можно получить доступ к секции из кода инсталлятора. Например, вот так:
Section “test2” sec2_id
SectionEnd
Section “-post”
SectionGetText ${sec2_id} $0
MessageBox MB_OK "Заголовок секции sec2_id:$\n$0" 
SectionEnd


Функция SectionGetText перечитывает заголовок секции в служебную переменную $0 (К слову, NSIS имеет ряд служебных переменных, не требующих объявления. Мы рассмотрим переменные позднее), результат выводится в диалоговое окно.

Секции можно объединять в логические группы. Для этого служит операторный блок SectionGroup … SectionGroupEnd. Объединенные в группу секции удобно управляются пометкой на группе, таким образом, можно выбрать или отменить выбор всех секций, входящих в состав группы

Пример:
SectionGroup "Обои"
Section "Красивая картинка"
SectionEnd
Section "Ещё одна картинка"
SectionEnd
SectionGroupEnd


Команда SectionGroup принимает всего один параметр: /e, который указывает, что секция должна быть раскрыта по умолчанию.

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

Section "!${PRODUCT_NAME}"
SetOutPath "$TEMP" 
File"${pkgdir}\opera\1049.MST" 
File"${pkgdir}\opera\opera installer.msi" 
ExecWait "msiexec.exe /i $\"$TEMP\opera installer.msi$\" /qb INSTALLDIR=$\"$INSTDIR$\" ALLUSERS=1 CREATE_DESKTOP_ICON=1 CREATE_QUICKLAUNCH_ICON=1 CREATE_STARTMENU_ICONS=1 MULTI_USER_SETTING=1 TRANSFORMS=$\"$TEMP\1049.MST$\""
Delete "$TEMP\opera installer.msi" 
Delete "$TEMP\1049.MST"
SectionEnd


А команды по установке Flash плеера и обложек оформления вынесем в отдельную группу секций:
SectionGroup /e "Дополнительные компоненты"
Section "Flash player for Opera"
File"${pkgdir}\opera\install_flash_player.exe" 
ExecWait "$TEMP\install_flash_player.exe /S" 
Delete "$TEMP\install_flash_player.exe" 
SectionEnd
Section "Обложки интерфейса"
SetOutPath "$PROGRAMFILES\opera\Skin" 
File"${pkgdir}\opera\*.zip" 
SectionEnd
SectionGroupEnd


Компилируем скрипт
Справа от секции отведено место для описания компонента. Как же его задать, возникает резонный вопрос. Это достаточно просто. Рассмотрим технологию на примере секции с бразуером.
Для задания описания воспользуемся командой LangString, общий вид использования которой:
LangString name language_id string


Эта команда создает константу вида ${name}, значение которой(string) зависит от текущего языка скрипта(language_id). Использование этой команды - общепринятый подход при создании многоязычного инсталлятора.

Необходимо добавить в конец скрипта следующий команды:
Например:
LangString message ${LANG_ENGLISH} "Here is in English"
LangString message ${LANG_RUSSIAN} "А тут по русски"


Соответственно, когда инсталлятор будет на русском языке, $(message) будет равно "А тут по русски", аналогично для английского. Обратите внимание, константы, заданные командой LangString, пишутся в (круглых) скобках, а не в {фигурных}.
Задаем для нашего случая строку в виде:
LangString DESC_OPERA ${LANG_RUSSIAN} "Браузер ${PRODUCT_NAME} ${PRODUCT_VERSION}"


Получим $(DESC_OPERA)=”Браузер Opera 10.10”

Теперь нам надо настроить модуль современного графического интерфейса, дабы использовать ${DESC_OPERA} в качестве описания. Это делается вызовом следующих макросов:
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${Section1} $(DESC_OPERA)
!insertmacro MUI_FUNCTION_DESCRIPTION_END


Макрос MUI_DESCRIPTION_TEXT требует в качестве первого параметра идентификатор секции. Поэтому зададим нашей секции идентификатор, например, такой:
Section "!${PRODUCT_NAME}" secOPERA


Следует иметь ввиду, что идентификатор определяется константой только после прохождения её кода, а это значит, что код ДО
Section "!${PRODUCT_NAME}" secOPERA
не увидит значения ${secOPERA}, в то время как код ПОСЛЕ этой секции — увидит. Поэтому добавляем код описания в конец скрипта. По аналогии создадим описание и для оставшихся секций, у нас должно получиться что-то подобное:

Section "!${PRODUCT_NAME}" secOPERA
SetOutPath "$TEMP"
File"${pkgdir}\opera10\1049.MST"
File"${pkgdir}\opera10\${MSI_NAME}"
ExecWait "msiexec.exe /i $\"$TEMP\${MSI_NAME}$\" /qb ALLUSERS=1 CREATE_DESKTOP_ICON=1 CREATE_QUICKLAUNCH_ICON=1 CREATE_STARTMENU_ICONS=1 MULTI_USER_SETTING=1 TRANSFORMS=$\"$TEMP\1049.MST$\""
Delete "$TEMP\${MSI_NAME}"
Delete "$TEMP\1049.MST"
SectionEnd

SectionGroup /e "Дополнительные компоненты"
Section "Flash player for Opera" secFlash
File"${pkgdir}\opera\install_flash_player.exe" 
ExecWait "$TEMP\install_flash_player.exe /S" 
Delete "$TEMP\install_flash_player.exe" 
SectionEnd
Section "Обложки интерфейса" secSkins
SetOutPath "$PROGRAMFILES\opera\Skin" 
File"${pkgdir}\opera\*.zip" 
SectionEnd
SectionGroupEnd

LangString DESC_OPERA ${LANG_RUSSIAN} "Браузер ${PRODUCT_NAME} ${PRODUCT_VERSION}"
LangString DESC_FLASH ${LANG_RUSSIAN} "Компонент Flash Player для просмотра Flash-анимаций в Opera"
LangString DESC_SKINS ${LANG_RUSSIAN} "Дополнительные скины для изменения внешнего вида браузера"

!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${secOPERA} $(DESC_OPERA)
!insertmacro MUI_DESCRIPTION_TEXT ${secFlash} $(DESC_FLASH)
!insertmacro MUI_DESCRIPTION_TEXT ${secSkins} $(DESC_SKINS)
!insertmacro MUI_FUNCTION_DESCRIPTION_END


Кстати,если вам вообще не нужны описания секций, вы можете отключить их, задав пустую константу
!define MUI_COMPONENTSPAGE_NODESC
в начале скрипта инсталлятора, но ПЕРЕД вызовом макроса MUI_PAGE_COMPONENTS примерно так:
!define MUI_ABORTWARNING
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
!define MUI_COMPONENTSPAGE_NODESC 
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH


Или вы можете переместить описание секций под элемент выбора компонентов, аналогично примеру выше, но определить надо константу
!define MUI_COMPONENTSPAGE_SMALLDESC
Я предпочитаю использовать именно этот вариант:

Используя секции, вы можете задать также варианты установки, такие, например, как полная, сокращенная и минимальная.
Сами типы установки задаются командой InstType, вот так:
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE "Russian"

InstType "Полная"
InstType "Сокращенная"
InstType "Минимальная"

Name "${PRODUCT_NAME}"


Команда InstType принимает также несколько опций

InstType "Полная" /NOCUSTOM
- отключает возможность самостоятельного выбора пользователем компонентов.
InstType "Полная" /CUSTOMSTRING= «Пользовательская»
- задает заголовок для случае, когда пользователь выбрал для установки набор компонентов, не совпадающий ни с одним типом установки инсталлятора.
InstType "Полная" /COMPONENTSONLYONCUSTOM
— список компонентов для выбора появляется только в случае нестандартной установки, т.е. «По выбору».
Для каждой секции следует определить, какому типу установки она будет соответствовать. Делается это командой SectionIn, вызванной внутри соответствующей секции. Вот так:
Section "!${PRODUCT_NAME}" secOPERA
SectionIn 1 2 3 RO


Секция secOPERA участвует во всех трёх типах установки, а опция RO означает, что флажок секции только для чтения, и пользователь не может снять или установить его.
Правим скрипт в соответствии с личными предпочтениями, у меня получилось вот что:
!define PRODUCT_NAME "Opera"
!define PRODUCT_VERSION "10.10"
!define pkgdir "d:\package"

!include "MUI.nsh"
SetCompressor /SOLID lzma

!define MUI_ABORTWARNING
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
!define MUI_COMPONENTSPAGE_SMALLDESC 
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE "Russian"

InstType "Полная"
InstType "Сокращенная"
InstType "Минимальная"


Name "${PRODUCT_NAME}"
Caption "Установка ${PRODUCT_NAME} ${PRODUCT_VERSION}"
OutFile "${PRODUCT_NAME}-${PRODUCT_VERSION}.exe"
InstallDir "$PROGRAMFILES\${PRODUCT_NAME}"
ShowInstDetails show

Section "!${PRODUCT_NAME}" secOPERA
SectionIn 1 2 3 RO
SetOutPath "$TEMP"
File"${pkgdir}\opera\1049.MST"
File"${pkgdir}\opera\opera installer.msi"
ExecWait "msiexec.exe /i $\"$TEMP\opera installer.msi$\" /qb ALLUSERS=1 CREATE_DESKTOP_ICON=1 CREATE_QUICKLAUNCH_ICON=1 CREATE_STARTMENU_ICONS=1 MULTI_USER_SETTING=1 TRANSFORMS=$\"$TEMP\1049.MST$\""
Delete "$TEMP\opera installer.msi"
Delete "$TEMP\1049.MST"
SectionEnd

SectionGroup /e "Дополнительные компоненты"
Section "Flash player for Opera" secFlash
SectionIn 1 2
File"${pkgdir}\opera\install_flash_player.exe" 
ExecWait "$TEMP\install_flash_player.exe /S" 
Delete "$TEMP\install_flash_player.exe" 
SectionEnd
Section "Обложки интерфейса" secSkins
SectionIn 1
SetOutPath "$PROGRAMFILES\opera\Skin" 
File"${pkgdir}\opera\*.zip" 
SectionEnd
SectionGroupEnd

LangString DESC_OPERA ${LANG_RUSSIAN} "Браузер ${PRODUCT_NAME} ${PRODUCT_VERSION}"
LangString DESC_FLASH ${LANG_RUSSIAN} "Компонент Flash Player для просмотра Flash-анимаций в Opera"
LangString DESC_SKINS ${LANG_RUSSIAN} "Дополнительные скины для изменения внешнего вида браузера"

!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${secOPERA} $(DESC_OPERA)
!insertmacro MUI_DESCRIPTION_TEXT ${secFlash} $(DESC_FLASH)
!insertmacro MUI_DESCRIPTION_TEXT ${secSkins} $(DESC_SKINS)
!insertmacro MUI_FUNCTION_DESCRIPTION_END


Таким образом, в данной статье мы рассмотрели способы взаимодействия с пользователем:

В следующей статье мы детально рассмотрим создание деинсталляторов.