Введение



Я не являюсь профессиональным программистом. В том смысле, что не зарабатываю денег этим ремеслом, а использую свои навыки в качестве инструмента для основной, научной, деятельности. Поэтому все мои «поделки» живут лишь отведенный им на решение конкретной задачи период и не выходят за пределы каталогов проекта. Кроме того, уже довольно давно я отошел от разработки под ОС Windows, ибо Linux для решения моих задач более удобен.

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

Недавно мне подкинули не слишком сложный проект — одна фирма хочет написать аналог программы, имеющейся у другой. Немного реверсинга, немного кодинга, в целом проект вполне обыденный. Однако тут же возник вопрос о создании инсталлятора — клиент ведь желает продукт «под ключ», чтобы клацнуть на «сетап», понажимать «Далее» и получить готовую к работе программу.

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

  1. InstallShield — классика жанра, достаточно солидный проприетарный продукт
  2. Adnvanced Installer — проприетарный инструмент с широкими возможностями кастомизации через GUI. На сайте сказано, что если Вы блоггер и будете писать об этом продукте много хороших слов, то у Вас есть возможность получить Free License
  3. WiX — открытый бесплатный продукт, основанный на XML-скриптах. Мощная, хорошо документированная штука. Разбираться с ним я пока не стал, ибо время дорого (да и к XML душа лежит не очень). Возможно когда нибудь я к нему вернусь. Да, к нему есть плагины для Visual Studio, что несомненный плюс.
  4. Inno Setup — опенсорсный проект, код которого доступен на гитхабе. В силу бесплатности и низкого порога вхождения мой выбор остановился именно на нем, как инструменте позволившем выполнить работу быстро и качественно.


Так что в статье мы будем рассматривать пример использования Inno Setup, для которого имеется полезный фронтэнд Inno Script Studio, позволяющий выполнять создание простых инсталляторов с помощью мастера и менять настройки через GUI. GUI понадобился мне для первого знакомства, с продуктом, но мы не будем уделять ему большого внимания — мой «линукс головного мозга» в последнее время всё больше и больше уводит меня от желания использовать разного рода «мастера» (это субъективно, прошу не пинать). Мы рассмотрим хардкорный способ написания скрипта с чистого листа.



1. Установка, настройка и простой (но довольно солидный) скрипт



Думаю, что скачать программу с официального сайта и установить её труда не составит. Запускаем Inno Setup Compiler и видим такое окно

Пугающе уныло встречает нас Inno Setup...


Что это? По сути это просто-напросто редактор для набора скриптов, снабженный подсветкой синтаксиса и кнопками компиляции и запуска. От нас ждут, что мы начнем набирать в этом окне текст скрипта, определяющий логику работы будущего инсталлятора. Ну так и не будем терять время.

Прежде всего определим необходимые константы
;------------------------------------------------------------------------------
;
;       Пример установочного скрипта для Inno Setup 5.5.5
;       (c) maisvendoo, 15.04.2015
;
;------------------------------------------------------------------------------

;------------------------------------------------------------------------------
;   Определяем некоторые константы
;------------------------------------------------------------------------------

; Имя приложения
#define   Name       "Miramishi Painter"
; Версия приложения
#define   Version    "0.0.1"
; Фирма-разработчик
#define   Publisher  "Miramishi"
; Сафт фирмы разработчика
#define   URL        "http://www.miramishi.com"
; Имя исполняемого модуля
#define   ExeName    "Miramishi.exe"


Эти строки будут часто встречаться в коде скрипта, поэтому определяем их, как и в C, с помощью дерективы #define

Тело скрипта разделяется на секции, каждая из которых несет свое функциональное назначение. Обязательная секция [Setup] задает глобальные параметры работы инсталлятора и деинсталатора.

;------------------------------------------------------------------------------
;   Параметры установки
;------------------------------------------------------------------------------
[Setup]

; Уникальный идентификатор приложения, 
;сгенерированный через Tools -> Generate GUID
AppId={{F3E2EDB6-78E8-4539-9C8B-A78F059D8647}

; Прочая информация, отображаемая при установке
AppName={#Name}
AppVersion={#Version}
AppPublisher={#Publisher}
AppPublisherURL={#URL}
AppSupportURL={#URL}
AppUpdatesURL={#URL}

; Путь установки по-умолчанию
DefaultDirName={pf}\{#Name}
; Имя группы в меню "Пуск"
DefaultGroupName={#Name}

; Каталог, куда будет записан собранный setup и имя исполняемого файла
OutputDir=E:\work\test-setup
OutputBaseFileName=test-setup

; Файл иконки
SetupIconFile=E:\work\Mirami\Mirami\icon.ico

; Параметры сжатия
Compression=lzma
SolidCompression=yes


Пристальное внимание уделаем опции AddId — уникальный идентификатор приложения (GUID), используемый для регистрации приложения в реестре Windows. Его пишем не «от фонаря», а генерируем, открывая фигурную скобку, и выбрав в меню пункт Tools -> Generate GUID (или используя хот-кей Shift + Ctrl + G). Далее указываем имя приложения, под которым оно будет установлено в системе, его версию, данные фирмы разработчика, адреса сайтов разработчика, технической поддержки и обновления.

Путь, по умолчанию предлагаемый инсталлятором для установки определяем опцией DefaultDirName. При этом переменная {pf} — это путь в каталог Program Files соответствующей разрядности. Опция DefaultGroupName определяет имя группы программы в меню «Пуск». Обратите внимание на то, что для указания имени приложения мы используем данное нами выше макроопределение Name, обрамляя его фигурными скобками и решеткой.

Пара опций OutputDir и OutputBaseFileName задают каталог, куда будет записан скомпилированный «сетап» и его имя (без расширения). Кроме этого, указываем где взять иконку для test-setup.exe опцией SetupIconFile.

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

В хорошем исталяторе должна быть поддержка нескольких языков. Включаем её в наш «сетап», используя опциональную секцию [Languages]. При отсутствии данной секции будет использоваться английский язык.

;------------------------------------------------------------------------------
;   Устанавливаем языки для процесса установки
;------------------------------------------------------------------------------
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"; LicenseFile: "License_ENG.txt"
Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl"; LicenseFile: "License_RUS.txt"


Каждая строка в данной секции задает один из используемых при установке языков. Синтаксис строки таков

<имя параметра>: <значение параметра>


в качестве разделителя параметров используется точка с запятой. Параметр Name говорит сам за себя — «имя» языка, допускаются общепринятые двухбуквенные сокращения («en», «ru», «de» и так далее). Параметр MessagesFile сообщает компилятору в каком месте взять шаблон сообщений, выводимых при инсталляции. Эти шаблоны берем в каталоге компилятора Inno Setup, о чем мы сообщаем директивой compiler. Для английского языка годится шаблон Default.isl, для русского — Languages\Russian.isl

Параметр LicenseFile задает путь к файлу с текстом лицензии на соответствующем языке.

Обычно установщик предлагает нам, например, определится, хотим мы или не хотим создать ярлык на рабочем столе. Такие опции установки определяются необязательной секцией [Tasks]

;------------------------------------------------------------------------------
;   Опционально - некоторые задачи, которые надо выполнить при установке
;------------------------------------------------------------------------------
[Tasks]
; Создание иконки на рабочем столе
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked


Здесь Name задает имя операции — «desktopicom» — создание иконки на рабочем столе; Description — описание чекбокса с опцией, которое увидит пользователь. Конструкция

{cm:<имя сообщения>}


задает стандартный текст сообщения, соответствующий выбранному в начале инсталляции языку. Параметр GroupDescription — заголовок группы чекбоксов с опциями. Параметр Flags задает определенные действия и состояния элементов управления, в данном случае указывая, что галочка «создать ярлык на рабочем столе» должна быть снята.

Чтобы было понятно - так выглядит результат



Теперь укажем, какие файлы надо включить в дистрибутив и где их надо поместить при установке. Для этого используется обязательная секция [Files]

;------------------------------------------------------------------------------
;   Файлы, которые надо включить в пакет установщика
;------------------------------------------------------------------------------
[Files]

; Исполняемый файл
Source: "E:\work\Mirami\Mirami\bin\Release\Miramishi.exe"; DestDir: "{app}"; Flags: ignoreversion

; Прилагающиеся ресурсы
Source: "E:\work\Mirami\Mirami\bin\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs


Здесь

  • Source — путь к файлу-источнику. У меня всё необходимое программе для работы лежит в каталоге Release проекта MS VS
  • DestDir — каталог установки, переменная {app} содержит путь, выбранный пользователем в окне установщика
  • Flags — разнообразные флаги. В нашем примере для исполняемого файла: игнорирование версии программы при перезаписи исполняемого модуля, если он уже существует в системе (ignorevarsion); для остальных файлов и каталогов так же игнорируем версию, рекурсивно включаем все подкаталоги и файлы источника (recursesubdirs) и создаем подкаталоги, если их нет (createallsubdirs)


Наконец, чтобы всё было красиво, опционально укажем компилятору, где брать иконки для размещения в меню программ и на рабочем столе

;------------------------------------------------------------------------------
;   Указываем установщику, где он должен взять иконки
;------------------------------------------------------------------------------ 
[Icons]

Name: "{group}\{#Name}"; Filename: "{app}\{#ExeName}"

Name: "{commondesktop}\{#Name}"; Filename: "{app}\{#ExeName}"; Tasks: desktopicon


Тут я указываю, что для группы в меню «Пуск» и для рабочего стола иконку надо брать из исполняемого модуля. Естественно, что иконка должна быть в него «вкомпилена», иначе в требуемых местах мы увидим стандартный значок из коллекции винды.

Итак, всё вроде готово. Жмем Ctrl + F9 и пытаемся собрать инсталлятор. Если не допущены синтаксические ошибки, начнется процесс сборки

Inno Setup собирает инсталлятор


После успешной сборки инсталлятор можно запустить, нажав F9. Если Вы работаете под учеткой с ограниченными правами (а я работаю в винде именно так), то придется полезть в каталог с результатами компиляции, который мы указали в скрипте, и запустить инсталлятор с правами админа

Запуск инсталлятора под ограниченной учетной записью


В итоге мы увидим до боли знакомое каждому пользователю Windows окно выбора языка

приветствие мастера

лицензионное соглашение


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

Ну что сказать? Ура! Мы написали свой первый «сетап» и могли бы радоваться, но

2. Развертывание .NET Framework



Вы не заметили, что мы о чем-то забыли? Приложение, созданное на C# не будет работать без фреймворка, с которым оно было собрано, если таковой отсутствует в системе. Соответствующий фреймворк надо установить, а для этого необходимо

  1. Определиться с тем, как будем получать дистрибутив фреймворка. Тут есть два варианта — поставлять его вместе с дистрибутивом программы или, при необходимости установки загрузить его онлайн на целевую машину. Мы остановимся на варианте включения фреймворка в дистрибутив. Сам фреймфорк, скажем версии 4.0, можно взять бесплатно у Майкрософта
  2. Детектировать наличие/отсутствие данного фреймфорка в целевой системе. Для этого потребуется написать логику, анализирующую состояние системного реестра.


Значения в реестре, которые необходимо проверить приведены в официальной документации Microsoft, в статье я приведу краткую выжимку из неё

Таблица 1. Ключи реестра, для проверки установленной версии .NET Framework
Версия .NET Ключ реестра Значение
3.0 HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\InstallSuccess 1
3.5 HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Install 1
4.0 Client Profile HKLM\Software\Microsoft\NET Framework Setup\NDP\v4.0\Client\Install 1
4.0 Full Profile HKLM\Software\Microsoft\NET Framework Setup\NDP\v4.0\Full\Install 1
4.5 HKLM\Software\Microsoft\NET Framework Setup\NDP\v4.0\Full\Release номер релиза


Для реализации произвольной логики работы инсталлятора в Inno Setup предусмотрена секция [Code]. В пределах этой секции размещается код реализующих логику функций на языке Pascal. Содежимое этой секции мы вынесем в отдельный файл dotnet.pas и включим в основной скрипт дерективой #include

;------------------------------------------------------------------------------
;   Секция кода включенная из отдельного файла
;------------------------------------------------------------------------------
[Code]
#include "dotnet.pas"


хотя можно набить код и непосредственно в секции [Code]. Надо помнить, что внутри этой секции используется синтаксис Pascal, и комментарии предваряются последовательностью "//" вместо используемой в основной части скрипта точки с запятой.

Напишем функцию, определяющую наличие в системе нужной версии .NET

//-----------------------------------------------------------------------------
//  Проверка наличия нужного фреймворка
//-----------------------------------------------------------------------------
function IsDotNetDetected(version: string; release: cardinal): boolean;

var 
    reg_key: string; // Просматриваемый подраздел системного реестра
    success: boolean; // Флаг наличия запрашиваемой версии .NET
    release45: cardinal; // Номер релиза для версии 4.5.x
    key_value: cardinal; // Прочитанное из реестра значение ключа
    sub_key: string;

begin

    success := false;
    reg_key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\';
    
    // Вресия 3.0
    if Pos('v3.0', version) = 1 then
      begin
          sub_key := 'v3.0';
          reg_key := reg_key + sub_key;
          success := RegQueryDWordValue(HKLM, reg_key, 'InstallSuccess', key_value);
          success := success and (key_value = 1);
      end;

    // Вресия 3.5
    if Pos('v3.5', version) = 1 then
      begin
          sub_key := 'v3.5';
          reg_key := reg_key + sub_key;
          success := RegQueryDWordValue(HKLM, reg_key, 'Install', key_value);
          success := success and (key_value = 1);
      end;

     // Вресия 4.0 клиентский профиль
     if Pos('v4.0 Client Profile', version) = 1 then
      begin
          sub_key := 'v4\Client';
          reg_key := reg_key + sub_key;
          success := RegQueryDWordValue(HKLM, reg_key, 'Install', key_value);
          success := success and (key_value = 1);
      end;

     // Вресия 4.0 расширенный профиль
     if Pos('v4.0 Full Profile', version) = 1 then
      begin
          sub_key := 'v4\Full';
          reg_key := reg_key + sub_key;
          success := RegQueryDWordValue(HKLM, reg_key, 'Install', key_value);
          success := success and (key_value = 1);
      end;

     // Вресия 4.5
     if Pos('v4.5', version) = 1 then
      begin
          sub_key := 'v4\Full';
          reg_key := reg_key + sub_key;
          success := RegQueryDWordValue(HKLM, reg_key, 'Release', release45);
          success := success and (release45 >= release);
      end;
        
    result := success;

end;


Не смотря на обилие кода, логика его работы достаточно проста — в зависимости от значения параметра version с помощью функции RegQueryDWordValue(...) читается значение соответствующего ключа реестра и сравнивается с требуемым значением (смотрим таблицу 1). Для версии 4.5 дополнительно передаем номер релиза в параметре release.

Для того приложения, которое мы будем развертывать, нужна вполне определенная версия .NET, поэтому напишем функцию-обертку для её определения в целевой системе

//-----------------------------------------------------------------------------
//  Функция-обертка для детектирования конкретной нужной нам версии
//-----------------------------------------------------------------------------
function IsRequiredDotNetDetected(): boolean;
begin
    result := IsDotNetDetected('v4.0 Full Profile', 0);
end;


Для того, чтобы перед началом установки проверить наличие фреймворка и сообщить пользователю о предпринимаемых действиях используем Callback-функцию InitializeSetup()

//-----------------------------------------------------------------------------
//    Callback-функция, вызываемая при инициализации установки
//-----------------------------------------------------------------------------
function InitializeSetup(): boolean;
begin

  // Если нет тербуемой версии .NET выводим сообщение о том, что инсталлятор
  // попытается установить её на данный компьютер
  if not IsDotNetDetected('v4.0 Full Profile', 0) then
    begin
      MsgBox('{#Name} requires Microsoft .NET Framework 4.0 Full Profile.'#13#13
             'The installer will attempt to install it', mbInformation, MB_OK);
    end;   

  result := true;
end;


Теперь в секцию [Files] добавим запись о том, где компилятор должен взять дистрибутив .NET, куда инсталлятор он должен её распаковать, и при каких условиях следует выполнять распаковку

; .NET Framework 4.0
Source: "E:\install\dotNetFx40_Full_x86_x64.exe"; DestDir: "{tmp}"; Flags: deleteafterinstall; Check: not IsRequiredDotNetDetected


Во флагах скажем, что надо удалить дистрибутив .NET после установки (deleteafterinstall). Условие, при котором требуется распаковка задаем опцией Check, где вызываем функцию IsRequiredDotNetDetected(), выполняя распаковку ели она вернет false.

Сам запуск инсталляции фрейворка можно выполнить после установки основной программы, поэтому включаем в скрипт секцию [Run], в которой указывается, что необходимо запускать по окончании установки

;------------------------------------------------------------------------------
;   Секция кода включенная из отдельного файла
;------------------------------------------------------------------------------
[Code]
#include "dotnet.pas"

[Run]
;------------------------------------------------------------------------------
;   Секция запуска после инсталляции
;------------------------------------------------------------------------------
Filename: {tmp}\dotNetFx40_Full_x86_x64.exe; Parameters: "/q:a /c:""install /l /q"""; Check: not IsRequiredDotNetDetected; StatusMsg: Microsoft Framework 4.0 is installed. Please wait...


Обратите внимание на то, что мы сначала указываем имя секции [Run], чтобы закрыть секцию [Code], а затем пишем комментарий начинающийся с точки с запятой. Это необходимо из-за различия синтаксиса основного скрипта и секции [Code], в противном случае при компиляции мы получим синтаксическую ошибку.

В секции задается путь к инсталлятору фреймворка — предварительно он распакован нами во временный каталог (переменная {tmp} содержит путь к веременному каталогу); задаются параметры командной строки. Опция Check определяет условие запуска инсталляции — это отсутствие в целевой системе нужного нам фреймворка. Опция StatusMsg определяет сообщение, которое увидит пользователь в окне инсталлера.

Снова компилируем наш проект. Теперь, при запуске на «чистой» винде инсталлятор выдаст сообщение

Нам сообщают что требуется .NET Framework 4.0 и для нас его установят


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



а затем процесс переключается на установку .NET

Майкрософт просит нас принять лицензию...


Установка .NET


После этого мы получаем работоспособное C# приложение установленное «по взрослому»

Заключение



Я не профессионал и во многих вещах могу ошибаться. Прошу отнестись к этому с пониманием. Статья писалась нубом для нубов, её основная цель — задать вектор поиска при решении задачи написания инсталлятора. За остальными вопросами можно обратится к документации, поставляемой вместе с Inno Setup.

Код данного примера доступен в моем репозитории на Github. «Кракозябры» в комментах вызваны несовпадением кирилических кодировок. Для себя всегда пишу английские комментарии, но для лучшего понимания кода допустил этот ляп. При скачивании в винде всё просматривается замечательно, так что прошу простить мне и эту несуразность.

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

Комментарии (14)


  1. Dima_Sharihin
    16.04.2015 13:57
    +3

    Почему не рассматривали вариант ClickOnce?


    1. maisvendoo Автор
      16.04.2015 15:52

      Позиции рассматривались до достижения приемлемого результата за разумное время. До ClickOnce дело не дошло


      1. agranom555
        16.04.2015 17:27

        Посмотрите. С ClockOnce почти не нужно ничего делать. В студии задали нужные все параметры и все. Хотя тут гибче получается. Но там, к примеру, net инсталятор приложить — 1 галочку. Притом он сперва проверит наличие его в системе у пользователя. Да и установщик он берет из винды и не надо качать с сайта МС


  1. leorush
    16.04.2015 14:07
    +4

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


    1. wertex
      16.04.2015 14:20
      +1

      В свое время также использовал Inno Setup, но потом перешел на NSIS. Это действительно очень достойный инсталятор с большой гибкостью настроек.


    1. KReal
      16.04.2015 14:48

      Спасибо за наводку!


  1. KReal
    16.04.2015 14:45
    +1

    WiX, конечно, монстр ещё тот, но на нём было бы не сильно сложнее, если не сказать проще. Особенно в части проверки .Net framework.

    И тут на хабре проскакивал WiX# — habrahabr.ru/post/253819

    А вот то, что вы сделали руками в скрипте — в GUI Inno Setup сделать можно?


    1. maisvendoo Автор
      16.04.2015 15:55

      Можно, но сделать установку .NET через GUI с наскока не вышло.


  1. driessen
    16.04.2015 18:34
    +2

    Отличный инсталлятор, на работе парочку прог завернул с помощью него. Немного советов от меня:

    Требование запуска под админом можно прописать в секции [Setup] с помощью параметра PrivilegesRequired.

    В [Tasks], как все уже догадались, добавляются задания. Сами по себе они ничего не делают, только создают галки. Но если добавить затем задание в какую-либо строчку в другой секции, то эта строчка выполнится только при нажатой галке. Например:

    [Tasks]
    ; Создание ярлыка не для основной программы, а для настройки
    Name: "setupshortcut"; Description: "Создать значок для настройки Программы"; GroupDescription: "{cm:AdditionalIcons}"
    ; Установка Firebird
    Name: "installingfirebird"; Description: "Установить Firebird 1.5"; GroupDescription: "База данных и драйверы:"
    
    [Icons]
    ; Данный ярлык создастся только в том случае, если будет нажата галка из задания "setupshortcut" выше
    Name: "{commondesktop}\Настройка Программы"; Filename: "{app}\setup.bat"; Tasks: setupshortcut
    
    [Run]
    ; Firebird установится только в том случае, если была нажата галка из задания "installingfirebird" выше
    Filename: "{src}\dist\Firebird\Firebird-1.5.6.5026-0-Win32.exe"; Description: "Firebird 1.5"; StatusMsg: "Установка Firebird 1.5..."; Tasks: installingfirebird
    


    Флаг uninsneveruninstall в секции [Files] используется для предотвращения удаления файла после деинсталляции программы. Иногда очень важно сохранить, например, базу данных. Полезно использовать совместно с onlyifdoesntexist. В общем, защита от «случайной» переустановки программы.

    Вообще, у Inno Setup хорошая документация. Перед тем как писать что-то на паскале, гляньте в Support Functions Reference, может нужная вам функция уже есть (там, в том числе, есть функции для работы с реестром и с INI-файлами).


  1. kets
    16.04.2015 22:36
    +1

    В дополнение к Inno Setup я бы хотел порекомендовать Inno Script Studio. Это надстройка над компилятором InnoSetup, которая позволяет автоматизировать процесс написания скрипта установщика.

    Вот примеры:

    Скриншоты продукта



  1. viklequick
    16.04.2015 23:06

    На любом языке программирования можно писать как на Фортране, и InnoSetup не исключение.

    Приведу пример из реального проекта.

    Парочка переменных

    #define CR '~~'
    #define i
    #define listToProceed		''
    


    Парочка функций с миллионом параметров, бомбящих все подряд в строку
    #define mkPInDdCode(src, dst, arch, indd, typo, prod, place, archmark1, archmark2, pf) 	'Source: ..\..\image\bin\' + arch + '\' + indd + '\' + src + '.pln; DestDir: {code:Get' + 		typo + 'Path_' + archmark1 + '|{' + pf + '}\Adobe\' + prod + '\}\Plug-ins' + place + '; ' + 		'DestName: ' + dst + '.pln; Flags: ignoreversion overwritereadonly replacesameversion ; ' + 		'Check: Checked' + typo + '_' + archmark2 + CR
    
    #define mkInDdCodePair(indd, typo, prod) 	 mkPInDdCode('FooBin', 'Foo', 'x64', indd, typo, prod, '', '64', '64', 'pf') +   	mkPInDdCode('FooBin', 'Foo', 'x86', indd, typo, prod, '', '32', '32in64', 'pf32') +  	mkPInDdCode('FooBin', 'Foo', 'x86', indd, typo, prod, '', '32', '32', 'pf32')
    


    Вот так используем
    #define listToProceed		mkInDdCodePair( 'cc2014', 'S71',  'Adobe InDesign Server CC2014') + 	 mkInDdCodePair( 'cc2014', 'CC2',  'Adobe InDesign CC2014') +  	mkInDdCodePair( 'cc2014', 'Icc2', 'Adobe InCopy CC2014')
    


    А теперь — сеанс магии: раскатываем эту строчку так чтобы она превратилась в набор инструкций InnoSetup:
    #sub mkPairEmit
    	#emit Copy(listToProceed, 1, i - 1)
    	#pragma message "<" + Copy(listToProceed, 1, i - 1) + ">"
    	#expr Delete(listToProceed, 1, i + Len(CR) - 1)
    #endsub
    
    #sub mkPairSubFiles
    	#for { i = Pos(CR, listToProceed); i > 0; i = Pos(CR, listToProceed) } mkPairEmit
    #endsub      
    #expr mkPairSubFiles
    


    Вуаля. 200 килобайт кода описывающие по много раз одно и то же в разных вариациях — превратились в несколько строк. А блоатварь уже не пишется руками, а генерится на лету. Профит!


  1. vlivyur
    17.04.2015 10:02
    +1

    Про WiX эт вы зря. У меня тоже не лежит душа к xml, но за пару дней разобрался как зарегистрировать сертификат, установить новую службу, добавить левые файлики, да ещё и с обновлениями. WixEdit очень сильно упрощает написание, кишки MSI очень сильно усложняют.


    1. maisvendoo Автор
      17.04.2015 10:54

      Постараюсь кратко ответить на все комментарии.

      Во-первых скажу что вопрос выбора инструмента, это, во многом, вопрос субъективных личных предпочтений. Большое спасибо за полезные замечания и поелзные советы — они определенно будут учтены мной, когда что-то подобное придётся делать снова.

      Но. Данная статья не позиционировалась как сравнение инструментов. Обычно статьи такого плана рождаются у меня так — возникает сугубо личная задача, эта задача успешно решается и возникает желание поделится с обществом достигнутыми результатами, дабы следующий «неофит» не тратил много времени на поиск информации. Принцип таков: сделал сам — отдай наработки другим. Считаю это правильным.

      Изложенный выше текст приведен «как есть», по факту решения задачи. Он не претендует на академичность и не выставляет описанный инструмент в преимущественное по сравнению с другими продуктами положение.

      Про WiX эт вы зря.


      Имею желание его изучить, возможно я к нему ещё вернусь.


      1. vlivyur
        17.04.2015 12:00

        Я это прочитал и понял. Подталкивал вот к этому:

        Имею желание его изучить, возможно я к нему ещё вернусь.
        Но наверно лучше сразу к WiX# перейти.