Приемы WiX и подсказки

264
задан 12 revs, 3 users 96% 25 September 2017 в 10:53
поделиться

21 ответ

  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 Rob Mensching отправил превосходная запись в блоге , который вдается в большее количество подробностей и фиксирует пограничный случай, когда свойства установлены из командной строки.

    Примеры с помощью 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 прикреплен к уникальному Гуиду и никогда не будет изменяться, если мы не хотим обновлять существующий продукт.

    Примечание : В 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. тепло Использования для сбора урожая файлов с подстановочным знаком (*) Гуид. Полезный, если Вы хотите снова использовать файлы 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 снимает Подверсию, работающую метаданные копии перед сбором урожая; -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. , Сохраняют себя некоторая боль и следуют совет Wim Coehen из одного компонента на файл. Это также позволяет Вам не учитывать (или подстановочный знак *) 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>
    
157
ответ дан 14 revs 23 November 2019 в 02:29
поделиться

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

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

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

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

, Если Вам не нужна библиотека, доступная в GAC (т.е., глобально доступный: БОЛЬШУЮ ЧАСТЬ ВРЕМЕНИ Вам не нужно это с Вашими блоками.NET так или иначе - Вы, вероятно, сделали что-то не так в этой точке, если она не будет предназначена, чтобы быть общей библиотекой), то Вы захотите удостовериться, что обновили CodeBase ключ реестра, который будет установлен на [#ComponentName]. Если Вы - планирование установки его к GAC (например, Вы сделали некоторую новую потрясающую общую библиотеку, которой все захотят пользоваться), необходимо удалить эту запись и добавить два новых атрибута к File элемент: Assembly и KeyPath. Блок должен быть установлен на ".NET", и KeyPath должен быть установлен на "да".

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

9
ответ дан Robert P 23 November 2019 в 02:29
поделиться

Peter Tate уже показал, как можно определить допускающие повторное использование определения 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-components в допускающий повторное использование wixlib с lit.exe. При создании установки приложения можно сослаться на этот wixlib во многом как wixobj файл. Компоновщик candle.exe автоматически устранит любые фрагменты, которые не "втянуты" основным продуктом wxs файл (файлы).

12
ответ дан Wim Coenen 23 November 2019 в 02:29
поделиться

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

у меня есть много файлов, которые я распределяю, таким образом, я настроил свой проект в несколько 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, на которые ссылаются в теге Функции в Product.wxs. Мой проект содержит хорошую логическую группировку файлов, которые я распределяю

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

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

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

25
ответ дан Peter Tate 23 November 2019 в 02:29
поделиться

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

Вкратце: Создайте уникальный UpgradeCode для каждого установщика и автоволшебно определите первый символ каждого Гуида для каждого установщика, оставив оставление 31 уникальным.

Предпосылки

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

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

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

  • Установка. Библиотека
    • Все wxs файлы (Компоненты, Функции, Диалоговые окна UI...)
    • Распространенный. Config.wxi (ProductCode = "*", ProductVersion, PlatformProgramFilesFolder...)
  • Установка. Живой (wixproj)
    • Ссылка вся Установка. Использование файлов библиотеки, "Добавляют Существующий Файл"->, "Добавляет Как Ссылка" (небольшая кнопка стрелки вниз прямо рядом с кнопкой Add в Visual Studio)
    • Config.wxi (имеет уникальный UpgradeCode, ProductName, InstallName...)
  • Установка. Тест ...
    • согласно живому, но Config.wxi настроен для Тестовой среды.

Процесс

  • Создают Установку. Каталог Library и перемещение весь Ваш wxs и wxi файлы (кроме Config.wxi) из существующего проекта.
  • Создают Установку. Живой, Установка. Тест, и т.д. согласно нормальному wixproj.
  • Добавляют цель BeforeBuild в wixproj в Установке. Живой, и т.д. для выполнения задачи FileUpdate Сообщества MSBuild изменить Гуиды (я использовал для Живого, B для Теста и C для обучения)
  • Добавляют цель AfterBuild для возвращения Гуидов Components.wxs назад к 0.
  • Проверяют с Косаткой, что каждый компонент в каждом MSI имеет измененный гуид.
  • Проверяют, что восстанавливаются исходные гуиды.
  • Проверяют, что каждый 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>

В качестве примера. Примечание в качестве примера 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">
          ...

: Я теперь предложил бы упустить атрибут Гуида из Компонента (эквивалентный из *), с помощью одного файла на компонент и установив файл как keypath. Это устраняет необходимость вызова ModifyComponentsGuids и RevertComponentsGuids цели, показанные ниже. Это не могло бы быть возможно для всех Ваших компонентов все же.

Установка В качестве примера. Последние мысли 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>

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

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

<забастовка> ОБНОВЛЕНИЕ 2: Одна из проблем, на которые мы натолкнулись, - то, если Вы не автоматически генерируете свой Гуид компонента и сбои сборки, затем временные файлы должны быть вручную удалены.

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

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

18
ответ дан 13 revs 23 November 2019 в 02:29
поделиться

Проверка, если 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>
38
ответ дан Simon Steele 23 November 2019 в 02:29
поделиться

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

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

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

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

<Icon Id="myicon.ico" SourceFile="$(var.FILESOURCE)" />
7
ответ дан Robert P 23 November 2019 в 02:29
поделиться

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

Люди говорят, что Javascript не подходит для MSI CustomActions . Приведенные причины: трудно отладить, сложно сделать его надежным. Я не согласен. Отлаживать нетрудно, конечно, не сложнее, чем 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 для выполнения работы. Легко.

Связанный вопрос: О CustomActions Javascript

17
ответ дан 23 November 2019 в 02:29
поделиться

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

...

<!-- 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>

Если вы сделаете это таким образом, "стандартный" вид не будет Совершенно верно. Флажок флажка всегда серый, а диалоговое окно - белое:

alt text 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, который выглядит более профессиональным:

alt text http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in -wix-3 / images / exit_dlg_2.gif

20
ответ дан 23 November 2019 в 02:29
поделиться

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

  • Функции начинаются с 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
  • и т. Д.

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

34
ответ дан 23 November 2019 в 02:29
поделиться

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

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

12
ответ дан 23 November 2019 в 02:29
поделиться

Использование Heat.exe для разбивания лица и нанесения "Epic Pwnage" на болезненно больших установках

Продолжая Si's и Robert-P's ответов о heat.

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

Подробный синтаксис Heat для WiX 2.0

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

Добавление следующего в событие сборки в 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 

Генерирует гиды при запуске heat (как при выполнении команды выше)

-scom 

Не захватывать "COM файлы"

-sreg 

Не захватывать "Файлы реестра"

-sfrag 

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

-srd 

Не захватывать "корневой Dir"

dir

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

"$(EnviromentVariable)"

Имя переменной, которую вы добавите в переменные препроцессора в (Щелкните правой кнопкой мыши проект, Перейти к свойствам) свойства проекта->Строить раздел, где написано Определить переменные препроцессора (предполагается, что visual studio 2010)

Example:
EnviromentVariable=C:\Project\bin\Debug;
Без двойных кавычек, но в конце с запятой.
-cg GroupVariable 

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

FragmentDir

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

FileName.wxs

Имя файла

Полное руководство здесь, очень полезно

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

12
ответ дан 23 November 2019 в 02:29
поделиться

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

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

Где

Package.msi
- это имя вашего пакета и
c:\Package.log
- это то место, где вы хотите вывод журнала

Msi Error Codes

Wix Intro Video
Oh и Random Wix вступительное видео с участием "Mr. WiX" Роб Меншинг - полезная "концептуальная большая картина".

17
ответ дан 23 November 2019 в 02:29
поделиться

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

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

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

<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>

NOTE: По умолчанию TARGETDIR не указывает на C:\! Он скорее указывает на ROOTDRIVE, который в свою очередь указывает на корень диска с наибольшим свободным пространством (см. здесь) - и это не обязательно C: диск. Это может быть другой жесткий диск, раздел или USB-накопитель!

Затем, где-то под тегом , вам нужны следующие теги каталогов, как обычно:

<Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="APPLICATIONFOLDER" Name="$(var.ProductName)">
        <!-- your content goes here... -->
    </Directory>
</Directory>
8
ответ дан 23 November 2019 в 02:29
поделиться

Это - хорошая структура, но на основе моего опыта интересно, как Вы обращаетесь к этим условиям:

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

B. Как Вы обрабатываете новые файлы, которые существуют в ТЕСТЕ и/или ОБУЧЕНИИ, но еще в ЖИВОМ?

2
ответ дан 23 November 2019 в 02:29
поделиться
  • Мы отображаем версию продукта, где-нибудь (крошечную) на первом экране GUI. Поскольку люди склонны делать ошибки в выборе правильной версии каждый раз. (И сохраните нас разработчиками, ищущими целую вечность..)

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

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

<час>

задача MSBuild генерировать MSI Преобразовывает файлы от XMLMarch 11 2008

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

, Хотя это обеспечивает уровень гибкости в Вашей конфигурации, существует две оборотных стороны файлов Преобразования:

  1. They’re двоичный формат
  2. Вы can’t “edit” или “view” файл преобразования. Необходимо применить его или воссоздать его для наблюдения то, что изменяется, это включает.

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

Кредиты переходят снова в , Alex Shevchuk †“От MSI до WiX †“Часть 7 †“Настройка использования установки Преобразовывает для показа нас, как достигнуть этого с VbScript. По существу весь сделанный I’ve взят пример Alex’s и использование Interop.WindowsInstaller.dll, I’ve реализовал задачу MSBuild. Загрузка Задачи

MSBuild исходный код & пример transforms.xml здесь (~7Kb Заархивированное Решение VS2008)

<час>
5
ответ дан thijs 23 November 2019 в 02:29
поделиться

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

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

2
ответ дан Community 23 November 2019 в 02:29
поделиться

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

Это выглядит так:
alt text http://i46.tinypic.com/s4th7t.jpg

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


Код 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);
}

Объявить CA Javascript:

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

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

Присоединить CA к кнопке. В этом примере CA запускается, когда в CustomizeDlg нажимают Next:

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

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

5
ответ дан 23 November 2019 в 02:29
поделиться

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

Сценарий 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)
2
ответ дан 23 November 2019 в 02:29
поделиться

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

Это касается как создания установщиков продуктов, так и исправлений, которые, если вы включаете какой-либо компонент во фрагмент, вы должны включать все компонентов в этом фрагменте. В случае создания установщика, если вы пропустите какие-либо ссылки на компоненты, вы получите сообщение об ошибке связывания от 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>

Кроме того, при установке патча с использованием темы «Использование чисто 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
ответ дан 23 November 2019 в 02:29
поделиться

Исправьте ProgressDlg, чтобы он отображался правильно.

Я увеличил размер шрифта для моего установщика с 8 до 10, чтобы сделать шрифт более человечным и удобным для использования на мониторах с высоким разрешением. Я делаю это с помощью этой магии XML:

<UI Id="MyCustomUI">
  <TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="10" />
  <TextStyle Id="WixUI_Font_Big"    FaceName="Tahoma" Size="12" />
  <TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="14" />
  <TextStyle Id="WixUI_Font_Title"  FaceName="Tahoma" Size="12" Bold="yes" />

  <Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
</UI>

Но это означает, что ProgressDlg больше не отображается должным образом. Это тот, который отображает ход установки в самом конце. ActionText обрезается, поэтому нисходящие элементы на таких буквах, как g и j, не отображаются. Исправьте это, отрегулировав размер и положение различных элементов управления в диалоговом окне Progressdialog в Javascript после обработки. Запустите этот сценарий после создания MSI:

var msiOpenDatabaseModeTransact = 1;
var filespec = WScript.Arguments(0);
var installer = new ActiveXObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);

// The text on the exit dialog is too close to the title.  This 
// step moves the text down from Y=70 to Y=90, about one line. 
sql = "UPDATE `Control` SET `Control`.`Y` = '90' " +
    "WHERE `Control`.`Dialog_`='ExitDialog' AND `Control`.`Control`='Description'";
view = database.OpenView(sql);
view.Execute();
view.Close();

// The progressbar is too close to the status text on the Progress dialog. 
// This step moves the progressbar down from Y=115 to Y=118, about 1/3 line. 
sql = "UPDATE `Control` SET `Control`.`Y` = '118' " +
    "WHERE `Control`.`Dialog_`='ProgressDlg' AND `Control`.`Control`='ProgressBar'";
view = database.OpenView(sql);
view.Execute();
view.Close();

// The StatusLabel and ActionText controls are too short on the Progress dialog,
// which means the bottom of the text is cut off.  This step
// increases the height from 10 to 16.
sql = "UPDATE `Control` SET `Control`.`Height` = '16' " +
    "WHERE `Control`.`Dialog_`='ProgressDlg' AND `Control`.`Control`='StatusLabel'";
view = database.OpenView(sql);
view.Execute();
view.Close();
sql = "UPDATE `Control` SET `Control`.`Height` = '16' " +
    "WHERE `Control`.`Dialog_`='ProgressDlg' AND `Control`.`Control`='ActionText'";
view = database.OpenView(sql);
view.Execute();
view.Close();

database.Commit();
1
ответ дан 23 November 2019 в 02:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: