Сообщений 2    Оценка 155        Оценить  
Система Orphus

Создание Microsoft installation package с помощью программного пакета MakeMsi.

Автор: Владимир Козлов
Источник: RSDN Magazine #5-2004
Опубликовано: 30.04.2005
Версия текста: 1.0
Введение
Создание инсталляции
Описание содержимого инсталляционного пакета
Описание интерфейса пользователя
Описание операций, выполняемых во время инсталляции
On-line ресурсы
Заключение

Введение

Технология Microsoft Windows Installer в настоящий момент является стандартом de-facto в области распространения программного обеспечения, и в распоряжении разработчиков имеется довольно широкий круг инструментов для создания инсталляционных пакетов формата msi.

С одной стороны представлены коммерческие продукты с большими возможностями, встроенными скриптовыми языками и солидным набором мастеров создания всего и вся - такие, как Wise Installer и InstallShield Studio. С другой, существует масса небольших, но порою весьма качественных инсталляторов (к примеру, Inno Setup) для создания ординарных инсталляций с помощью несложного графического интерфейса. Но среди всех подобных программ особое место занимает продукт под названием MakeMsi, созданный австралийцем Денисом Барисом (Dennis Bareis).

Дело в том, что MakeMsi практически не имеет графического интерфейса (если не считать интеграцию в контекстное меню проводника) и представляет собой набор макроопределений на языке REXX, которые с помощью специального транслятора преобразовываются в скрипт на Visual Basic, создающий требуемый msi-пакет. На первый взгляд сложно, но при детальном знакомстве с программой становится ясно, что подобный подход обладает рядом существенных преимуществ.

Во-первых, это традиционная форма написания программы. Скрипт для создания msi-пакета именно пишется, так как представляет собой простой текстовый файл. Этот факт весьма примечателен, так как дает возможность использовать комментарии, системы контроля версий и всеми любимую технологию «Copy/Paste». К тому же, общение с компьютером посредством консоли всегда идет человеку на пользу ("Вторая сигнальная система и мышь" :-).

Во-вторых, полный контроль над базой данных создаваемого пакета. MakeMsi позволяет работать напрямую со строками таблиц базы данных пакета при помощи SQL-подобных конструкций.

В-третьих, возможность модификации скриптов из состава MakeMsi. Если вас что-то не устраивает в работе продукта, вы можете изменить процесс формирования msi-пакета путем редактирования существующих макросов.

Создание инсталляции

Для облегчения процесса создания инсталляции в состав пакета входят два файла-заготовки – company.mm и dept.mm, на основе которых разработчику предлагается сформировать шаблоны для создания инсталляций в рамках организации в целом и отдельных ее подразделений в частности. В дополнение к ним имеется файл uisample.msi, содержащий пользовательский интерфейс для типовой инсталляции –диалог приветствия, текст лицензии, выбор типа установки, выбор компонентов и так далее. Поэтому простейший пример создания полноценного инсталляционного пакета выглядит более чем компактно:

#include "DEPT.MMH"

;--- Create INSTALLDIR ------------------------------------------------------
<$DirectoryTree Key="INSTALLDIR" 
  Dir="c:\program files\TryMe (makemsi sample)" CHANGE="\">
;--- Add the files (components generated) -----------------------------------
<$Files "TryMe.rtf" DestDir="INSTALLDIR">

Как видно из примера, инсталляция описывается с помощью набора специальных тегов. Эти теги могут использоваться как парно – открывающий и закрывающий <$имя_тега>текст<$/имя_тега>, так и одиночно – <$имя_тега текст>. Все они подробно описаны в прилагающейся справке, поэтому рассматривать мы здесь будем не синтаксис каждого тега, а общую логику их совместного использования.

В общем случае процесс создания инсталляции состоит из трех основных этапов:

Описание содержимого инсталляционного пакета

Данные об устанавливаемых программах хранятся в специальных таблицах базы данных msi-пакета, представленных на рисунке 1.


Рисунок 1.

Из представленной схемы, на которой для упрощения картины представлены лишь основные таблицы, видно, что приложение (product в терминологии Microsoft) состоит из опций (таблица Feature), имеющих иерархическую структуру, которые, в свою очередь, делятся на компоненты (таблица Component).

При этом компонент может принадлежать более чем к одной опции благодаря наличию таблицы FeatureComponents. Компонент является неделимой единицей, которой оперирует служба MS Installer в процессе установки, удаления или восстановления приложения. В состав компонента могут входить файлы (таблица File) и операции с реестром Windows (таблица Registry и RemoveRegistry).

ПРИМЕЧАНИЕ

Помимо этого, компонент может содержать переменные окружения, COM-объекты, DCOM-приложения, источники данных ODBC и другие объекты, но их рассмотрение выходит за рамки данной статьи.

Для заполнения вышеперечисленных таблиц предназначены одноименные теги. Логика их использования достаточно проста, поэтому сразу перейдем к примеру:

;--- Установка кодовой страницы -------------------------------------------
<$CodePage "1251">
;--- Создание модуля ------------------------------------------------------
#(
<$Feature "ClientFeature" 
  Title="Консоль администратора" 
  Description="Клиентское приложение" 
>
#)
;--- Создание компонента --------------------------------------------------
  <$Component "Client" Directory_="INSTALLDIR">
;--- Добавление файла в состав компонента----------------------------------
    <$File Source="Client.exe" RowKey="Client.exe" KeyPath="Y">
  <$/Component>
<$/Feature>

<$Feature "ServerFeature" Title="Сервер" Description="Серверное приложение">
  <$Feature "MonitorFeature" Title="Монитор" Description="Модуль контроля">
    <$Component "Monitor" Directory_="INSTALLDIR">
      <$File Source="Monitor.dll" RowKey="Monitor.dll" KeyPath="Y">
;--- Регистрация COM-объекта -----------------------------------------------
      <$SelfRegister "Monitor.dll">
    <$/Component>
  <$/Feature>
  <$Component "Server"  Directory_="INSTALLDIR">
    <$File Source="Server.exe" RowKey="Server.exe" KeyPath="Y">
  <$/Component>
<$/Feature>

СОВЕТ

Для более наглядного форматирования используются операции конкатенации строк – конструкция «#(...#)» и обратный слеш в конце строки.

Отображение структуры полученного инсталляционного пакета в диалоге выбора компонентов (CustomiseDlg) представлено на рисунке 2.


Рисунок 2.

Все достаточно просто, однако следует обратить внимание на некоторые детали.

Появился корневой модуль «Complete», который мы, вроде бы, не заказывали. Он создается по умолчанию в файле COMPANY.mmh в секции «Set up the "COMPLETE" feature». Иметь корневой модуль – дело, конечно, хорошее, но хотелось бы дать ему более подходящее название. Нет ничего проще, ведь COMPANY.mmh – это шаблон, который можно переделывать на свой вкус. Ищем в нем строку «@@T = 'Complete.<$ProductVersion>.<$ProdInfo.ProductName>'» и пишем в ней новое название корневого модуля. С другой стороны, бывают ситуации, когда корень в дереве выбора модулей не требуется. Это вариант также предусмотрен – необходимо только определить значение константы COMPANY_WANT_COMPLETE_FEATURE:

#define  COMPANY_WANT_COMPLETE_FEATURE  N

Рассмотрим подробнее тег <$Component>, который имеет ряд интересных параметров.

ПРИМЕЧАНИЕ

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

Параметр KeyPath. Указывает на файл или каталог, принадлежащий компоненту, по существованию которого в дальнейшем служба MS Installer будет судить, установлен компонент или нет. Естественно, что два компонента не могут иметь одинаковое значение в этом поле. Если значение KeyPath равно NULL, то ключом для поиска компонента является каталог, указанный в поле Directory_. Рекомендуется создавать по одному компоненту на каждый устанавливаемый файл, так как если из двадцати динамических библиотек, входящих в ваш компонент, на диске сохранится только одна, но именно та, которая указана в значении KeyPath, компонент будет считаться установленным корректно и не будет обновлен при восстановлении приложения. Установить значение этого свойства можно либо непосредственно в теге <$Component KeyPath=«value»>, либо в теге <$File> с помощью конструкции KeyPath="Y". Во втором случае значением свойства станет имя файла, указанного в поле Source.

Параметр Attributes. Его значения с описаниями представлены в таблице 1.

СвойствоЗначениеОписание
LocalOnly0Компонент может запускаться только при установке на жесткий диск.
SourceOnly1Компонент может запускаться только с источника инсталляции.
Optional2Компонент может запускаться как при установке на диск, так и из источника.
RegistryKeyPath4Компонент представляет операции с реестром.
SharedDllRefCount8При наличии этого атрибута MS Installer создаст в реестре ключи для подсчета ссылок на файлы компонента, считая компоненты разделяемыми библиотеками, и увеличит созданные счетчики на единицу. При отсутствии этого атрибута количество ссылок будет увеличено только для тех файлов, у которых счетчик ссылок уже существует.
Permanent16Компонент не будет удален при деинсталляции приложения.
ODBCDataSource32Компонент является источником данных ODBC.
Transitive64Применяется для транзитивных компонентов. Предполагает, что при переустановке приложения, если компонент был установлен, то он будет удален и наоборот.
NeverOverwrite128Компонент не будет переписан при установке или переустановке. При этом приложению не требуется регистрироваться как клиенту данного компонента. Применяется в основном для записей реестра.
Таблица 1. Свойства параметра Attributes.

Для иллюстрации следующего параметра, Condition, представим себе гипотетическую задачу: устанавливаемый продукт в своей работе использует технологию DCOM. При установке на операционную систему Windows XP SP2 необходимо удостовериться, что брандмауэр разрешает использование соответствующего порта, так как по умолчанию порт 135 закрыт, равно как и большинство других. Для решения этой задачи может служить компонент следующего вида:

#(
<$Component "XP_SP2_firewall_settings" 
  Condition="VersionNT=501 AND ServicePackLevel=2" Attributes="RegistryKeyPath"
>
#)
  #(
  <$Registry HKEY="LOCAL_MACHINE" 
    Key="SYSTEM\ControlSet001\Services\SharedAccess\Parameters\
    FirewallPolicy\StandardProfile\GloballyOpenPorts\List"  
    Name="135:TCP"  
    Value="135:TCP:*:Enabled:DCOM"
  >
  #)
<$/Component>

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

Свойства (Properties). Используемые в данном случае литералы «VersionNT» и «ServicePackLevel» являются встроенными свойствами, инициализацией которых занимается непосредственно служба MS Installer. Полный список встроенных свойств можно найти в разделе MSDN «Platform SDK: Windows Installer» . Помимо встроенных свойств существуют еще и пользовательские свойства, которые хранятся в таблице Property и могут быть добавлены с помощью тега <$Property>. Свойства могут быть как доступными из командной строки (Public property), так и недоступными (Private). Это зависит от имени свойства – если имя состоит только из заглавных букв, то свойство считаются Public, иначе – Private.

Состояние модулей и компонентов. Таких состояний может быть пять:

Состояние (константы из MS Install SDK)ЗначениеОписание
INSTALLSTATE_UNKNOWN-1С модулем или компонентом не производилось никаких действий.
INSTALLSTATE_ADVERTISED1Модуль будет установлен по требованию (для компонентов не применяется).
INSTALLSTATE_ABSENT2Не установлен.
INSTALLSTATE_LOCAL3Установлен на жесткий диск.
INSTALLSTATE_SOURCE4Запускается с источника инсталляции.
Таблица 2.

С помощью специальных префиксов, значения которых приведены в таблице 3, можно проверить, в каком состоянии находится модуль или компонент.

Тип аргументаПрефиксЗначение
Значение ключевого поля таблицы «Component”$Текущее действие над компонентом.
Значение ключевого поля таблицы «Component”?Действие над компонентом, выполненное во время предыдущей инсталляции.
Feature table key&Текущее действие над модулем.
Feature table key!Действие, выполненное над модулем во время предыдущей инсталляции.
Таблица 3.

В качестве примера приведу условие, принимающее истинное значение при ранее локально установленном модуле ServerFeature без компонента Monitor и выборе пункта «Запускать с носителя» для модуля Client при текущей инсталляции: “!ServerFeature=3 AND ?Monitor=2 AND &Client=4”.

Переменные окружения. Для их использования применяется префикс «%». Например “%NUMBER_OF_PROCESSOR=2”.

Помимо управления одиночными записями, для более объемных операций с реестром существует тег <$RegistryImport>. С его помощью можно включить в состав инсталляции содержимое reg-файла. Особенностью его использования является то, что указанный файл должен иметь формат “REGEDIT4”. С помощью таких параметров, как CURRENT_USER, LOCAL_MACHINE и CLASSES_ROOT, можно указать, к какому компоненту будут принадлежать импортируемые записи:

<$Component "Client" ...>
<$Component "Server" ...>
<$RegistryImport "settings.reg" LOCAL_MACHINE="Server" CURRENT_USER="Client">

Еще один использованный в нашем примере тег – <$SelfRegister >. Он предназначен для добавления записи в таблицу SelfReg. В качестве параметра этот тег принимает значение из ключевого столбца таблицы File. В процессе инсталляции при выполнении стандартных действий SelfRegModules и SelfUnRegModules для всех файлов, перечисленных в таблице SelfReg, будет выполнен вызов соответственно функций DllRegisterServer и DllUnregisterServer.

Описание интерфейса пользователя

Данные о пользовательском интерфейсе также хранятся в базе данных msi-пакета. Структура предназначенных для этого таблиц представлена на рисунке 3.


Рисунок 3

Интерфейс состоит из последовательности модальных диалогов (таблица Dialog), на которых расположены элементы управления (таблица Control). С каждым из элементов может быть ассоциировано одно или несколько событий (таблица ControlEvent), которые могут менять свои состояния (таблица ControlCondition) и взаимодействовать с другими элементами через подписку на их события (таблица EventMapping).

Входящий в состав пакета MakMsi файл UiSample.msi содержит стандартные диалоги, которых вполне достаточно для создания типовой инсталляции. Но иногда возникает потребность в изменении или расширении пользовательского интерфейса инсталляции.

Рассмотрим теги, предназначенные для манипуляций с диалогами.

<$DialogPreview "имя_диалога"> – позволяет просмотреть внешний вид диалога в процессе компиляции mm-файла. В названии диалога допускается использование символа «*» для обработки диалогов по шаблону. При отсутствии названия будут показаны все имеющиеся диалоги.

<$DialogRemove " имя_диалога "> – удаляет указанный диалог. Может использоваться только в скриптах, использующих в качестве основы файл UiSample.msi, и не предназначен для удаления первого и последнего диалога в последовательности.

<$Dialog> – создает новый диалог. В качестве иллюстрации использования данного тега рассмотрим пример настройки подключения к серверу БД в процессе инсталляции:

;--- Создание диалога -----------------------------------------------------
#(
<$Dialog "Подключение к серверу БД" 
  INSERT="WelcomeDlg" 
  Description="Укажите параметры подключения"
   Dialog="ConnectionDialog"
>
#)
;--- Создание радиокнопок Radio Button ------------------------------------
  #data 'RadioButton_SSPI'
    '1'   'Yes'       '50'  
    '2'    'No'       '75' 
  #data
;--- Создание полей ввода -------------------------------------------------
  #(
  <$DialogEntry Property="SSPI" 
    Label="Использовать Windows-идентификацию" 
    Control="RB"
  >
  #)
  #(
    <$DialogEntry Property="UID" 
    Label="Логин:" 
    ToolTip="Введите имя пользователя (10 символов)" 
    Max=10 
    Width=50
  >
  #)
  #(   
  <$DialogEntry Property="PWD" 
    Label="Пароль:" 
    ToolTip="Введите пароль (20 символов)" 
    Max=20 Password=Y 
    Width=50>
  <$/Dialog>
  #)

Сформированный таким образом диалог показан на рисунке 4.


Рисунок 4.

Параметр Insert предназначен для указания места вставки нового диалога. Представляет собой строку вида «имя_предыдущего_диалога[-имя_следующего диалога]». По умолчанию в качестве следующего диалога устанавливается диалог выбора типа инсталляции (SetupTypeDlg).

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

Элементы управления создаются с помощью тегов <$DialogEntry>, размещенных внутри тега <$Dialog>. С помощью перечисленных ниже параметров тега <$DialogEntry> можно реализовать достаточно сложный пользовательский интерфейс.

Параметр Label (обязательный) содержит текст подписи к компоненту. На рисунке 4 видно, что текст подписи к радио-кнопке выбора метода идентификации не выводится полностью. Для исправления этой ситуации предназначен макрос «DIALOGTEMPLATE_LABEL_COLUMN_WIDTH». В нашем случае достаточно задать ему значение 200:

#define DIALOGTEMPLATE_LABEL_COLUMN_WIDTH 200

Параметр Property (обязательный). Определяет пользовательское свойство, в котором будет сохранено значение элемента управления.

Параметр Control (необязательный). Определяет тип элемента управления. Значения этого параметра представлены в таблице 4.

ЗначениеОписание
EFПоле ввода. Значение по умолчанию.
XB[:False|True]Флажок. После двоеточия имеется возможность указать значения связанного свойства при невыбранном и выбранном (именно в таком порядке) состоянии элемента. По умолчанию используются значения 0 и 1.
ME:EditMaskПоле ввода по шаблону. Для задания шаблона используются следующие символы:“#” – цифра;“?” – цифра или буква;“=”, “.”, “\” и другие не цифробуквенные символы – сепараторы для разделения вводимого значения на отдельные поля ввода;“<text>” – при использовании этой конструкции поле ввода становиться неизменяемым, а все что находиться вне блока “<text>” не отображается;
CBКомбинированный список. Для его создания необходимо с помощью тега #data определить структуру данных, состоящую из двух столбцов. Первый предназначен для значения свойства, второй – для отображаемого текста. Допускает ввод новых значений пользователем.
CLТо же, что и CB, но допускает только выбор из списка.
RBПереключатель. Для его создания необходимо с помощью тега #data определить структуру данных, состоящую из четырех столбцов. Первый предназначен для значения свойства, второй – для отображаемого текста, третий определяет позицию для размещения следующей кнопки (если этот параметр не указан или равен "", то кнопка будет располагается на следующей строке), четвертый содержит текст всплывающей подсказки.
Таблица 4.

Параметр Max (необязательный). Определяет максимальное количество символов, допустимое в поле ввода.

Параметр Integer. Ограничивает вводимое значение только цифрами.

Для каждого элемента диалога генерируется функция проверки корректности. По умолчанию проверяется только наличие значения в поле. Для организации более сложных проверок предназначены параметры Blank (отключает проверку на обязательный ввод значения) и MatchRe (проверка с помощью регулярных выражений).

Необязательные параметры Password, Trim, Case, X и Y в комментариях не нуждаются.

Для придания законченности нашему примеру неплохо было бы отключать возможность ввода логина и пароля при выборе Windows-идентификации. Для этого необходимо использовать таблицу ControlCondition. Для работы с ней нет специальных тегов, поэтому добавим требуемые записи в помощью <$Table> и <$Row>:

<$Table "ControlCondition">
  <$Row Dialog_="ConnectionDialog" Control_="Entry.2" Action="Enable" \
    Condition="SSPI=2">
  <$Row Dialog_="ConnectionDialog" Control_="Entry.2" Action="Disable" \
    Condition="SSPI=1">
  <$Row Dialog_="ConnectionDialog" Control_="Entry.3" Action="Enable" \
    Condition="SSPI=2">
  <$Row Dialog_="ConnectionDialog" Control_="Entry.3" Action="Disable" \
    Condition="SSPI=1">
<$/Table>

Тег <$Table>, кроме имени таблицы, имеет всего один необязательный параметр Create. Значение “Y” подразумевает создание новой таблицы. По умолчанию этот параметр имеет значение “N”. При желании базу данных инсталляционного пакета можно дополнить собственными таблицами. Предположим, что необходимо добавить второй поток (естественно, только для файловой системы NTFS) к устанавливаемым файлам с различного рода служебной информацией. Для этого создадим новую таблицу, связанную с таблицей File, которая и будет содержать необходимые данные:

<$TableDefinition "FileStream">
  <$Column "File_"   TYPE="s72" Key="Y">
  <$Column "Info"    TYPE="S254"> ;;максимальная длина символьных полей
<$/TableDefinition>

<$Table "FileStream" Create="Y">
  #(
    <$Row @Validate="" 
      File_="Client.exe" 
      Info="Что может быть во втором потоке exe-файла?" 
    >
  #)
    #(
    <$Row @Validate="" 
      File_="Server.exe" 
      Info="Например, номер лицензии, для организации контроля над распространением продукта"
    >
  #)
<$/Table>

Комментариев в этом примере требует только параметр TYPE тега <$Column >. Его значение состоит из буквы, обозначающей тип поля, и числа, определяющего длину. В случае заглавной буквы поле может принимать значение «NULL», в противном случае – нет. Значение длины ограничено диапазоном 0-254. Если оно равно нулю, то длина поля может быть переменной. Доступные типы:

Описание операций, выполняемых во время инсталляции

В процессе инсталляции службой MS Installer выполняется некоторая последовательность операций. Это могут быть как встроенные операции – стандартные именованные процедуры, которые выполняет MS Installer, так и операции пользователя, определенные при создании инсталляции. Перечень выполняемых операций хранится в специальных таблицах. То, какие из этих таблиц будут использованы службой установки, зависит от значения свойства ACTION (INSTALL, ADMIN или ADVERTISE) и уровня пользовательского интерфейса. Эта зависимость представлена в таблице 5.

Название таблицыУсловия использования
Тип инсталляции
(значение свойства ACTION)
Уровень пользовательского интерфейса (значение свойства UILevel)
AdminExecuteSequenceADMINНе важен
AdminUISequenceADMINПолный или сокращенный (5 или 4)
AdvtUISequenceТаблица не используется. Должна либо отсутствовать в базе, либо не содержать записей.
AdvtExecuteSequenceADVERTISEНе важен
InstallExecuteSequenceINSTALLНе важен
InstallUISequnceINSTALLПолный или сокращенный (5 или 4)
Таблица 5.

Все перечисленные таблицы имеют одинаковую структуру и состоят из трех полей – наименование операции (Action), условия выполнения (Condition) и порядковый номер (Ordering).

Содержание поля «Condition» аналогично рассмотренным выше условиям установки компонента.

Поле «Ordering» предназначено для определения порядка выполнения перечисленных операций.

Поле «Action» может содержать имена как стандартных операций, так и определенных пользователем. Тема стандартных операций слишком объемна для журнальной статьи, поэтому здесь я приведу лишь ссылку на раздел MSDN, посвященный этому вопросу. Рассмотрим подробнее возможности, предоставляемые пакетом MakeMsi для формирования пользовательских операций.

Пользовательские операции (Custom action) предназначены для произвольного расширения функциональности msi-пакета. Для их описания предназначена таблица CustomAction, содержащая название операции (поле Action), ее тип (поле Type) и еще два поля (Source и Target), интерпретация значения которых зависит от значения поля Type.

Перечень возможных типов пользовательских операций и соответствующих значений полей таблицы CustomAction приведен в таблице 6.

Тип пользовательской операцииЗначение поля «Type»Значение поля «Source»Значение поля «Target»
Вызов функции динамической библиотеки, содержащейся в бинарном потоке1Значение ключевого поля в таблице «Binary»Точка входа в DLL
Вызов функции динамической библиотеки, установленной в составе продукта17Значение ключевого поля в таблице «File»Точка входа в DLL
Выполнение EXE-файла, содержащегося в бинарном потоке2Значение ключевого поля в таблице «Binary»Аргументы командной строки
Выполнение EXE-файла, установленного в составе продукта18Значение ключевого поля в таблице «File»Аргументы командной строки
Отображение сообщения об ошибке и прекращение инсталляции 19ПустоФорматированное сообщение или значение ключевого поля в таблице «Error».
Выполнение EXE-файла, путь которому указан в значении свойства50Значение ключевого поля в таблице «Property» Аргументы командной строки
Выполнение EXE-файла, расположенного в указанном каталоге34Значение ключевого поля в таблице «Directory»Имя исполняемого файла. Дополнительно могут быть указаны аргументы командной строки. Длинные имена файлов должны быть заключены в кавычки.
Выполнение Java-скрипта, содержащегося в бинарном потоке5Значение ключевого поля в таблице «Binary»Необязательное указание вызываемой функции
Выполнение Java-скрипта из файла, установленного в составе продукта21Значение ключевого поля в таблице «File»Необязательное указание вызываемой функции
Выполнение Java-скрипта, текст которого содержится в свойстве53Значение ключевого поля в таблице «Property»Необязательное указание вызываемой функции
Выполнение Java-скрипта37NULLКод Jscript
Выполнение VB-скрипта, содержащегося в бинарном потоке6Значение ключевого поля в таблице «Binary»Необязательное указание вызываемой функции
Выполнение VB-скрипта из файла, установленного в составе продукта22Значение ключевого поля в таблице «File»Необязательное указание вызываемой функции
Выполнение VB-скрипта, текст которого содержится в свойстве54Значение ключевого поля в таблице «Property»Необязательное указание вызываемой функции
Выполнение VB-скрипта38NULLКод VBScript
Установка значения свойства51Имя свойства или значение ключевого поля в таблице «Property»Форматированная строка
Сопоставление каталога из таблицы «Directory» с каталогом файловой системы 35Значение ключевого поля в таблице «Directory»Форматированная строка, представляющая абсолютное имя каталога файловой системы завершающим с обратным слешем
Установка вложенного msi-пакета7Название хранилища, в котором находится устанавливаемый пакетЗначение свойств устанавливаемого пакета
Установка msi-пакета, расположенного на том же носителе, что и запускающая инсталляция23Путь к msi-пакетуЗначение свойств устанавливаемого пакета
Запуск вложенной инсталляции для изменения состава или удаления установленного продукта 39Код продукта (внутренний номер продукта в базе MSI Installer – GUID инсталляционного пакета)Значение свойств устанавливаемого пакета
Таблица 6.

Теги MakeMsi, предназначенные для создания пользовательских операций, аналогичны приведенным выше типам. Большинство из них имеют сходные параметры:

Type может принимать следующие значения:

Теги создания пользовательских операций

<$DllCa> – предназначен для вызова функций динамической библиотеки. Функция должна иметь вполне определенный вид (по адресу http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcsample98/html/vcsmpcustact1.asp расположен пример реализации на C++). Пример использования:

;--- Включаем библиотеку в инсталляцию ---
#(
<$File RowKey="DllAsInstalledFile" SOURCE="C:\tst\Sample.dll" 
  Destination="[INSTALLDIR]\TestDll.DLL"
  >
#)
;--- Вызываем функцию TstFunc  ---------
#(
<$DllCa File="DllAsInstalledFile" SEQ=5001 Entry="TstFunc" 
  Condition="<$DLLCA_CONDITION_INSTALL_ONLY>"
  >
#)

;--- Или используем бинарный поток ------
#(
<$DllCa Binary="DllInBinaryTable" SEQ=2001 Entry="TstFunc" 
  Condition="<$DLLCA_CONDITION_INSTALL_ONLY>" DLL=^C:\tst\Sample.dll^
  >
#)

<$ExeCa> – предназначен для запуска исполняемого файла. Также может применяться к файлам из состава инсталляции и хранящимся в бинарных потоках:

<$Files "c:\tst\tst.exe">
<$/Component>

;--- Запускаем установленный файл -----------------------------------------
#(
   <$ExeCa
     EXE="[INSTALLDIR]tst.exe"
    Args=^[тут аргументы командной строки]^
  CONDITION=^<$VBSCA_CONDITION_INSTALL_ONLY>^
    Rc0="N"                        ;;Не проверяем код возврата
    Seq=4011
   >
#)
;--- Или запускаем его из бинарного потока --------------------------------
#(
   <$ExeCa
  Binary="tst.exe"
  EXE="c:\tst\tst.exe"
    CONDITION=^<$VBSCA_CONDITION_INSTALL_ONLY>^
    Seq=4011
   >
#)

<$JsCa> и <$VbsCa> предназначены для запуска Java- или VB-скриптов, помещенных в бинарный поток. Имеют дополнительный параметр DATA, указывающий на структуру, содержащую именованные переменные. В качестве иллюстрации использования этих тегов вернемся к примеру с использованием дополнительных потоков файлов. Применение этих тегов практически идентично, так что ограничимся одним примером. Реализуем на VBScript функцию, которая выполнит запись данных из таблицы FileStream в соответствующие файлы:

#data "Parameters"
      "DATABASE" "[DATABASE]"    ;;Передаем имя устанавливаемого пакета
      "DestDir"  "[INSTALLDIR]"  ;;и каталог установки
#data

<$VbsCa Binary="WriteSecondStream.vbs" DATA="Parameters">
  <$VbsCaEntry "Install">

  const msiOpenDatabaseModeReadOnly = 0

  dim streamView, fileView
  dim rec, rec1
  dim stmt
  dim fl, fileName
  dim fs : set fs = CreateObject("Scripting.FileSystemObject")
  dim DB  : DB = VbsCaCadGet("DATABASE")
  dim oDB : set oDB = session.installer.OpenDatabase(DB, _
    msiOpenDatabaseModeReadOnly)
         if  err.number <> 0 then
          on error goto 0
    VbsCaRaiseError "WriteSecondStream()", "Ошибка открытия базы" & DB 
  end if
  CaDebug 1, DB & " успешно открыта"
  set streamView = oDB.OpenView ("SELECT `File_`, `Info` FROM `FileStream`")
  if streamView is Nothing then 
                on error goto 0
    VbsCaRaiseError "WriteSecondStream()", "Ошибка открытия streamView"
  end if
  streamView.Execute
  CaDebug 1, "Открыт запрос потоков"
  set rec = streamView.Fetch
  while not (rec is Nothing)
    CaDebug 1, "Обрабатывается файл " & rec.StringData(1)
    stmt = "SELECT FileName FROM File WHERE File='" & _
      rec.StringData(1) & "'"
    CaDebug 1, stmt
    set fileView = oDB.OpenView (stmt)
                if fileView is Nothing then 
                  on error goto 0
      VbsCaRaiseError "WriteSecondStream()", "Ошибка открытия fileView" 
    end if
    fileView.Execute
    CaDebug 1, "Открыт запрос списка устанавливаемых файлов"
    set rec1 = fileView.Fetch
    if not (rec1 is Nothing) then
      fileName = VbsCaCadGet("DestDir") & rec1.StringData(1) & _
      ":SecStream"
      CaDebug 1, "Открытие файла " & fileName
      set fl = fs.OpenTextFile(fileName, 2, -1)   
      CaDebug 1, "Запись данных " & rec.StringData(2)
      fl.Write rec.StringData(2)
      fl.Close
    end if
    fileView.Close
    set rec = streamView.Fetch
  wend
  streamView.Close
  set fs = Nothing
  set oDB = Nothing  
  <$/VbsCaEntry>
<$/VbsCa>
#(
<$VbsCaSetup 
  Binary="WriteSecondStream.vbs" 
  Entry="Install" 
  Deferred="Y" 
  Seq=4422 
  CONDITION=^<$CONDITION_INSTALL_ONLY>^
  DATA="Parameters"
>
#)

Глобальный объект «session» позволяет получить доступ к объектной модели службы MS Installer в контексте выполняемой сессии. Но при его использовании нужно иметь в виду, что к моменту выполнения отложенных операций (а в нашем примере именно так и есть – Deferred = "Y") значения свойств сессии становятся равны “NULL”. И если при Deferred = N мы можем получить каталог, в который производится установка, с помощью конструкции «session.property("INSTALLDIR")», то при выполнении отложенной операции придется передавать его через параметр «DATA», как показано в примере.

Тег <$VbsCa> используется совместно с тегом <$VbsCaEntry>, содержащим название функции, вызываемой при выполнении пользовательской операции, и <$VbsCaSetup>, предназначенным для задания условий выполнения и атрибутов операции.

При генерации скрипта MakeMsi создает некоторые вспомогательные функции, две из которых уже были использованы в примере. Это VbsCaRaiseError, генерирующая ошибку и прекращающая выполнение скрипта, и CaDebug, которая выводит содержимое своего второго параметра в качестве описания операции над индикатором выполнения (над первым или вторым, что определяется первым параметром) в диалоге ProgressDlg, и осуществляет запись в автоматически создаваемый (если не указано обратное) Log-файл. По умолчанию файлы протоколов пишутся в корень диска C, что бывает не всегда удобно. Для исправления этой ситуации служит макроопределение VBSCA_LOGDIR_ENVVAR, которому можно присвоить значение переменной окружения, содержащей имя каталога. Существует и другой способ – макроопределение VBSCA_LOGDIR_EXP_4_DEFAULT, в котором можно напрямую определить каталог для файлов протокола. Для отключения протоколирования выполнения скриптов можно воспользоваться макроопределением VBSCA_LOG_TO_FILE, установив его значение «N».

Еще одной вспомогательной функцией является CaMsgBox. Разработчик настоятельно рекомендует пользоваться ей вместо стандартной MsgBox, так как она проверяет уровень пользовательского интерфейса и не создает модального окна в том случае, когда это не требуется.

On-line ресурсы

По этому адресу расположен сайт разработчика, Дениса Бариса, с которого можно скачать дистрибутив MakeMsi. На момент написания статьи последняя версия (04.267) находилась здесь Программа предоставляется бесплатно всем желающим с привычной маркировкой "as is". В состав дистрибутива входит достаточно полная документация в формате .chm и большое количество примеров. Кроме того, Денис Барис ведет новостную группу на Yahoo, где очень оперативно и доброжелательно отвечает на все задаваемые вопросы.

Помимо ресурсов, посвященных MakeMsi, определенный интерес представляют новостные группы Microsoft, посвященные вопросам службы инсталляции – microsoft.public.platformsdk.msi и microsoft.public.win2000.msi, очень информативный ресурс installsite.org и достаточно активный форум на myitforum.com.

Заключение

Программа, написанная Денисом, позволяет создавать как простые, типовые инсталляции, так и сложные установочные пакеты. Во втором случае для использования всех предоставляемых программой возможностей требуются знания в области технологии MS Installer и скриптовых языков (VBScript или JavaScript). Автором проделан очень большой объем работы, который позволяет существенно ускорить процесс создания msi-пакетов, автоматизировав его основные операции. Помимо рассмотренного в статье примера создания нового инсталлятора, имеется возможность изменения существующей, сравнения двух пакетов с автоматической генерацией скрипта изменений (утилита DiffMsi), работы с merge-модулями и многое другое.

Проект имеет некоммерческий характер. Автор будет рад принять помощь в форме отчетов об ошибках, предложений по расширению функциональности и участия в форуме MakeMsi.


Эта статья опубликована в журнале RSDN Magazine #5-2004. Информацию о журнале можно найти здесь
    Сообщений 2    Оценка 155        Оценить