WiX трюки и советы - программирование

WiX трюки и советы

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

  • Настройка проекта WiX (макет, ссылки, шаблоны файлов)
  • Интеграция WiX в решения и процессы сборки/выпуска
  • Настройка установщиков для новых установок и обновлений
  • Любые хорошие хаки WiX, которые вы хотели бы поделиться.
4b9b3361

Ответ 1

  • Сохранять переменные в отдельном файле wxi include. Позволяет повторно использовать, переменные быстрее найти и (при необходимости) позволяет упростить манипуляцию с помощью внешнего инструмента.

  • Определить переменные платформы для сборки 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 ?>
    
  • Сохраните место установки в реестре, включив обновления, чтобы найти правильное местоположение. Например, если пользователь устанавливает каталог установочной установки.

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

    Примечание. В WiX 3.5 есть новый элемент MajorUpgrade, который делает жизнь еще проще!

  • Создание значка в "Установка и удаление программ"

    <Icon Id="Company.ico" SourceFile="..\Tools\Company\Images\Company.ico" />
    <Property Id="ARPPRODUCTICON" Value="Company.ico" />
    <Property Id="ARPHELPLINK" Value="http://www.example.com/" />
    
  • В версиях релизов мы обновляем наши установщики, копируя файл 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>
    
  • Используйте тепло для сбора файлов с помощью подстановочного знака (*) 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 используется для создания переменной для указания исходного каталога (выход веб-развертывания).

  • Простой способ включить версию продукта в заголовок приветственного диалога, используя 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>
    
  • Спасите себя от боли и следуйте советам Wim Coehen одного компонента на файл. Это также позволяет вам оставить (или wild-card *) компонентный GUID.

  • Rob Mensching имеет опрятный путь, чтобы быстро отслеживать проблемы в файлах журнала MSI, ища value 3. Обратите внимание на комментарии относительно интернационализации.

  • При добавлении условных функций более интуитивно необходимо установить для уровня по умолчанию значение 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>
    

Ответ 2

Проверка наличия 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 Metabase Compatibility на 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>

Ответ 3

Сохранять все идентификаторы в разных пространствах имен

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

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

Ответ 4

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

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

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

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

Ответ 5

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

...

<!-- 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();

Запуск этого кода в виде командной строки script (с использованием 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

Ответ 6

Создание версий 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(имеет уникальный код UpgradeCode, ProductName, InstallName,...)
  • Setup.Test,...
    • в режиме live, но Config.wxi настроен для тестовой среды.

Процесс

  • Создайте каталог Setup.Library и переместите все ваши файлы wxs и wxi (кроме Config.wxi) из существующего проекта.
  • Создайте Setup.Live, Setup.Test и т.д. в соответствии с обычным wixproj.
  • Добавить цель BeforeBuild в wixproj в Setup.Live и т.д. для выполнения команды сообщества MSBuild FileUpdate для изменения Guids (я использовал A для Live, B для тестирования и C для обучения)
  • Добавить цель AfterBuild, чтобы вернуть Components.wxs обратно до 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 из Component (эквивалент *), используя один файл для каждого компонента и задав файл как путь к ключу. Это устраняет необходимость вызова 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,... as features) для одного и того же установщика. Я пошел с разными установщиками, поскольку это казалось более безопасным вариантом, есть больше риска, что кто-то может обновить Live вместо обучения, если они находятся в одном окне, и вы просто используете функции для разных модулей слияния.
  • Если вы используете MSI для выполнения обновлений, а также для новых установок, то есть для основного обновления, и вы сохраняете свое место установки в реестре, не забудьте создать переменную для имени ключа для каждой установки.
  • Мы также создаем переменные в каждом Config.wxi, чтобы включить уникальные имена виртуальных каталогов, пулы приложений, имена баз данных и т.д. для каждого установщика.

UPDATE 1: Автогенерирующие компоненты Гиды устраняют необходимость вызова задачи FileUpdate, если вы создаете компонент с Guid = "*" для каждого файла, установив файл как путь к ключу.

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

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

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

Ответ 7

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

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

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

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


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 для IIS не существует поставщика WMI, поэтому я использовал метод shell.Run() для вызова appcmd.exe для выполнения работы. Легко.

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

Ответ 8

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

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

Где

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

Коды ошибок Msi

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

Ответ 9

Питер Тейт уже показал, как вы можете определить многоразовые определения 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

Вышеупомянутая идея графика зависимостей работает лучше всего, если вы скомпилируете компоненты большого пула-повторного использования в многоразовый wixlib с lit.exe. При создании настройки приложения вы можете ссылаться на этот wixlib так же, как файл wixobj. Компилятор candle.exe автоматически удалит все фрагменты, которые не "втянуты" основным файлом wxs продукта.

Ответ 10

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

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

Ответ 11

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

Расширяясь на Si's и Роберт-П отвечает о жаре.

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



Синтаксис высокой мощности WiX 2.0

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

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

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

"$(EnviromentVariable)"

Имя переменной, которую вы добавили бы в переменные препроцессора в свойствах проекта "Право щелчка", "Перейти к свойствам" → "Сборка", где указано "Определить переменные препроцессора (предполагает визуальную студию 2010)

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

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

FragmentDir

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

FileName.wxs

Имя файла

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

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

Ответ 12

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

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

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

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

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

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

Ответ 13

Установка на 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>

Ответ 14

Экологические переменные

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

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

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

<Icon Id="myicon.ico" SourceFile="$(var.FILESOURCE)" />

Ответ 17

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

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

Ответ 18

Установка флажка 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" />

Ответ 19

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

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

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


Задача MSBuild для генерации файлов преобразования MSI из XMLMarch 11 2008

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

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

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

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

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

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


Ответ 20

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

Это выглядит так:
alt text 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 к кнопке. В этом примере CA запускается при нажатии Next на CustomizeDlg:

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

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

Ответ 21

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

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

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

Ответ 22

Печать EULA из Wix3.0 и более поздних версий

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

2) Если при добавлении ссылки на файл WixUIExtension.dll ваш проект не скомпилируется, это, скорее всего, связано с конфликтами идентификаторов Dialog, т.е. ваш проект использовал те же идентификаторы диалогов, что и некоторые стандартные диалоги в 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>

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

<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" (с учетом регистра).

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

Ответ 23

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

Это просто простой вызов в командной строке (по сообщению Terrences), откройте командную строку и введите

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

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

Ответ 24

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

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

Ответ 25

Регистрация сборков .NET для COM-взаимодействия с совместимостью 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 Telescope Driver.

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

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

Элемент 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), чтобы получить полное имя сборки.

Ответ 26

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

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

Я думаю, что установщик Windows 4.0 или выше - это .

Ответ 27

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

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

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

Ответ 28

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

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

Ответ 29

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



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

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)

Ответ 30

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

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

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