WiX хитрости и советы

264

Мы уже давно используем WiX, и, несмотря на обычные жалобы на простоту использования, все идет довольно хорошо. То, что я ищу, это полезный совет относительно:

  • Настройка проекта WiX (макет, ссылки, шаблоны файлов)
  • Интеграция WiX в решения и процессы сборки / выпуска
  • Настройка установщиков для новых установок и обновлений
  • Любые хорошие взломы WiX, которыми вы хотели бы поделиться
si618
источник
взгляните на gui4wix.codeplex.com
TarunG
10
Закрыто как не конструктивно? Я научился куче задавать этот вопрос! Немного согласованности со StackOverflow также было бы неплохо ... например, stackoverflow.com/questions/550632/…
si618
15
Он получил «203» Ups, этого достаточно, чтобы доказать его полезность.
TarunG
ТАК вопросы должны иметь четкий, правильный ответ; открытые вопросы заставляют людей задавать вопросы о реальных проблемах с первой страницы. faq @Si .: Эта политика всегда была там, AFAIK, но сейчас ее лучше применять; этому вопросу почти три года.
Джим Дагг
Честно, Джим, это открытый вопрос, и я думаю, что это зависит от Сообщества, но я должен сказать, что закрытие его как неконструктивного кажется странным, учитывая, что я и, судя по всему, многие другие люди Я нашел этот вопрос полезным (например, goo.gl/Zqp2X ), и что он очень хорошо соответствует practical, answerable questions based on actual problems that you faceчасти FAQ.
si618

Ответы:

157
  1. Храните переменные в отдельном wxiвключаемом файле. Позволяет использовать повторно, переменные быстрее находят и (при необходимости) облегчают манипулирование внешним инструментом.

  2. Определите переменные платформы для сборок x86 и x64

    <!-- Product name as you want it to appear in Add/Remove Programs-->
    <?if $(var.Platform) = x64 ?>
      <?define ProductName = "Product Name (64 bit)" ?>
      <?define Win64 = "yes" ?>
      <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
    <?else ?>
      <?define ProductName = "Product Name" ?>
      <?define Win64 = "no" ?>
      <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
    <?endif ?>
  3. Сохраните место установки в реестре, чтобы обновления могли найти правильное местоположение. Например, если пользователь устанавливает пользовательский каталог установки.

     <Property Id="INSTALLLOCATION">
        <RegistrySearch Id="RegistrySearch" Type="raw" Root="HKLM" Win64="$(var.Win64)"
                  Key="Software\Company\Product" Name="InstallLocation" />
     </Property>

    Заметка : гуру WiX Роб Меншинг опубликовал отличную запись в блоге, в которой более подробно рассматриваются и исправляются крайние случаи, когда свойства задаются из командной строки.

    Примеры с использованием 1. 2. и 3.

    <?include $(sys.CURRENTDIR)\Config.wxi?>
    <Product ... >
      <Package InstallerVersion="200" InstallPrivileges="elevated"
               InstallScope="perMachine" Platform="$(var.Platform)"
               Compressed="yes" Description="$(var.ProductName)" />

    и

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="$(var.PlatformProgramFilesFolder)">
        <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
  4. Самый простой подход - это всегда делать крупные обновления , поскольку он позволяет как новые установки, так и обновления в одном MSI.Код UpgradeCode привязан к уникальному Guid и никогда не изменится, если мы не хотим обновить существующий продукт.

    Примечание : в WiX 3.5 появился новый элемент MajorUpgrade, который оживляет еще проще !

  5. Создание значка в «Установка и удаление программ»

    <Icon Id="Company.ico" SourceFile="..\Tools\Company\Images\Company.ico" />
    <Property Id="ARPPRODUCTICON" Value="Company.ico" />
    <Property Id="ARPHELPLINK" Value="http://www.example.com/" />
  6. В сборках выпуска мы устанавливаем версии для наших установщиков, копируя файл MSI в каталог развертывания. Пример этого с использованием цели wixproj, вызываемой из цели AfterBuild:

    <Target Name="CopyToDeploy" Condition="'$(Configuration)' == 'Release'">
      <!-- Note we append AssemblyFileVersion, changing MSI file name only works with Major Upgrades -->
      <Copy SourceFiles="$(OutputPath)$(OutputName).msi" 
            DestinationFiles="..\Deploy\Setup\$(OutputName) $(AssemblyFileVersion)_$(Platform).msi" />
    </Target>
  7. Используйте тепло для сбора файлов с помощью шаблона Guid. Полезно, если вы хотите повторно использовать файлы WXS в нескольких проектах (см. Мой ответ о нескольких версиях одного и того же продукта). Например, этот пакетный файл автоматически собирает выходные данные RoboHelp.

    @echo off  
    robocopy ..\WebHelp "%TEMP%\WebHelpTemp\WebHelp" /E /NP /PURGE /XD .svn  
    "%WIX%bin\heat" dir "%TEMP%\WebHelp" -nologo -sfrag -suid -ag -srd -dir WebHelp -out WebHelp.wxs -cg WebHelpComponent -dr INSTALLLOCATION -var var.WebDeploySourceDir 

    Там происходит немного, robocopyубирает метаданные рабочей копии Subversion перед сбором; -drссылка корневую директорию установлена ​​на нашем месте установки, а не на TARGETDIR по умолчанию; -varиспользуется для создания переменной, указывающей исходный каталог (выходные данные веб-развертывания).

  8. Простой способ включить версию продукта в заголовок приветствия с помощью Strings.wxl для локализации. ( Предоставлено : saschabeaumont . Добавлено, поскольку этот замечательный совет скрыт в комментарии)

    <WixLocalization Culture="en-US" xmlns="http://schemas.microsoft.com/wix/2006/localization">
        <String Id="WelcomeDlgTitle">{\WixUI_Font_Bigger}Welcome to the [ProductName] [ProductVersion] Setup Wizard</String>
    </WixLocalization>
  9. Избавьте себя от боли и следуйте советам Вима Коэна по одному компоненту на файл. Это также позволяет вам пропустить (или подстановочный знак *) GUID компонента .

  10. Роб Меншинг (Rob Mensching) имеет удобный способ быстро отследить проблемы в файлах журналов MSI путем поиска value 3. Обратите внимание на комментарии, касающиеся интернационализации.

  11. При добавлении условных функций более интуитивно понятно установить уровень функции по умолчанию на 0 (отключен), а затем установить уровень условия на желаемое значение. Если вы установите уровень функции по умолчанию> = 1, уровень условия должен быть 0, чтобы отключить его, а это означает, что логика условия должна быть противоположна ожидаемой, что может сбивать с толку :)

    <Feature Id="NewInstallFeature" Level="0" Description="New installation feature" Absent="allow">
      <Condition Level="1">NOT UPGRADEFOUND</Condition>
    </Feature>
    <Feature Id="UpgradeFeature" Level="0" Description="Upgrade feature" Absent="allow">
      <Condition Level="1">UPGRADEFOUND</Condition>
    </Feature>
si618
источник
Насчет добавления значка в Установка и удаление программ, это именно то, что я искал. Куда ты вставляешь эти три строки? +1 за абсолютную удивительность.
Эверетт
Я склонен размещать их сразу после (и, очевидно, ниже) элемента <Package>. Взгляните на схему на валидность wix.sourceforge.net/manual-wix3/schema_index.htm
si618
+1, хотел бы я сделать +100, это самый полезный бит информации о Wix, на который я наткнулся.
Тим Лонг
Спасибо, Тим! Роб Меншинг, Боб Арсон, Вим Коэн и другие заслуживают похвалы за то, что поделились своими знаниями.
si618
38

Проверка, установлен ли IIS:

<Property Id="IIS_MAJOR_VERSION">
    <RegistrySearch Id="CheckIISVersion" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp" Name="MajorVersion" Type="raw" />
</Property>

<Condition Message="IIS must be installed">
    Installed OR IIS_MAJOR_VERSION
</Condition>

Проверка, установлена ​​ли совместимость с метабазой IIS 6 в Vista +:

<Property Id="IIS_METABASE_COMPAT">
    <RegistrySearch Id="CheckIISMetabase" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp\Components" Name="ADSICompatibility" Type="raw" />
</Property>

<Condition Message="IIS 6 Metabase Compatibility feature must be installed">
    Installed OR ((VersionNT &lt; 600) OR IIS_METABASE_COMPAT)
</Condition>
Саймон Стил
источник
34

Храните все идентификаторы в отдельных пространствах имен

  • Особенности начинаются с F. примеров: F.Documentation, F.Binaries, F.SampleCode.
  • Компоненты начинаются с C. примера: C.ChmFile, C.ReleaseNotes, C.LicenseFile, C.IniFile, C.Registry
  • CustomActions являются CA. например: CA.LaunchHelp, CA.UpdateReadyDlg, CA.SetPropertyX
  • Файлы Fi.
  • Каталоги Di.
  • и так далее.

Я считаю, что это очень помогает отслеживать все идентификаторы во всех категориях.

Cheeso
источник
Я не использую пространства имен, но добавляю идентификаторы; например: examplesFeature, ChmFileComponent. Я думаю, что я люблю печатать ;-)
dvdvorle
25

Фантастический вопрос. Я хотел бы увидеть некоторые лучшие практики.

У меня есть много файлов, которые я распространяю, поэтому я настроил свой проект на несколько исходных файлов wxs.

У меня есть исходный файл верхнего уровня, который я называю Product.wxs, который в основном содержит структуру для установки, но не фактические компоненты. Этот файл имеет несколько разделов:

<Product ...>
  <Package ...>
    <Media>... 
   <Condition>s ...
   <Upgrade ..>
   <Directory> 
        ...
   </Directory>
   <Feature>
      <ComponentGroupRef ... > A bunch of these that
   </Feature>
   <UI ...>
   <Property...>
   <Custom Actions...>
   <Install Sequences....
  </Package>
</Product>

Остальные файлы .wix состоят из фрагментов, содержащих ComponentGroups, на которые есть ссылки в теге Feature в Product.wxs. Мой проект содержит хорошую логическую группу файлов, которые я распространяю

<Fragment>
   <ComponentGroup>
     <ComponentRef>
     ....
    </ComponentGroup>
    <DirectoryRef>
      <Component... for each file
      .... 
    </DirectoryRef>
</Fragment>

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

Я хотел бы услышать комментарии по этому поводу, или если у кого-то тоже есть хорошие советы!

Питер Тейт
источник
Наша установка также очень похожа на этот подход. Это хорошо, потому что мы можем использовать наш эквивалент Products.wxs в качестве базовой установки для различных продуктов.
si618
@ Питер Тейт: ваше чувство паука правильное. Смотрите мой ответ о псевдонимах каталогов.
Вим Коенен
Я использую тот же подход: Product.wxs с компоновкой статичен, а задача сборки (heat.exe) создает мой файл Content.wxs
timvw
20

Установите флажок в диалоговом окне выхода, чтобы запустить приложение или файл справки.

...

<!-- CA to launch the exe after install -->
<CustomAction Id          ="CA.StartAppOnExit"
              FileKey     ="YourAppExeId"
              ExeCommand  =""
              Execute     ="immediate"
              Impersonate ="yes"
              Return      ="asyncNoWait" />

<!-- CA to launch the help file -->
<CustomAction Id         ="CA.LaunchHelp"
              Directory  ="INSTALLDIR"
              ExeCommand ='[WindowsFolder]hh.exe IirfGuide.chm'
              Execute    ="immediate"
              Return     ="asyncNoWait" />

<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT"
          Value="Launch MyApp when setup exits." />

<UI>
  <Publish Dialog  ="ExitDialog"
           Control ="Finish"
           Order   ="1"
           Event   ="DoAction"
           Value   ="CA.StartAppOnExit">WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT</Publish>
</UI>

Если вы делаете это таким образом, «стандартный» внешний вид не совсем подходит. Флажок всегда серый фон, а диалог белый:

альтернативный текст http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_1.gif

Одним из способов решения этой проблемы является указание собственного настраиваемого ExitDialog с другим флажком . Это работает, но, кажется, много работы, чтобы изменить цвет одного элемента управления. Другой способ решения этой проблемы - постобработка сгенерированного MSI для изменения полей X, Y в таблице Control для этого конкретного элемента управления CheckBox. Код JavaScript выглядит следующим образом:

var msiOpenDatabaseModeTransact = 1;
var filespec = WScript.Arguments(0);
var installer = new ActiveXObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);
var sql = "UPDATE `Control` SET `Control`.`Height` = '18', `Control`.`Width` = '170'," +
          " `Control`.`Y`='243', `Control`.`X`='10' " +
          "WHERE `Control`.`Dialog_`='ExitDialog' AND " + 
          "  `Control`.`Control`='OptionalCheckBox'";
var view = database.OpenView(sql);
view.Execute();
view.Close();
database.Commit();

Запуск этого кода в виде сценария командной строки (с использованием cscript.exe) после создания MSI (из light.exe) приведет к созданию ExitDialog, который выглядит более профессионально:

альтернативный текст http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_2.gif

Cheeso
источник
Ха! Не мой блог. Я тоже это читал. И у меня есть ссылка на запись в блоге в тексте выше. Но они сделали это не так, как я. Мне нравится мой путь лучше. !!
Cheeso
1
Спасибо за JS, очень полезно! Одна вещь, которую мне пришлось изменить в wxs, это заменить WIXUI_EXITDIALOGOPTIONALCHECKBOXна WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installedвнутреннюю<Publish>
Александр Кожевников
Есть ли способ сделать флажок установленным по умолчанию?
Алек Дэвис
Чтобы установить флажок по умолчанию, я использовал это: <Property Id = "WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value = "1" />
Алек Дэвис,
Похоже на изящное решение, но как мне его использовать? Есть ли способ поместить js внутри элемента <AfterBuild> в моем wixproj? Или, поскольку вы имеете в виду запуск его из командной строки, лучше ли это как событие после сборки, в таком случае, какой хороший интерпретатор командной строки js для Windows?
vanmelle
18

Создание версий Live, Test, Training, ... с использованием одних и тех же исходных файлов.

В двух словах: создайте уникальный код UpgradeCode для каждого установщика и автоматически определите первый символ каждого идентификатора Guid для каждого установщика, оставив оставшиеся 31 уникальными.

Предпосылки

Предположения

  • Переменные WiX используются для определения UpgradeCode, ProductName, InstallName.
  • У вас уже есть работающий установщик. Я не буду пытаться это делать, пока вы не сделаете.
  • Все ваши компоненты хранятся в одном файле (Components.wxs). Этот процесс будет работать, если у вас есть несколько файлов, просто будет больше работы.

Структура каталогов

  • Setup.Library
    • Все файлы wxs (Компоненты, Функции, Диалоги Интерфейса, ...)
    • Common.Config.wxi (ProductCode = "*", ProductVersion, PlatformProgramFilesFolder, ...)
  • Setup.Live (wixproj)
    • Свяжите все файлы Setup.Library с помощью «Добавить существующий файл» -> «Добавить как ссылку» (маленькая кнопка со стрелкой вниз рядом с кнопкой «Добавить» в Visual Studio)
    • Config.wxi (имеет уникальный код обновления, ProductName, InstallName, ...)
  • Setup.Test , ...
    • в режиме реального времени, но Config.wxi настроен для тестовой среды.

Обработать

  • Создайте каталог Setup.Library и переместите все ваши файлы wxs и wxi (кроме Config.wxi) из существующего проекта.
  • Создайте Setup.Live, Setup.Test и т. Д. В соответствии с обычным wixproj.
  • Добавьте цель BeforeBuild в wixproj в Setup.Live и т. Д., Чтобы выполнить задачу FileUpdate сообщества MSBuild для изменения Guids (я использовал A для Live, B для Test и C для обучения)
  • Добавьте цель AfterBuild, чтобы вернуть Components.wxs Guids обратно в 0.
  • С Orca убедитесь, что у каждого компонента в каждом MSI есть измененный guid.
  • Убедитесь, что оригинальные направляющие восстановлены.
  • Убедитесь, что каждый MSI устанавливает (и обновляет) правильный продукт и местоположение.

Пример Config.wxi

<?xml version="1.0" encoding="utf-8"?>
<Include>
<!-- Upgrade code should not change unless you want to install 
     a new product and have the old product remain installed, 
     that is, both products existing as separate instances. -->
<?define UpgradeCode = "YOUR-GUID-HERE" ?>

<!-- Platform specific variables -->
<?if $(var.Platform) = x64 ?>
  <!-- Product name as you want it to appear in Add/Remove Programs-->
  <?define ProductName = "Foo 64 Bit [Live]" ?>
<?else ?>
  <?define ProductName =  "Foo [Live]" ?>
<?endif ?>

<!-- Directory name used as default installation location -->
<?define InstallName = "Foo [Live]" ?>

<!-- Registry key name used to store installation location -->
<?define InstallNameKey = "FooLive" ?>

<?define VDirName = "FooLive" ?>
<?define AppPoolName = "FooLiveAppPool" ?>
<?define DbName = "BlahBlahLive" ?>
</Include>

Пример Config.Common.wxi

<?xml version="1.0" encoding="utf-8"?>
<Include>
<!-- Auto-generate ProductCode for each build, release and upgrade -->
<?define ProductCode = "*" ?>

<!-- Note that 4th version (Revision) is ignored by Windows Installer -->
<?define ProductVersion = "1.0.0.0" ?>

<!-- Minimum version supported if product already installed and this is an upgrade -->
<!-- Note that 4th version (Revision) is ignored by Windows Installer -->
<?define MinimumUpgradeVersion = "0.0.0.0" ?>

<!-- Platform specific variables -->
<?if $(var.Platform) = x64 ?>
   <?define Win64 = "yes" ?>
   <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
   <?define Win64 = "no" ?>
   <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif ?>

<?define ProductManufacturer = "Foo Technologies"?>

<!-- Decimal Language ID (LCID) for the Product. Used for localization. -->
<?define ProductLanguage = "1033" ?>

<?define WebSiteName = "DefaultWebSite" ?>
<?define WebSitePort = "80" ?>

<?define DbServer = "(local)" ?>
</Include>

Пример Components.wxs

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <!-- The pre-processor variable which allows the magic to happen :) -->
  <?include $(sys.CURRENTDIR)\Config.wxi?>
  <?include ..\Setup.Library\Config.Common.wxi?>
  <Fragment Id="ComponentsFragment">
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="$(var.PlatformProgramFilesFolder)">
        <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
          <Component Id="ProductComponent" Guid="0XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" KeyPath="yes">
          ...

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

Пример Setup.Live.wixproj

<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<Target Name="BeforeBuild">
  <CallTarget Targets="ModifyComponentsGuids" />
</Target>
<Target Name="AfterBuild">
  <CallTarget Targets="RevertComponentsGuids" />
</Target>
<!-- Modify the first character of every Guid to create unique value for Live, Test and Training builds -->
<Target Name="ModifyComponentsGuids">
  <FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid=&quot;([a-f]|[A-F]|\d)" ReplacementText="Guid=&quot;A" />
</Target>
<!-- Revert the first character of every Guid back to initial value -->
<Target Name="RevertComponentsGuids">
  <FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid=&quot;([a-f]|[A-F]|\d)" ReplacementText="Guid=&quot;0" />
</Target>

Последние мысли

  • Этот процесс также должен работать для создания разных установщиков для разных модулей слияния (Live, Test, ... как функции) для одного и того же установщика. Я использовал разные установщики, так как это казалось более безопасным вариантом, поэтому существует больший риск того, что кто-то может обновить Live вместо Training, если они находятся в одном окне и вы просто используете функции для различных модулей слияния.
  • Если вы используете MSI для выполнения обновлений, а также новых установок, то есть подхода с использованием только основных обновлений, и сохраняете место установки в реестре, не забудьте создать переменную для имени ключа для каждой установки.
  • Мы также создаем переменные в каждом Config.wxi для включения уникальных имен виртуальных каталогов, пулов приложений, имен баз данных и так далее для каждого установщика.

ОБНОВЛЕНИЕ 1: Автоматически генерируемый компонент Guids устраняет необходимость вызова задачи FileUpdate, если вы создаете компонент с Guid = "*" для каждого файла, устанавливая файл в качестве ключевого пути.

ОБНОВЛЕНИЕ 2: Одна из проблем, с которой мы столкнулись, это то, что если вы не генерируете Guid вашего компонента автоматически, а сборка завершается неудачно, то временные файлы необходимо удалить вручную.

ОБНОВЛЕНИЕ 3: Найден способ убрать зависимость от svn: externals и создания временных файлов. Это делает процесс сборки более гибким (и это лучший вариант, если вы не можете использовать символы подстановки для ваших гидов) и менее хрупким, если в свете или свече происходит сбой сборки.

ОБНОВЛЕНИЕ 4: Поддержка нескольких экземпляров, использующих преобразования экземпляров, в WiX 3.0+, безусловно, также стоит посмотреть.

si618
источник
+1 для справки Задачи сообщества MSBuild, обожаю этот пакет
BozoJoe
17

Использование журнала Msi Diagnostic для получения подробной информации об ошибках

msiexec /i Package.msi /l*v c:\Package.log

куда

Package.msi
это название вашей посылки и
C: \ Package.log
где вы хотите вывод журнала

Msi коды ошибок

Вводное видео Wix
Да и Рандом Вводное видео Wix с участием «Мистера WiX» Роба Меншинга полезно для «концептуальной общей картины».

Терренс
источник
2
+1 Было бы намного лучше, если бы мы могли включить ведение журнала изнутри Wix вместо командной строки.
si618
3
WiX делает. Установите свойство MsiLogging. Поддерживается только установщиком Windows 4.0+.
Роб Меншинг
Большое спасибо "Мистер Викс". Должен проверить это.
Терренс
17

Используйте Javascript CustomActions, потому что они очень просты

Люди говорят, что Javascript не подходит для пользовательских действий MSI . Причины: трудно отлаживать, сложно сделать надежным. Я не согласна Это не сложно отладить, конечно, не сложнее, чем C ++. Это просто по-другому. Я обнаружил, что писать CustomActions в Javascript очень просто, гораздо проще, чем с помощью C ++. Намного быстрее. И так же надежно.

Есть только один недостаток: Javascript CustomActions можно извлечь через Orca, тогда как C / C ++ CA потребует обратного инжиниринга. Если вы считаете, что ваше установочное волшебство защищено интеллектуальной собственностью, вам следует избегать сценариев.

Если вы используете скрипт, вам просто нужно начать с какой-то структуры. Вот некоторые, чтобы вы начали.


Javascript «шаблонный» код для CustomAction:

//
// CustomActions.js 
// 
// Template for WIX Custom Actions written in Javascript.
// 
// 
// Mon, 23 Nov 2009  10:54
// 
// ===================================================================


// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons = {
        OkOnly           : 0,
        OkCancel         : 1,
        AbortRetryIgnore : 2,
        YesNoCancel      : 3
};

var Icons = {
        Critical         : 16,
        Question         : 32,
        Exclamation      : 48,
        Information      : 64
};

var MsgKind = {
        Error            : 0x01000000,
        Warning          : 0x02000000,
        User             : 0x03000000,
        Log              : 0x04000000
};

// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus = {
        None             : 0,
        Ok               : 1, // success
        Cancel           : 2,
        Abort            : 3,
        Retry            : 4, // aka suspend?
        Ignore           : 5  // skip remaining actions; this is not an error.
};


function MyCustomActionInJavascript_CA() {
    try {
        LogMessage("Hello from MyCustomActionInJavascript");
        // ...do work here...
        LogMessage("Goodbye from MyCustomActionInJavascript");
    }
    catch (exc1) {
        Session.Property("CA_EXCEPTION") = exc1.message ;
        LogException(exc1);
        return MsiActionStatus.Abort;
    }
    return MsiActionStatus.Ok;
}

// Pop a message box.  also spool a message into the MSI log, if it is enabled. 
function LogException(exc) {
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "CustomAction: Exception: 0x" + decimalToHexString(exc.number) + " : " + exc.message;
    Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}


// spool an informational message into the MSI log, if it is enabled. 
function LogMessage(msg) {
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "CustomAction:: " + msg;
    Session.Message(MsgKind.Log, record);
}


// http://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx
var WindowStyle = {
    Hidden : 0,
    Minimized : 1,
    Maximized : 2
};

// http://msdn.microsoft.com/en-us/library/314cz14s(v=VS.85).aspx
var OpenMode = {
    ForReading : 1,
    ForWriting : 2,
    ForAppending : 8
};

// http://msdn.microsoft.com/en-us/library/a72y2t1c(v=VS.85).aspx
var SpecialFolders = {
    WindowsFolder : 0, 
    SystemFolder :  1, 
    TemporaryFolder : 2
};

// Run a command via cmd.exe from within the MSI
function RunCmd(command)
{
    var wshell = new ActiveXObject("WScript.Shell");
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var tmpdir = fso.GetSpecialFolder(SpecialFolders.TemporaryFolder);
    var tmpFileName = fso.BuildPath(tmpdir, fso.GetTempName());

    LogMessage("shell.Run("+command+")");

    // use cmd.exe to redirect the output
    var rc = wshell.Run("%comspec% /c " + command + "> " + tmpFileName, WindowStyle.Hidden, true);
    LogMessage("shell.Run rc = "  + rc);

    // here, optionally parse the output of the command 
    if (parseOutput) {
        var textStream = fso.OpenTextFile(tmpFileName, OpenMode.ForReading);
        while (!textStream.AtEndOfStream) {
            var oneLine = textStream.ReadLine();
            var line = ParseOneLine(oneLine);
                ...
        }
        textStream.Close();
    }

    if (deleteOutput) {
        fso.DeleteFile(tmpFileName);
    }

    return {
        rc : rc,
        outputfile : (deleteOutput) ? null : tmpFileName
    };
}

Затем зарегистрируйте настраиваемое действие примерно так:

<Fragment>
  <Binary Id="IisScript_CA" SourceFile="CustomActions.js" />

  <CustomAction Id="CA.MyCustomAction"
              BinaryKey="IisScript_CA"
              JScriptCall="MyCustomActionInJavascript_CA"
              Execute="immediate"
              Return="check" />
</Fragmemt>

Вы можете, конечно, вставить столько функций Javascript, сколько захотите, для нескольких пользовательских действий. Один пример: я использовал Javascript для выполнения запроса WMI на IIS, чтобы получить список существующих веб-сайтов, на которые можно установить фильтр ISAPI. Этот список затем использовался для заполнения списка, показанного позже в последовательности пользовательского интерфейса. Все очень просто.

На IIS7 нет поставщика WMI для IIS, поэтому я использовал shell.Run()подход для вызова appcmd.exe для выполнения работы. Легко.

Похожий вопрос: О Javascript CustomActions

Cheeso
источник
2
+1 Я считаю, что подход DTF прост в настройке, но также может быть полезен javascript.
si618
12

Питер Тейт уже показал, как вы можете определить определения многократного использования ComponentGroup в отдельных фрагментах wix. Некоторые дополнительные трюки, связанные с этим:

Псевдоним каталога

Фрагментам группы компонентов не нужно знать о каталогах, определенных основным продуктом wxs. В вашем фрагменте группы компонентов вы можете говорить о такой папке:

<DirectoryRef Id="component1InstallFolder">
...
</DirectoryRef>

Тогда основной продукт может создать псевдоним одного из своих каталогов (например, «productInstallFolder»), например:

<Directory Id="productInstallFolder" Name="ProductName">
   <!-- not subfolders (because no Name attribute) but aliases for parent! -->
   <Directory Id="component1InstallFolder"/> 
   <Directory Id="component2InstallFolder"/> 
</Directory>

График зависимостей

Элементы ComponentGroup могут содержать дочерние элементы ComponentGroupRef. Это хорошо, если у вас большой пул повторно используемых компонентов со сложным графом зависимостей между ними. Вы просто настраиваете ComponentGroup в своем собственном фрагменте для каждого компонента и объявляете зависимости следующим образом:

<ComponentGroup Id="B">
   <ComponentRef Id="_B" />
   <ComponentGroupRef Id="A">
</ComponentGroup>

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

Многоразовый wixlib

Вышеупомянутая идея графа зависимостей работает лучше всего, если вы скомпилируете компоненты big-pool-o-reusable-компоненты в повторно используемый пакет wixlib с lit.exe. При создании настройки приложения вы можете ссылаться на этот wixlib во многом как файл wixobj. Компоновщик Candle.exe автоматически удалит все фрагменты, которые не «извлекаются» основным файлом (файлами) wxs.

Вим Коенен
источник
12

Я удивлен, что никто не упомянул использование T4 для генерации файла WXS во время сборки. Я узнал об этом с помощью Henry Lee @ New Age Solutions .

По сути, вы создаете пользовательскую задачу MSBuild для выполнения шаблона T4, и этот шаблон выводит WXS непосредственно перед компиляцией проекта Wix. Это позволяет (в зависимости от того, как вы его реализуете) автоматически включать все выходные сборки, полученные при компиляции другого решения (это означает, что вам больше не нужно редактировать wxs при каждом добавлении новой сборки).

Питер Т. Лакомб младший
источник
2
+1 это действительно приятно, меня не очень беспокоит сборка, но у наших веб-проектов могут быть проблемы со страницами aspx и другими артефактами (изображениями, css), которые добавляются в проект, но не WiX.
si618
4
Для будущих посетителей в Wix 3.5 есть утилита heat.exe, которая выполняет этот сбор автоматически
Mrchief
@Mrchief - я не верю, что Heat собирает ссылочные сборки, которые копируются локально - хотя, очевидно, это запланировано на 4.0. Ссылка: sourceforge.net/tracker/…
Питер Т. Лакомб младший
Тепло не подхватывает ссылочные сборки.
тофутим
Каковы хорошие примеры использования T4 для создания файла WXS?
тофутим
12

Используя Heat.exe, чтобы разбить лицо и нанести «Epic Pwnage» на мучительно больших инсталляциях

Расширяя ответы Си и Роберта-П о жаре.

Перевод: (Использование Heat, чтобы не вводить отдельные файлы в проект вручную и для автоматизации сборок для упрощения процесса.)

Синтаксис WiX 2.0 Heat подробно

Для более новых версий (не все, что отличается от более старых версий, но есть потенциально раздражающие изменения синтаксиса ....) перейдите в каталог Heat is from cmd.exe и просто введите команду heat, но у меня есть пример прямо здесь для справки с более новыми версиями, если это необходимо.

Добавление следующего к событию Build Event в Visual Studio 2010.
(Щелкните правой кнопкой мыши Project-> Properties -> Build Events-> Pre-Build Events)

$(WIX)bin\heat.exe" dir "$(EnviromentVariable)" -cg GroupVariable -gg -scom -sreg -sfrag - srd -dr INSTALLLOCATION -var env.LogicPath -out "$(FragmentDir)\FileName.wxs

-gg 

Генерирует направляющие при нагреве (как при выполнении команды выше)

-scom 

Не берите "COM-файлы"

-sreg 

Не берите "Файлы реестра"

-sfrag 

Не хватайте "Фрагменты"

-srd 

Не берите "Корень Dir"

реж

dir указывает, что вы хотите, чтобы Heat просматривал папку

"$ (EnviromentVariable)"

Имя переменной, которую вы добавили бы к переменным препроцессора в свойствах проекта (щелкните правой кнопкой мыши, перейдите в свойства) -> раздел Построение, где указано определение переменных препроцессора (предполагается, что Visual Studio 2010)

Пример:
EnviromentVariable = C: \ Project \ Bin \ Debug;
Нет двойных кавычек, но заканчивается точкой с запятой

-cg GroupVariable 

ComponentGroup, на которую будут ссылаться из созданного фрагмента в основной файл wxs

FragmentDir

Каталог фрагментов, в котором будет храниться выходной фрагмент wxs

FileName.wxs

Имя файла

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

Часть 1 Часть 2

Терранс
источник
Есть еще один полезный инструмент для немного других целей: Paraffin ( wintellect.com/CS/blogs/jrobbins/archive/2010/03/10/4107.aspx )
ralf.w.
9

Включая COM-объекты:

heatгенерирует все большинство (если не все) записи реестра и другую необходимую для них конфигурацию. Радуйтесь!

Включение управляемых COM-объектов (иначе, .NET или C # COM-объектов)

Использование heatуправляемого COM-объекта даст вам почти полный документ wix.

Если вам не нужна библиотека, доступная в GAC (т. Е. Глобально доступная: в большинстве случаев это вам не нужно с вашими сборками .NET в любом случае - вы, вероятно, сделали что-то не так в этот момент, если она не предназначена для этого). общей библиотекой) вам нужно будет обязательно обновить CodeBaseраздел реестра, который будет установлен [#ComponentName]. Если вы планируете установить его в GAC (например, вы создали новую классную библиотеку, которую все захотят использовать), вы должны удалить эту запись и добавить два новых атрибута в Fileэлемент: Assemblyи KeyPath. Сборка должна быть установлена ​​на ".net" и KeyPathдолжна быть установлена ​​на "да".

Тем не менее, некоторым средам (особенно всем, что связано с управляемой памятью, например языками сценариев) также потребуется доступ к Typelib. Убедитесь, что вы запустили heatсвою библиотеку типов и включили ее. heatсгенерирует все необходимые ключи реестра. Как это круто?

Роберт П
источник
8

Установка в C:\ProductName

Некоторые приложения должны быть установлены C:\ProductNameили что-то подобное, но 99,9% (если не 100%) примеров в сетевой установке C:\Program Files\CompanyName\ProductName.

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

<CustomAction Id="AssignTargetDir" Property="TARGETDIR" Value="C:\" Execute="firstSequence" />
<InstallUISequence>
    <Custom Action="AssignTargetDir" Before="CostInitialize">TARGETDIR=""</Custom>
</InstallUISequence>
<InstallExecuteSequence>
    <Custom Action="AssignTargetDir" Before="CostInitialize">TARGETDIR=""</Custom>
</InstallExecuteSequence>

ПРИМЕЧАНИЕ: по умолчанию TARGETDIR не указывает на C:\! Это скорее указывает на то, ROOTDRIVEчто в свою очередь указывает на корень диска с наибольшим количеством свободного места ( см. Здесь ) - и это не обязательно C:диск. Там может быть другой жесткий диск, раздел или USB-накопитель!

Затем, где-то под вашим <Product ...>тегом, вам понадобятся следующие теги каталога, как обычно:

<Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="APPLICATIONFOLDER" Name="$(var.ProductName)">
        <!-- your content goes here... -->
    </Directory>
</Directory>
gehho
источник
Не проще ли будет просто установить на WindowsVolume?
Вим Коенен
1
Да, но вам придется использовать обходной путь, потому что WindowsVolumeсвойство не может использоваться как Directory(компилятор выдает ошибку / предупреждение), как указано здесь и здесь . Лично я нахожу этот обходной путь запутанным.
gehho
7

Переменные среды

При компиляции документов Wxs в код wixobj вы можете использовать переменные среды для определения различной информации. Например, допустим, вы хотите изменить, какие файлы будут включены в проект. Допустим, у вас есть переменная окружения RELEASE_MODE, которую вы установили непосредственно перед сборкой MSI (с помощью скрипта или вручную, это не имеет значения). В исходном коде wix вы можете сделать что-то вроде:

<define FILESOURCE = c:\source\output\bin\$(env.RELEASE_MODE) >

а затем позже в своем коде, используйте его на месте, чтобы на лету изменить ваш документ wxs, например:

<Icon Id="myicon.ico" SourceFile="$(var.FILESOURCE)" />
Роберт П
источник
1
Также доступны переменные компиляции, такие как $ (Configuration) и $ (Platform). Также куча других на msdn.microsoft.com/en-us/library/aa302186.aspx
si618
1
@Si - Когда-то до сегодняшнего дня, эта ссылка больше не активна. Я не мог найти последний.
Питер М
7

Редактирование диалогов

Хорошей способностью редактировать диалоги является использование SharpDevelop в версии 4.0.1.7090 (или выше). С помощью этого инструмента можно открывать, просматривать и редактировать автономный диалог (файлы wxs из источников WiX, например, InstallDirDlg.wxs) в режиме конструктора.

user432758
источник
Круто, не знал, что SharpDevelop поддерживает это.
anton.burger
6

Установка флага IIS enable32BitAppOnWin64 http://trycatchfail.com/blog/post/WiX-Snippet-change-enable32BitAppOnWin64.aspx

<InstallExecuteSequence>
   <RemoveExistingProducts After="InstallFinalize" />
   <Custom Action="ConfigureAppPool" After="InstallFinalize" >
     <![CDATA[NOT Installed AND VersionNT64 >= 600]]>         
   </Custom>
</InstallExecuteSequence>

<CustomAction Id="ConfigureAppPool" Return="check" Directory="TARGETDIR" ExeCommand="[SystemFolder]inetsrv\appcmd set apppool /apppool.name:[APPPOOLNAME] /enable32BitAppOnWin64:false" />
EdmundYeung99
источник
5

Изменить "Готов к установке?" диалог (он же VerifyReadyDlg), чтобы предоставить сводку сделанных выборов.

Это выглядит так:
альтернативный текст http://i46.tinypic.com/s4th7t.jpg

Сделайте это с помощью Javascript CustomAction:


Javascript код:

// http://msdn.microsoft.com/en-us/library/aa372516(VS.85).aspx
var MsiViewModify = 
    {
        Refresh          : 0,
        Insert           : 1,
        Update           : 2,
        Assign           : 3,
        Replace          : 4,
        Merge            : 5,
        Delete           : 6,
        InsertTemporary  : 7,   // cannot permanently modify the MSI during install
        Validate         : 8,
        ValidateNew      : 9,
        ValidateField    : 10,
        ValidateDelete   : 11
    };


// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons = 
    {
        OkOnly           : 0,
        OkCancel         : 1,
        AbortRetryIgnore : 2,
        YesNoCancel      : 3
    };

var Icons= 
    {
        Critical         : 16,
        Question         : 32,
        Exclamation      : 48,
        Information      : 64
    }

var MsgKind =
    {
        Error            : 0x01000000,
        Warning          : 0x02000000,
        User             : 0x03000000,
        Log              : 0x04000000
    };

// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus = 
    {
        None             : 0,
        Ok               : 1, // success
        Cancel           : 2,
        Abort            : 3,
        Retry            : 4, // aka suspend?
        Ignore           : 5  // skip remaining actions; this is not an error.
    };

function UpdateReadyDialog_CA(sitename)
{
    try 
    {
        // can retrieve properties from the install session like this:
        var selectedWebSiteId = Session.Property("MSI_PROPERTY_HERE");

        // can retrieve requested feature install state like this:
        var fInstallRequested   = Session.FeatureRequestState("F.FeatureName");

        var text1 = "This is line 1 of text in the VerifyReadyDlg";

        var text2 = "This is the second line of custom text";

        var controlView     = Session.Database.OpenView("SELECT * FROM Control");
        controlView.Execute();

        var rec             = Session.Installer.CreateRecord(12);
        rec.StringData(1)   = "VerifyReadyDlg";    // Dialog_
        rec.StringData(2)   = "CustomVerifyText1"; // Control - can be any name
        rec.StringData(3)   = "Text";              // Type
        rec.IntegerData(4)  = 25;                  // X
        rec.IntegerData(5)  = 60;                  // Y
        rec.IntegerData(6)  = 320;                 // Width
        rec.IntegerData(7)  = 85;                  // Height
        rec.IntegerData(8)  = 2;                   // Attributes
        rec.StringData(9)   = "";                  // Property
        rec.StringData(10)  = vText1;              // Text
        rec.StringData(11)  = "";                  // Control_Next
        rec.StringData(12)  = "";                  // Help
        controlView.Modify(MsiViewModify.InsertTemporary, rec);

        rec                 = Session.Installer.CreateRecord(12);
        rec.StringData(1)   = "VerifyReadyDlg";    // Dialog_
        rec.StringData(2)   = "CustomVerifyText2"; // Control - any unique name
        rec.StringData(3)   = "Text";              // Type
        rec.IntegerData(4)  = 25;                  // X
        rec.IntegerData(5)  = 160;                 // Y
        rec.IntegerData(6)  = 320;                 // Width
        rec.IntegerData(7)  = 65;                  // Height
        rec.IntegerData(8)  = 2;                   // Attributes
        rec.StringData(9)   = "";                  // Property
        rec.StringData(10)  = text2;               // Text
        rec.StringData(11)  = "";                  // Control_Next
        rec.StringData(12)  = "";                  // Help
        controlView.Modify(MsiViewModify.InsertTemporary, rec);

        controlView.Close();
    }
    catch (exc1)
    {
        Session.Property("CA_EXCEPTION") = exc1.message ;
        LogException("UpdatePropsWithSelectedWebSite", exc1);
        return MsiActionStatus.Abort;
    }
    return MsiActionStatus.Ok;
}


function LogException(loc, exc)
{
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "Exception {" + loc + "}: " + exc.number + " : " + exc.message;
    Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}

Объявите Javascript CA:

<Fragment>
  <Binary Id="IisScript_CA" SourceFile="CustomActions.js" />

  <CustomAction Id="CA.UpdateReadyDialog"
              BinaryKey="IisScript_CA"
              JScriptCall="UpdateReadyDialog_CA"
              Execute="immediate"
              Return="check" />
</Fragment>

Присоедините ЦС к кнопке. В этом примере CA запускается при нажатии Next из CustomizeDlg:

<UI ...>
  <Publish Dialog="CustomizeDlg" Control="Next" Event="DoAction" 
           Value="CA.UpdateReadyDialog" Order="1"/>
</UI>

Связанный вопрос SO: Как я могу установить, во время выполнения, текст, который будет отображаться в VerifyReadyDlg?

Cheeso
источник
Конечно, это не должен быть JScript язык сценариев Windows, а не JavaScript язык сценариев DHTML. Возможно, немного педантичный, но может быть немного запутанным для некоторых людей.
caveman_dick
5

Поместите компоненты, которые могут быть исправлены по отдельности внутри своих фрагментов

Это относится как к созданию установщиков продукта, так и к исправлениям: если вы включаете какой-либо компонент в фрагмент, вы должны включить все компоненты в этот фрагмент. В случае сборки установщика, если вы пропустите какие-либо ссылки на компоненты, вы получите ошибку компоновки из light.exe. Однако, когда вы делаете исправление, если вы включаете ссылку на один компонент во фрагмент, то все измененные компоненты из этого фрагмента будут отображаться в вашем исправлении.

как это:

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent1" Guid="{C28843DA-EF08-41CC-BA75-D2B99D8A1983}" DiskId="1">
            <File Id="SampleFile1" Source=".\$(var.Version)f\Sample1.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent2" Guid="{6CEA5599-E7B0-4D65-93AA-0F2F64402B22}" DiskId="1">
           <File Id="SampleFile2" Source=".\$(var.Version)f\Sample2.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent3" Guid="{4030BAC9-FAB3-426B-8D1E-DC1E2F72C2FC}" DiskId="1">
           <File Id="SampleFile3" Source=".\$(var.Version)f\Sample3.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

вместо этого:

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent1" Guid="{C28843DA-EF08-41CC-BA75-D2B99D8A1983}" DiskId="1">
            <File Id="SampleFile1" Source=".\$(var.Version)\Sample1.txt" />
        </Component>

        <Component Id="SampleComponent2" Guid="{6CEA5599-E7B0-4D65-93AA-0F2F64402B22}" DiskId="1">
           <File Id="SampleFile2" Source=".\$(var.Version)\Sample2.txt" />
        </Component>

        <Component Id="SampleComponent3" Guid="{4030BAC9-FAB3-426B-8D1E-DC1E2F72C2FC}" DiskId="1">
           <File Id="SampleFile3" Source=".\$(var.Version)\Sample3.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

Кроме того, при исправлении с использованием темы «Использование Purely WiX» из файла справки WiX.chm используйте следующую процедуру для создания исправления:

torch.exe -p -xi 1.0\product.wixpdb 1.1\product.wixpdb -out patch\diff.wixmst
candle.exe patch.wxs
light.exe patch.wixobj -out patch\patch.wixmsp
pyro.exe patch\patch.wixmsp -out patch\patch.msp -t RTM patch\diff.wixmst

недостаточно просто иметь версию 1.1 product.wixpdb, собранную с использованием компонентов в отдельных фрагментах. Поэтому перед отправкой обязательно правильно фрагментируйте свой продукт.

Дейв Андерсен
источник
5

Печать лицензионного соглашения от Wix3.0 и выше

1) Когда вы компилируете исходный код wix, light.exe должен ссылаться на WixUIExtension.dll в командной строке. Для этого используйте ключ командной строки -ext.

2) Если при добавлении ссылки на WixUIExtension.dll ваш проект не скомпилируется, это, скорее всего, из-за столкновений идентификаторов диалогов, т.е. ваш проект использовал те же идентификаторы диалогов, что и некоторые стандартные диалоги в WixUIExtension.dll, дать разные идентификаторы для ваших диалогов. Это довольно распространенная проблема.

3) В вашем диалоговом окне лицензии должен быть элемент управления ScrollableText с идентификатором «LicenseText». Wix ищет именно это имя элемента управления при печати.

<Control Id="LicenseText" Type="ScrollableText" X="20" Y="60" Width="330" Height="160" Sunken="yes" TabSkip="no">
    <Text SourceFile="License.rtf" />
</Control>

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

<Control Type="PushButton" Id="PrintButton" Width="57" Height="17" X="19" Y="244" Text="Print">
    <Publish Event="DoAction" Value="PrintEula">1</Publish>
</Control>

4) Определите CustomAction с помощью Id = "PrintEula" следующим образом:

<CustomAction Id="PrintEula" BinaryKey="WixUIWixca" DllEntry="PrintEula" Return="ignore" Execute="immediate" />

Примечание: BinaryKey отличается в Wix3.0 по сравнению с Wix2.0 и должен быть точно "WixUIWixca" (с учетом регистра).

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

farfareast
источник
5
  • Мы показываем версию продукта где-то (крошечную) на первом экране графического интерфейса. Потому что люди склонны делать ошибки при выборе правильной версии каждый раз. (И держать нас, разработчиков, ищущих целую вечность ..)

  • Мы настроили TFSBuild, чтобы также генерировать преобразования (файлы MST) с конфигурацией для наших различных сред. (Мы знаем обо всех средах, в которых нам нужно развернуться).

Поскольку оригинальное сообщение в блоге Гранта Холлидея не работает, я скопировал его содержимое здесь:


Задача MSBuild для создания файлов MSI Transform из XMLMarch 11 2008

В моем предыдущем посте я описал, как вы можете использовать файлы MSI Transform (* .mst), чтобы отделить параметры конфигурации, относящиеся к среде, от стандартного пакета MSI.

Хотя это обеспечивает уровень гибкости в вашей конфигурации, есть два недостатка файлов Transform:

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

К счастью, мы можем использовать библиотеку объектов установщика Microsoft Windows (c: windowssystem32msi.dll), чтобы открывать «базы данных» MSI и создавать файлы преобразования.

Кредиты снова идут к Алексу Шевчуку - От MSI к WiX - Часть 7 Настройка установки с использованием Transforms, чтобы показать нам, как этого добиться с помощью VbScript. По сути, все, что я сделал, это взял пример Алекса и, используя Interop.WindowsInstaller.dll, я реализовал задачу MSBuild. Задача MSBuild

Загрузите исходный код и пример transforms.xml здесь (~ 7Kb Zipped VS2008 Solution)


Тайс
источник
2
Мы переопределяем WelcomeDlgTitle в моем файле локализаций - отлично работает! <String Id = "WelcomeDlgTitle"> {\ WixUI_Font_Bigger} Добро пожаловать в мастер установки [ProductName] [ProductVersion] </ String>
saschabeaumont
5

Перед развертыванием установочного пакета я всегда контролирую его содержимое.

Это всего лишь простой вызов в командной строке (в соответствии с постом Terferences), откройте командную строку и введите

msiexec /a Package.msi /qb TARGETDIR="%CD%\Extract" /l*vx "%CD\install.log%"

Это извлечет содержимое пакета в подкаталог «Извлечь» с текущим путем.

tobaer
источник
4

Вместо ORCA используйте InstEd, который является хорошим инструментом для просмотра таблиц MSI. Также он имеет возможность различать два пакета с помощью Transform -> Compare To ...

Дополнительно доступна версия Plus с дополнительными функциями. Но также бесплатная версия предлагает хорошую альтернативу для Orca.

user432758
источник
4

Регистрация сборок .NET для COM Interop с совместимостью с x86 / x64

NB Этот фрагмент по сути такой же, как REGASM Assembly.dll / codebase

В этом примере происходит несколько вещей, так что вот код, и я объясню это позже ...

  <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <?include $(sys.CURRENTDIR)\Config.wxi?>
  <?if $(var.Win64) ?>
  <?define CLSIDRoots = "CLSID;Wow6432Node\CLSID"?>
  <?else ?>
  <?define CLSIDRoots = "CLSID"?>
  <?endif?>
  <!-- ASCOM Driver Assembly with related COM registrations -->
  <Fragment>
    <DirectoryRef Id="INSTALLLOCATION" />
  </Fragment>
  <Fragment>
    <ComponentGroup Id="cgAscomDriver">
      <Component Id="cmpAscomDriver" Directory="INSTALLLOCATION" Guid="{0267031F-991D-4D88-A748-00EC6604171E}">
        <File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly"  />
        <RegistryKey Root="HKCR" Key="$(var.DriverId)"  Action="createAndRemoveOnUninstall">
          <RegistryValue Type="string" Value="$(var.DriverTypeName)"/>
          <RegistryKey Key="CLSID">
            <RegistryValue Type="string" Value="$(var.DriverGuid)" />
          </RegistryKey>
        </RegistryKey>
        <?foreach CLSID in $(var.CLSIDRoots) ?>
        <RegistryKey Root="HKCR" Key="$(var.CLSID)" Action="none">
          <RegistryKey Key="$(var.DriverGuid)" Action="createAndRemoveOnUninstall">
            <RegistryValue Type="string" Value="$(var.DriverTypeName)"/>
            <RegistryKey Key="InprocServer32">
              <RegistryValue Type="string" Value="mscoree.dll" />
              <RegistryValue Type="string" Name="ThreadingModel" Value="Both"/>
              <RegistryValue Type="string" Name="Class" Value="$(var.DriverTypeName)"/>
              <RegistryValue Type="string" Name="Assembly" Value="!(bind.assemblyFullname.filDriverAssembly)" />
              <RegistryValue Type="string" Name="RuntimeVersion" Value="v2.0.50727"/>
              <RegistryValue Type="string" Name="CodeBase" Value="file:///[#filDriverAssembly]" />
              <RegistryKey Key="!(bind.fileVersion.filDriverAssembly)" >
                <RegistryValue Type="string" Name="Class" Value="$(var.DriverTypeName)"/>
                <RegistryValue Type="string" Name="Assembly" Value="!(bind.assemblyFullname.filDriverAssembly)" />
                <RegistryValue Type="string" Name="RuntimeVersion" Value="v2.0.50727"/>
                <RegistryValue Type="string" Name="CodeBase" Value="file:///[#filDriverAssembly]" />
              </RegistryKey>
            </RegistryKey>
            <RegistryKey Key="ProgId" Action="createAndRemoveOnUninstall">
              <RegistryValue Type="string" Value="$(var.DriverId)" />
            </RegistryKey>
            <RegistryKey Key="Implemented Categories" Action="createAndRemoveOnUninstall" >
              <RegistryKey Key="{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Action="createAndRemoveOnUninstall" />
            </RegistryKey>
          </RegistryKey>
        </RegistryKey>
        <?endforeach?>
      </Component>
    </ComponentGroup>
  </Fragment>
</Wix>

Если вам интересно, это на самом деле для драйвера телескопа ASCOM .

Во-первых, я воспользовался советом сверху и создал несколько платформенных переменных в отдельном файле, вы можете увидеть их, разбросанные по XML.

Часть if-then-else рядом с топом имеет дело с совместимостью между x86 и x64. Моя сборка нацелена на «Любой процессор», поэтому в системе x64 мне нужно зарегистрировать его дважды, один раз в 64-битном реестре и один раз в 32-битных Wow6432Nodeобластях. If-then-else настраивает меня на это, значения используются в foreachцикле позже. Таким образом, мне нужно только один раз создать ключи реестра (принцип СУХОГО).

Элемент file указывает фактическую сборку dll, устанавливаемую и зарегистрированную:

<File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly"  />

Ничего революционного, но обратите внимание на Assembly=".net"- этот один атрибут мог бы привести сборку в GAC, а это НЕ то, что я хотел. Использование AssemblyApplicationатрибута для указания на себя - это просто способ остановить Wix, помещающий файл в GAC. Теперь, когда Wix знает, что это сборка .net, он позволяет мне использовать определенные переменные связующего в моем XML, такие как, !(bind.assemblyFullname.filDriverAssembly)чтобы получить полное имя сборки.

Тим Лонг
источник
3

Установите DISABLEADVTSHORTCUTSсвойство, чтобы все объявленные ярлыки в вашем установщике становились обычными, и вам не нужно включать фиктивный ключ reg, который будет использоваться в качестве пути к ключу.

<Property Id="DISABLEADVTSHORTCUTS" Value="1"/>

Я думаю, что Windows Installer 4.0 или выше является обязательным требованием .

Дейв Андерсен
источник
2

Это хорошая структура, но, исходя из моего опыта, мне интересно, как вы решаете эти условия:

О. Все ваши установки появляются в одном и том же месте назначения. Если пользователю необходимо установить все 3 версии одновременно, ваш процесс разрешит это. Могут ли они однозначно сказать, какую версию каждого исполняемого файла они запускают?

B. Как вы обрабатываете новые файлы, которые существуют в TEST и / или TRAINING, но еще не в LIVE?


источник
Привет Блейн, А. Нет, они этого не делают. InstallName находится в Config.wxi, который является единственным файлом, на который не ссылается svn: externals. Так что это уникально для каждой установки, то есть для каждого продукта. Вот почему мы модифицируем Guids для каждой версии. B. GOTO A. :) Это отдельные MSI с собственным кодом UpgradeCode.
si618,
1
Кстати, я понимаю, почему вы ответили на мой вопрос с вопросом, но как только вы наберете достаточно очков репутации, перенесите свой вопрос в комментарии к ответам, в противном случае ветку будет трудно отслеживать.
si618
2

Вот способ помочь крупным веб-проектам проверить, что количество развернутых файлов соответствует количеству файлов, встроенных в MSI (или модуль слияния). Я только что запустил пользовательскую задачу MSBuild для нашего основного приложения (все еще находящегося в разработке), и оно обнаружило довольно много отсутствующих файлов, в основном, изображений, но несколько файлов javascript уже проскользнули!

Этот подход (просмотр таблицы файлов MSI путем подключения к цели AfterBuild проекта WiX) может работать для других типов приложений, где у вас есть доступ к полному списку ожидаемых файлов.

si618
источник
2

Выполнение принудительной переустановки, когда установка не позволяет удалить или переустановить и не выполняет откат.

Скрипт VBscript, используемый для переопределения установки, которая не удаляется по какой-либо причине.

Dim objShell
set objShell = wscript.createObject("wscript.shell")

iReturn = objShell.Run("CMD /K MsiExec.exe /I ""C:\Users\TheUser\Documents\Visual Studio 2010\Projects\InstallationTarget\HelloInstaller\bin\Debug\HelloInstaller.msi"" REINSTALLMODE=vomus REINSTALL=ALL",,True)
Terrance
источник
2

Создайте пользовательский интерфейс с пользовательским действием, которое будет устанавливать переменную, и пользовательский интерфейс будет отключать / включать следующую кнопку (или аналогичную) на основе переменной, установленной в пользовательском действии.

Не так просто, как вы думаете, не слишком сложно, просто нигде не задокументировано!

Взаимодействие Wix с условиями, свойствами и пользовательскими действиями

Даниэль Пауэлл
источник