Обновление отдельной функции в дереве функций WIX без удаления / обновления других функций

Я пытаюсь создать проект установки с использованием WIX, который позволит мне установить несколько функций одного продукта. Как я могу обновить одну из установленных функций (которая не зависит от других установленных функций) без необходимости переустанавливать другие функции в дереве функций?

Например, я хочу иметь возможность иметь проект (возвращающийся к HelloWolrd) с именем HelloWolrd, который (неожиданно) печатает "Hello world!" на экране. Допустим, у меня есть три из этих приложений hello world: Hello World 1, Hello World 2 и Hello World 3. Каждое из них печатает на экране Hello World 1, 2 или 3 с уважением. Я хотел бы создать MSI, который по умолчанию устанавливает все три эти "функции", но также позволяет обновлять каждую функцию в отдельности позднее.

Вот мой макет моего решения:

http://img12.imageshack.us/img12/5671/solutionexplorerm.jpg

Мой файл WIX Product.wxs выглядит так:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="ca484210-c719-4b2e-b960-45212d407c11" Name="HelloWorldInstaller" Language="1033" Version="1.0.0.0" Manufacturer="HelloWorldInstaller" UpgradeCode="68eeb8cb-9ef3-443c-870c-9b406129f7ff">
        <Package InstallerVersion="200" Compressed="yes" />

        <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

        <!-- Create Directory Structure -->
        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFilesFolder">
                <Directory Id="INSTALLLOCATION" Name="Hello World" />
            </Directory>
            <Directory Id="DesktopFolder" Name="Desktop"/>
        </Directory>

        <DirectoryRef Id="INSTALLLOCATION">
            <Component Id="HelloWorld1" Guid="6D1D9D33-DA17-4db3-8132-C39F32200C3A">
                <RegistryKey Root="HKCU" Key="Software\HelloWorldInstaller\HelloWorld1\Install" Action="createAndRemoveOnUninstall">
                    <RegistryValue Name="DTSC" Value="1" Type="integer" KeyPath="yes" />
                </RegistryKey>

                <File Id="HelloWorld1.exe" Name="$(var.HelloWorld1.TargetFileName)" Source="$(var.HelloWorld1.TargetPath)" DiskId="1" Checksum="yes">
                    <Shortcut Id="HelloWorld1ApplicationDesktopShortcut" Name="Hello World 1" Description="Hello World Application 1" Directory="DesktopFolder" WorkingDirectory="INSTALLLOCATION" />
                </File>

            </Component>
            <Component Id="HelloWorld2" Guid="B2D51F85-358B-41a7-8C45-B4BB341158F8">
                <RegistryKey Root="HKCU" Key="Software\HelloWorldInstaller\HelloWorld2\Install" Action="createAndRemoveOnUninstall">
                    <RegistryValue Name="DTSC" Value="1" Type="integer" KeyPath="yes" />
                </RegistryKey>

                <File Id="HelloWorld2.exe" Name="$(var.HelloWorld2.TargetFileName)" Source="$(var.HelloWorld2.TargetPath)" DiskId="1" Checksum="yes">
                    <Shortcut Id="HelloWorld2ApplicationDesktopShortcut" Name="Hello World 2" Description="Hello World Application 2" Directory="DesktopFolder" WorkingDirectory="INSTALLLOCATION" />
                </File>
            </Component>
            <Component Id="HelloWorld3" Guid="A550223E-792F-4169-90A3-574D4240F3C4">
                <RegistryKey Root="HKCU" Key="Software\HelloWorldInstaller\HelloWorld3\Install" Action="createAndRemoveOnUninstall">
                    <RegistryValue Name="DTSC" Value="1" Type="integer" KeyPath="yes" />
                </RegistryKey>

                <File Id="HelloWorld3.exe" Name="$(var.HelloWorld3.TargetFileName)" Source="$(var.HelloWorld3.TargetPath)" DiskId="1" Checksum="yes">
                    <Shortcut Id="HelloWorld3ApplicationDesktopShortcut" Name="Hello World 3" Description="Hello World Application 3" Directory="DesktopFolder" WorkingDirectory="INSTALLLOCATION" />
                </File>
            </Component>
        </DirectoryRef>

        <Feature Id="HelloWorld1Feature" Title="Hello World 1" Level="1">
            <ComponentRef Id="HelloWorld1"/>
        </Feature>
        <Feature Id="HelloWorld2Feature" Title="Hello World 2" Level="1">
            <ComponentRef Id="HelloWorld2"/>
        </Feature>
        <Feature Id="HelloWorld3Feature" Title="Hello World 3" Level="1">
            <ComponentRef Id="HelloWorld3"/>
        </Feature>

    </Product>
</Wix>

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

Когда я обновляю один файл и перестраиваю решение, затем пытаюсь установить MSI, я получаю эту ошибку:

http://img696.imageshack.us/img696/849/anotherversionisinstall.jpg

Я обновил свой код, чтобы разрешить удаление функций и разрешить использование кодов обновления, но это привело к удалению всех функций и их повторной установке.


- Применение в реальном мире -

Реальным приложением для этого является большой программный пакет, который нуждается в нескольких приложениях поддержки, которые регулярно выполняются как службы / запланированные задачи. Я хотел бы получить установку этих приложений поддержки в один MSI, что позволит нам не иметь такой кошмар развертывания каждого exe-файла в отдельности. Я знаю, что если у нас есть обновление одного из исполняемых файлов, мы могли бы просто вручную скомпилировать этот исполняемый файл и развернуть его, но я бы хотел сделать это полностью воспроизводимым способом.

Любая помощь будет оценена,

Спасибо!

РЕДАКТИРОВАТЬ:

Я добавил источник для скачивания из Google Code. Еще раз спасибо!

2 ответа

Решение

Я понял это и подумал, что опубликую ответ здесь для дальнейшего использования другими. Итак, я полностью объяснил проблему, я углублюсь в сценарий реального мира.

У нас есть довольно крупное программное обеспечение, которое требует от нас нескольких вспомогательных приложений, работающих на нескольких разных серверах. Наше текущее развитие обновлений делает умеренно сложным обновление кода надежным способом. В настоящее время мы используем самораспаковывающиеся exe-файлы для развертывания нашего кода на разных серверах. Проблема возникает, когда у нас есть такое большое количество поддерживающих приложений, что становится трудно убедиться, что приложения были установлены правильно с правильными настройками конфигурации и т. Д. Чтобы решить эту проблему, мы рассматриваем возможность вместо сжатия каждого из Поддерживая приложения, мы создаем единый установщик (MSI), который позволит команде инфраструктуры установить определенный набор вспомогательных приложений для каждого конкретного компьютера. Когда у нас будет существенное изменение (например, с 1.0 до 2.0), мы сделаем полную установку обновления (то есть все службы / процессы должны быть остановлены, удалены, установлены и запущены.) Когда у нас есть незначительное изменение, нам бы хотелось только остановить и переустановить затронутые сервисы / процессы, не затрагивая другие приложения. Теперь, хватит мне бродить, давайте перейдем к решению:

Я изменил WIX Product.wxs, чтобы удалить ярлыки, так как они нам не нужны в нашем сценарии. Вот обновленный файл wxs:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
 <Product Id="13C373D3-5C27-487e-A020-C2C89E4607B1" Name="HelloWorldInstaller" Language="1033" Version="1.0.0.0"
      Manufacturer="HelloWorldInstaller" UpgradeCode="E7CB3C76-4D51-48a8-BFB4-6D11B2E2E65B">

  <Package InstallerVersion="200" Compressed="yes" />

  <Media Id="1" Cabinet="product.cab" EmbedCab="yes" />
  <FeatureRef Id="HelloWorld1Feature" />
  <FeatureRef Id="HelloWorld2Feature" />
  <FeatureRef Id="HelloWorld3Feature" />
 </Product>

 <Fragment>
  <Directory Id="TARGETDIR" Name="SourceDir">
   <Directory Id="ProgramFilesFolder">
    <Directory Id="INSTALLLOCATION" Name="Hello World" />
   </Directory>
   <Directory Id="DesktopFolder" Name="Desktop"/>
  </Directory>
 </Fragment>

 <Fragment>
  <DirectoryRef Id="INSTALLLOCATION">
   <Directory Id="HelloWorld1Directory" Name="Hello World 1">
    <Component Id="HelloWorld1Component" Guid="6D1D9D33-DA17-4db3-8132-C39F32200C3A">
     <File Id="HelloWorld1.exe" Name="HelloWorld1.exe" Source="HelloWorld1.exe" DiskId="1" Checksum="yes" />    
    </Component>
   </Directory>
   <Directory Id="HelloWorld2Directory" Name="Hello World 2">
    <Component Id="HelloWorld2Component" Guid="B2D51F85-358B-41a7-8C45-B4BB341158F8">
     <File Id="HelloWorld2.exe" Name="HelloWorld2.exe" Source="HelloWorld2.exe" DiskId="1" Checksum="yes" />
    </Component>
   </Directory>
   <Directory Id="HelloWorld3Directory" Name="Hello World 3">
    <Component Id="HelloWorld3Component" Guid="A550223E-792F-4169-90A3-574D4240F3C4">
     <File Id="HelloWorld3.exe" Name="HelloWorld3.exe" Source="HelloWorld3.exe" DiskId="1" Checksum="yes" />
    </Component>
   </Directory>
  </DirectoryRef>
 </Fragment>

 <Fragment>
  <Feature Id="HelloWorld1Feature" Title="Hello World 1" Level="1">
   <ComponentRef Id="HelloWorld1Component"/>
  </Feature>
 </Fragment>
 <Fragment>
  <Feature Id="HelloWorld2Feature" Title="Hello World 2" Level="1">
   <ComponentRef Id="HelloWorld2Component"/>
  </Feature>
 </Fragment>
 <Fragment>
  <Feature Id="HelloWorld3Feature" Title="Hello World 3" Level="1">
   <ComponentRef Id="HelloWorld3Component"/>
  </Feature>
 </Fragment>
</Wix>

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

Например, скажем, у нас есть ProductA, который имеет три компонента - 1,2 и 3. Эти три компонента должны запускаться либо как службы, либо как запланированные задачи. Характер нашего продукта, мы не можем закрыть все наши службы, чтобы обновить или исправить один из наших компонентов. Итак, если после установки версии 1.0 мы обнаружим ошибку в компоненте 2, но мы не хотим, чтобы исправление, применяемое к этой ошибке, затронуло 1 или 3, мы выпустим патч для компонента 2, таким образом, будет затронут только компонент 2.

Для нашего быстрого примера выше мы используем HelloWorld1, HelloWorld2 и HelloWorld3 в качестве наших трех компонентов в нашем программном приложении. Мысль заключается в том, что мы должны иметь возможность установить все три с одним MSI, но затем обновить каждый независимо, не затрагивая другие установленные компоненты.

Итак, чтобы продемонстрировать это, я создал три консольных приложения, которые будут отображать "Hello World 1!", "Hello World 2!" И "Hello World 3!". Затем, после того, как мы выпустим исходный MSI, допустим, что мы нашли "ошибку", которая заставляет HelloWorld1 сказать "Hello World 1! Обновлено". вместо. Вот что мы будем делать, чтобы смоделировать это:

  1. Создайте Product.wixobj, выполнив эту команду в командной строке:
    candle.exe Product.wxs
    Помните, что для того, чтобы вызывать свечу или любую из команд WIX, каталог установки Wix должен находиться в переменной PATH. ( Краткое руководство по обновлению переменной среды PATH). Также, пожалуйста, выполняйте команды в том же каталоге, что и ваш файл Product.wxs.
  2. Создайте первую версию вашего продукта (скажем, 1.0):
    light.exe Product.wixobj -out ProductA-1.0.msi
  3. Теперь найдите ошибку (измените вывод HelloWorld1 на "Hello World 1! Обновлено."), Затем обновите версию сборки и версию файла. Это важно, поскольку именно так WIX может сказать, что исполняемые файлы разные.
  4. Выполните ту же команду, что и на первом этапе (для правильной оценки):
    candle.exe Product.wxs
  5. Выполните почти ту же команду, что и на втором шаге:
    light.exe Product.wixobj -out ProductA-1.1.msi
    Обратите внимание, что это версия 1.1 вместо 1.0 (это msi с нашим обновленным кодом). Однако мы не хотим просто установить это, продолжайте читать.
  6. Вот забавная часть, мы получаем разницу в двух MSI с помощью следующей команды:
    torch.exe -p -xi ProductA-1.0.wixpdb ProductA-1.1.wixpdb -out Diff.WixMst
  7. Теперь мы создадим файл патча из этого (Patch.wxs будет объяснен ниже):
    candle.exe Patch.wxs
  8. Теперь мы создадим файл WixMsp с помощью этой команды:
    light.exe Patch.wixobj -out Patch.WixMsp
  9. А теперь самое интересное. Создайте файл MSP с помощью этой команды:
    pyro.exe Patch.WixMsp -out Patch.msp -t RTM Diff.Wixmst

Теперь, если все прошло по плану, у вас должно быть два файла msi и один файл msp. Если вы установили первую версию msi (ProductA-1.0.msi) и запустили HelloWorld1.exe, вы должны увидеть сообщение "Hello World 1!". Просто для забавы (и пример), запустите оба других приложения и оставьте их работающими (я сделал остановку, чтобы они оставались открытыми). Закройте HelloWorld1.exe, так как теперь мы собираемся применить обновление для этого exe-файла, но при этом мы не затронем HelloWorld2.exe или HelloWorld3.exe. Если вы сейчас установите файл msp (Patch.msp), а затем запустите HelloWorld1.exe, вы увидите обновленное сообщение "Hello World 1! Обновлено".

Теперь для магического файла Patch.wxs:

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
 <Patch
   AllowRemoval="yes"
   Manufacturer="Dynamo Corp"
   MoreInfoURL="http://www.dynamocorp.com/"
   DisplayName="Sample Patch"
   Description="Small Update Patch"
   Classification="Update"
        >

  <Media Id="5000" Cabinet="RTM.cab">
   <PatchBaseline Id="RTM"/>
  </Media>

  <PatchFamilyRef Id="SamplePatchFamily"/>
 </Patch>

 <Fragment>
  <PatchFamily Id='SamplePatchFamily' Version='1.0.0' Supersede='yes'>
   <ComponentRef Id="HelloWorld1Component"/>
  </PatchFamily>
 </Fragment>
</Wix>

Не так много, не так ли? Ну, самые интересные части это:

  1. <PatchBaseline Id="RTM"/> - Это, если вы помните, используется в нашем создании патча MSI. "RTM" упоминается в последнем шаге выше: -t RTM - Это должно соответствовать.
  2. <ComponentRef Id="HelloWorld1Component"/> - Это указывает патч на правильный компонент для обновления, в нашем случае HelloWorld1Component.

Если вы выполняли какие-либо поиски, приведенный выше код может показаться вам знакомым, поскольку он взят из блога Питера Марку: WiX: создание патча с использованием новой системы сборки патчей - часть 3

Я также сильно полагался на блог Алекса Шевчука: от MSI до WiX, часть 8 - крупное обновление

Если вам интересно: "Вау, это много шагов, зачем кому-то делать так много шагов?", Пожалуйста, помните, что после выполнения тяжелой работы (см. Выше), вам нужно перенести это в свою процедуру интеграции. Это правильно, интегрировать, интегрировать, интегрировать! Как ты это делаешь? Ну, это немного больше исследований, и, возможно, пост в блоге? - Наверное. Чтобы получить правильную информацию, вот отличная статья об автоматизации выпусков с помощью MSBuild и установщика Windows XML.

Ух ты, я надеюсь, что ты прочитал все это (все вы двое) и многому научился. Я надеюсь, что это помогает кому-то, кроме меня.

Спасибо!

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

Другие вопросы по тегам